Suppose I have the following Haskell program:
假设我有以下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 = _
I would like to implement f
such that it has the type Constr -> A
, and so that it is functionally equivalent to A 1 2
. However, I do not want to use the actual constructor; I am only permitted to use the Constr
for A
, and gunfold
. In effect, how can I use gunfold
to apply a constructor, while providing different arguments for each constructor position? The more minimal/efficient, the better.
我想实现f使它具有类型Constr - > A,并且它在功能上等同于A 1 2.但是,我不想使用实际的构造函数;我只被允许使用Constr for A和gunfold。实际上,我如何使用gunfold来应用构造函数,同时为每个构造函数位置提供不同的参数?越小/越有效越好。
Here is some more context: http://comments.gmane.org/gmane.comp.lang.haskell.libraries/24594 (specifically, see Michael Sloan's comment.) Essentially, we are generating code to call the constructors using Template Haskell; however, since we're basing this off of the Data
instance, at the end of the day we have to go through Data
to get the right behavior in the case that someone is using a virtual constructor.
这里有一些更多的上下文:http://comments.gmane.org/gmane.comp.lang.haskell.libraries/24594(具体来说,参见Michael Sloan的评论。)基本上,我们正在生成使用Template Haskell调用构造函数的代码。但是,由于我们基于Data实例,所以在一天结束时,我们必须通过Data来获取正确的行为,以防有人使用虚拟构造函数。
1 个解决方案
#1
fromConstrM
is derived from gunfold
. You can use it with State
to trivially number the Int
fields of a constructor.
fromConstrM源自枪支。您可以将它与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
If you know what the arguments to the constructor would be you could keep a list of them in state and cast each one as it is used.
如果您知道构造函数的参数是什么,那么您可以将它们保存在状态中,并在使用它们时转换它们。
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]))
#1
fromConstrM
is derived from gunfold
. You can use it with State
to trivially number the Int
fields of a constructor.
fromConstrM源自枪支。您可以将它与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
If you know what the arguments to the constructor would be you could keep a list of them in state and cast each one as it is used.
如果您知道构造函数的参数是什么,那么您可以将它们保存在状态中,并在使用它们时转换它们。
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]))