I'm extending an answer to Haskell setting record field based on field name string? to add a generic getField
. I'm using gmapQi
, and I want to generate an error if the type of the encountered sub-element does not match the expected type. I want the error message to include the name of the type that was encountered, as well as the name of the type expected. The function looks like this:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics
import Prelude hiding (catch)
import Control.Exception
getField :: (Data r, Typeable v) => Int -> r -> v
getField i r = gmapQi i (e `extQ` id) r
where
e x = error $ "Type mismatch: field " ++ (show i) ++
" :: " ++ (show . typeOf $ x) ++
", not " ++ (show . typeOf $ "???")
---------------------------------------------------------------------------------
data Foo = Foo Int String
deriving(Data, Typeable)
handleErr (ErrorCall msg) = putStrLn $ "Error -- " ++ msg
main = do
let r = Foo 10 "Hello"
catch (print (getField 0 r :: Int)) handleErr
catch (print (getField 0 r :: String)) handleErr
catch (print (getField 1 r :: Int)) handleErr
catch (print (getField 1 r :: String)) handleErr
The problem is, I don't know what to put in place of "???"
to get the return type of the getField
function (i.e. how to reify v
from the type signature).
In other languages, such as C# or D and, to some degree, in C (as part of nonstandard extensions and proposed standard revisions), the typeof operator returns the static type of the operand. That is, it evaluates to the declared type at that instant in the program, irrespective of its original form.
typeof() function in R Language is used to return the types of data used as the arguments.
typeof is used to differentiate between the different types in TypeScript. By the use of typeof we can differentiate between number, string, symbol, Boolean, etc. typeof can be used with any type in TypeScript, by the use of it we can re-use the code by passing any parameter type.
The reasoning behind this is that null , in contrast with undefined , was (and still is) often used where objects appear. In other words, null is often used to signify an empty reference to an object. When Brendan Eich created JavaScript, he followed the same paradigm, and it made sense (arguably) to return "object" .
typeOf
never evaluates its argument, so you can use any expression as long as it's of the correct type. In this case, the type of the result is the same as the return type of e
, so you can just use e x
.
getField :: (Data r, Typeable v) => Int -> r -> v
getField i r = gmapQi i (e `extQ` id) r
where
e x = error $ "Type mismatch: field " ++ (show i) ++
" :: " ++ (show . typeOf $ x) ++
", not " ++ (show . typeOf $ e x)
This gives the expected output when run:
10
"Error -- Type mismatch: field 0 :: Int, not [Char]
Error -- Type mismatch: field 1 :: [Char], not Int
"Hello"
You can use (show . typeOf $ (undefined :: v))
if you add forall r v.
to the start of your type signatures and turn on the ScopedTypeVariables
extension.
It'll be possible to achieve without that — usually it involves dummy helper functions to coerce the types into the form you want — but it will be very ugly and I'm not sure how.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With