我正在扩展对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 )。
发布于 2011-12-31 05:36:01
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"发布于 2011-12-31 05:19:24
如果将forall r v.添加到类型签名的开头并打开ScopedTypeVariables扩展,则可以使用(show . typeOf $ (undefined :: v))。
没有这一点也是可以实现的--通常它会涉及到虚拟助手函数来强制将类型转换成你想要的形式--但是它会非常丑陋,而且我不知道该怎么做。
https://stackoverflow.com/questions/8683836
复制相似问题