使用枪支填写位置参数

时间:2015-05-05 16:37:39

标签: haskell

假设我有以下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使用Agunfold。实际上,如何使用gunfold来应用构造函数,同时为每个构造函数位置提供不同的参数?越小/越有效,越好。

这里有一些更多的上下文:http://comments.gmane.org/gmane.comp.lang.haskell.libraries/24594(具体来说,参见Michael Sloan的评论。)基本上,我们正在生成使用Template Haskell调用构造函数的代码;但是,因为我们基于Data实例,所以在一天结束时,我们必须通过Data才能在有人使用虚拟构造函数的情况下获得正确的行为。

1 个答案:

答案 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]))