typeOf返回类型

时间:2011-12-30 20:36:45

标签: haskell

我正在将答案扩展到Haskell setting record field based on field name string?以添加通用getField。我正在使用gmapQi,如果遇到的子元素的类型与预期的类型不匹配,我想生成错误。我希望错误消息包括遇到的类型的名称,以及预期类型的​​名称。该函数如下所示:

{-# 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

问题是,我不知道应该用什么代替"???"来获取getField函数的返回类型(即如何从类型签名中提取v )。

2 个答案:

答案 0 :(得分:5)

typeOf从不评估它的参数,所以你可以使用任何表达式,只要它的类型正确。在这种情况下,结果的类型与e的返回类型相同,因此您只需使用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)

这会在运行时给出预期输出:

10
"Error -- Type mismatch: field 0 :: Int, not [Char]
Error -- Type mismatch: field 1 :: [Char], not Int
"Hello"

答案 1 :(得分:1)

如果您在类型签名的开头添加(show . typeOf $ (undefined :: v))并打开forall r v.扩展程序,则可以使用ScopedTypeVariables

没有它可以实现 - 通常它涉及虚拟辅助函数以将类型强制转换为您想要的形式 - 但它将非常难看,我不确定如何。