6

有没有办法使用模板haskell创建带有隐式参数的函数或让带有隐式参数的绑定?

即是否可以使用模板haskell生成这样的签名:

 doSomething :: (?context :: Context) => m a

或者像这样的调用:

 invoc = let ?context = newContext in doSomething

在模板 haskell 的 API 文档中,我找不到合适的代数数据类型或任何可以帮助我解决此主题的函数。我正在使用 GHC 7.4.2。

如果在模板 haskell 中没有对这个扩展的原生支持,是否还有其他可能在编译期间注入代码(可能类似于模板 haskell 中的一般“代码注入功能”?)。

编辑:我尝试了评论中的建议,这就是发生的事情:

runQ [d| f :: (?c :: String) => Int ; f = 7 |]

<interactive>:10:17: parse error on input `c'

而这有效:

 runQ [d| f :: Int ; f = 7|]
 [SigD f_0 (ConT GHC.Types.Int),ValD (VarP f_0) (NormalB (LitE (IntegerL 7))) []]

似乎不支持。

4

1 回答 1

3

这是一种非常脆弱但有效的方法。虽然您不能在模板 haskell 使用的 Exp 中引用 ?x ,但您可以引用另一个模块中的定义,例如:

reserved_prefix_x = ?x

下面是一些代码,它们在一次 ghc 运行中生成类似于上面的变量,而在第二次运行 ghc 中,变量实际上是指隐式参数。

{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}
module GenMod (h) where

import Data.Generics
import Data.IORef
import Data.List
import Language.Haskell.Meta.Parse as P
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import qualified Data.Set as S
import qualified Language.Haskell.Exts.QQ as Q
import System.IO.Unsafe

h = Q.hs { quoteExp = \s -> do
    r <- either fail (upVars . return) (P.parseExp s)
    writeMod'
    return r
    }

pfx = "q_"

{-# NOINLINE vars #-}
vars :: IORef (S.Set String)
vars = unsafePerformIO (newIORef S.empty)

writeMod' = runIO $ writeFile "GEN.hs" . ppMod =<< readIORef vars

writeMod = -- might be needed to avoid multiple calls to writeFile?
           -- in this example this is called for every use of `h'
    QuasiQuoter { quoteDec = \ _ -> do
                    writeMod'
                    [d| _ = () |] }

ppMod xs = "{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}\n\
            \module GEN where\n" ++
    unlines (map (\x -> pfx ++ x ++ " = ?" ++ x) (S.toList xs))

upVars x = do
    x' <- x
    runIO $ modifyIORef vars (S.union (getMatchingVars x'))
    runIO $ print =<< readIORef vars
    return x'

getMatchingVars =
    everything
        S.union
        (mkQ S.empty
            (\ (OccName x) -> maybe S.empty S.singleton (stripPrefix pfx x)))

使用 quasiquoter GenMod.hs 的 Main.hs 文件:

{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams, QuasiQuotes, TemplateHaskell, CPP #-}
import GenMod

#ifndef stage1
import GEN
#endif

f_ = [h| q_hithere |]

您必须调用 ghc 两次,例如:

ghci -Dstage1 Main.hs
GHCi, version 7.6.1: http://www.haskell.org/ghc/  :? for help
[1 of 2] Compiling GenMod           ( GenMod.hs, interpreted )
[2 of 2] Compiling Main             ( Ex.hs, interpreted )
fromList ["hithere"]

Ex.hs:8:6: Not in scope: `q_hithere'
Failed, modules loaded: GenMod.

尽管 ghc 失败,它仍然会生成 GEN.hs,其中包含:

{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}
module GEN where
q_hithere = ?hithere

加载 Main 时会出现哪个(省略 -D 标志)

*Main> :t f_
f_ :: (?hithere::t) => t

这种麻烦恐怕不值得。也许从 TH 调用其他程序的其他情况更有动力,例如对其他语言的内联调用http://hpaste.org/50837(gfortran示例)

由于我使用了 haskell-src-meta 的默认解析器,准引用开始使用变量“reserved_prefix_x”而不是“?x”。应该可以毫不费力地接受“?x”。

于 2013-01-11T03:52:34.810 回答