在模板Haskell中运行模板Haskell

4
insertST :: StateDecoder -> SomeState -> Update SomeState SomeThing
insertST stDecoder st = ...

StateDecoder中的内容无法使用。
$(makeAcidic ''SomeState ['insertST])

但是如果我声明一个状态并像这样包装它...
myDecoder :: StateDecoder 
myDecoder = ...

insertSomeState :: SomeState -> Update SomeState SomeThing
insertSomeState st = insertST someDecoder

然后它就有效了

我有许多遵循这种模式的数据类型,所以我想写一些 TH 来解决它。

mkWrappedAcid :: Name -> Name -> Q [Dec] 
mkWrappedAcid decoder stname = do 
    insP@(FunD n _) <- insertMaker decoder stname  
    acidP <- mkAcidic stname [n]
    return $[insP] ++ acidP

insertMaker :: Name -> Name -> Q [Dec]
insertMaker decoder stname = (funD istorename) [(clause [] (normalB insertStTH ) [] )
 where 
    istorename = mkName.concat $ ["insert" , (nameBase stname)]
    insertStTH = appE (varE 'insertST ) (varE decoder)

这一切看起来都很完美,但当我尝试运行时...

$(mkWrappedAcid 'myDecoder ''SomeState) 

I get...

    `insertSomeState' is not in scope at a reify

我知道这与模板Haskell中的分期问题有关,但我不知道如何解决。如果我这样做就可以工作:
$(mkWrappedAcid 'myDecoder ''SomeState)
$(makeAcidic ''SomeState ['insertSomeState]) 

但这并没有帮助!


1
你试图做的是不可能的。如果你查看makeAcidic的源代码,它将尝试reify stname。这将失败,因为TH剪辑的范围不包括在该剪辑中生成的内容。 - user2407038
1个回答

1

我认为用户2407038是正确的,直接这样做是不可能的。我最终使用的一个解决方法是从创建函数的程序中提取命名函数的程序。然后,您可以在同一模块中在创建第一个部分之后调用一个新的Haskell模板。我的方法大致如下。

mkWrappedAcid :: Name -> Name -> Q [Dec] 
mkWrappedAcid decoder stname = do 
    insP@(FunD n _) <- insertMaker decoder stname  
    acidP <- mkAcidic stname [n]
    return $[insP] ++ acidP

insertMaker :: Name -> Name -> Q [Dec]
insertMaker decoder stname = (funD istorename) [(clause [] (normalB insertStTH ) [] )
 where 
    istorename = mkName.concat $ ["insert" , (nameBase stname)]
    insertStTH = appE (varE 'insertST ) (varE decoder)

成为...
mkWrappedAcid :: Name -> Name -> Q [Dec] 
mkWrappedAcid decoder stname = do 
  (Loc _ _ md _ _) <- location
  makeAcidic storename (acidPathMaker md storename)

insertMaker :: Name -> Name -> Q [Dec]
insertMaker decoder stname = (funD istorename) [(clause [] (normalB insertStTH ) [])] 
 where 
    insertStTH = appE (varE 'insertST ) (varE decoder)

istorename = mkName.concat $ ["insert" , (nameBase stname)]

acidPathMaker md storename = [modulePlusIname]
  where 
     inameString = nameBase (istorename storename)
     modulePlusIname = mkName . concat $ [md , ".", inameString]

使用这种分离技术,您可以创建包装的酸态(Acid States),例如...
$(acidPathMaker 'myDecoder ''SomeState)
$(mkWrappedAcid ''SomeState )

这对我来说完全没问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接