假设我有以下Haskell程序:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data A = A Int Int
deriving (Show, Typeable, Data)
main = print (f (toConstr (A undefined undefined)))
f :: Constr -> A
f c = _
我想实现f
,使其具有类型Constr -> A
,以便它在功能上等同于A 1 2
。但是,我不想使用实际的构造函数;我只允许Constr
使用A
和gunfold
。实际上,如何使用gunfold
来应用构造函数,同时为每个构造函数位置提供不同的参数?越小/越有效,越好。
这里有一些更多的上下文:http://comments.gmane.org/gmane.comp.lang.haskell.libraries/24594(具体来说,参见Michael Sloan的评论。)基本上,我们正在生成使用Template Haskell调用构造函数的代码;但是,因为我们基于Data
实例,所以在一天结束时,我们必须通过Data
才能在有人使用虚拟构造函数的情况下获得正确的行为。
答案 0 :(得分:4)
fromConstrM
源自gunfold
。
您可以将其与State
一起使用,以轻松地为构造函数的Int
字段编号。
import Data.Maybe
import Control.Monad.Trans.State
numbered :: Data d => State Int d
numbered = fromMaybe (fail "numbered - don't know which constructor to use next") $ gcast $ do
i <- get
put (i + 1)
return i
f :: Constr -> A
f c = fst . runState (fromConstrM numbered c) $ 1
如果您知道构造函数的参数是什么,那么您可以将它们保存在状态中,并在使用时将每个参数投射出来。
import Data.Dynamic
fillArgs :: Data d => State [Dynamic] d
fillArgs = do
args <- get
case args of
[] -> fail "fillArgs - not enough arguments provided"
(x:xs) -> maybe
(fail "fillArgs - type mismatch")
(\x -> do
put xs
return x
)
(fromDynamic x)
applyConstr :: Data d => Constr -> [Dynamic] -> d
applyConstr c = fst . runState (fromConstrM fillArgs c)
f :: Constr -> A
f c = applyConstr c (map toDyn ([1..] :: [Int]))