记录字段名为变量的模板Haskell?
我有下面一段实现monad的代码。稍后,我将尝试使用它简化具有更复杂逻辑的字段设置记录字段名为变量的模板Haskell?,haskell,template-haskell,Haskell,Template Haskell,我有下面一段实现monad的代码。稍后,我将尝试使用它简化具有更复杂逻辑的字段设置 data Rec = Rec { alpha :: Int, beta :: Double, } deriving (Show) defaultRec = Rec 0 0 0 data Record r = Record { runRecord :: Rec -> (Rec, r) } instance Monad Record where return r = Record $
data Rec = Rec {
alpha :: Int,
beta :: Double,
} deriving (Show)
defaultRec = Rec 0 0 0
data Record r = Record { runRecord :: Rec -> (Rec, r) }
instance Monad Record where
return r = Record $ \s -> (s, r)
a >>= b = Record $ \s -> let (q, r) = runRecord a s in runRecord (b r) q
createRecord f = fst $ runRecord f defaultRec
changeAlpha x = Record $ \s -> (s { alpha = x }, ())
我将使用如下代码:
myRecord = createRecord (changeAlpha 9)
changeBeta x = $(makeChange beta) x
这段代码可以工作,但我想使用模板Haskell来简化changeAlpha函数。有这样的东西会很好:
myRecord = createRecord (changeAlpha 9)
changeBeta x = $(makeChange beta) x
现在,我已经做到了这一点:
changeBeta x = Record $ $([| \z -> \s -> (s { beta = z }, ()) |]) x
但一旦我把它改成这个:
changeBeta f x = Record $ $([| \z -> \s -> (s { f = z }, ()) |]) x
我明白了:
TestTH.hs:21:49: `f' is not a (visible) constructor field name
没有变化的工作。这可能吗?我认为
f
只是一个名字;您需要“取消引用”它,因为它来自环境,而不仅仅是引号内的内容。我在这里找到了一个例子[,请参阅“10.1.1从元组中选择”]。unquote使用了$
,所以我想您的最终代码应该是这样的
changeField f = [| \z s -> s { $(varE f) = z } |]
不幸的是,我的ghc版本(7.0.3)似乎在抱怨
$
。(它给出了一个解析错误。)希望这个问题能得到更多的关注,对我来说似乎不错(尽管可能会删除不相关的monad部分)。问题是,您只能拼接类型、表达式或声明列表。记录字段标签不是这两种类型,因此您必须使用TH组合符生成类型为Q Exp
的表达式,然后将其拼接,尽管您仍然可以对其余部分使用牛津括号:
makeChange :: Name -> Q Exp
makeChange x = [|
\z -> Record $ \s -> ( $(recUpdE [| s |] [fieldExp x [| z |]]), () ) |]
要使用它,您必须引用要更改的字段的名称:
changeBeta :: Double -> Record ()
changeBeta x = $(makeChange 'beta) x
你不是在重新发明状态单子和镜头吗 有一个数据镜头模板代码,它为类型中以u开头的每个“记录”生成镜头。不是
$([| x |])
只是x
?i、 e.$()
和[| |]
立即相互抵消。因此,您的changeBeta
的上一个工作版本就是changeBeta x=Record$\s->(s{beta=z},())