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).
typeOfnever 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 ofe, so you can just usee x.This gives the expected output when run: