AST的表达式quasiquoter,其中一个构造函数产生monadic计算?

时间:2012-07-20 09:18:08

标签: haskell template-haskell

在非常简单的意义上,我有以下内容:

type Runtime a = {- More or less a StateT on top of an Either monad -}

-- The list of strings in Fn is a bunch of parameter names, the values of
-- which are pushed into the state of the runtime before executing the actual
-- function expr
data Expr = Num Int
          | Str T.Text
          | Fn [T.Text] (Runtime Expr)
          | {- Bunch of other constructors -}

eval :: Expr -> Runtime Expr
parseExp :: Parser Expr

现在,在我决定为我的玩具语言准备一个准引号之前,我从来没有使用过模板Haskell,所以我承认我可能会遗漏一些明显的东西。

但无论如何,我开始摆弄它,跟着一些教程等等,基本上发现除了如何处理Fn构造函数之外的一切都很容易。

在我在网上的信息搜索过程中,我发现了两种人们写表达式的一般方式:

  • 使他们的Expr数据类型成为TH的实例:Lift并简单地[|引用|]解析导致的表达式
  • DataTypeable等同于Expr,然后对同一个解析器结果应用dataToExpQ

在这两种情况下,我遇到了Runtime Expr的并发症。对于第一种情况,问题是我无法弄清楚如何实施:

instance Lift Expr where
  lift (Fn ps e) = [| Fn ps ...? |]

(我确实设法自己实现了Data.Text的实例)。

我认为真正的问题是我根本就不太了解TH,但到目前为止,没有多少教程或exmaples帮助我实现这一目标。

在第二种情况下,问题是Expr要成为Data的实例,还需要有

instance Data (StateT (...) (Either ...) Expr) where
  -- Something

我的问题是,是否有一种简单的方法可以做到这一点?或许我应该重新考虑我的玩具语言的功能如何运作?

如果是后者,有关如何获得等效功能的任何建议而不在monad中运行它们?毕竟,这似乎是直观的解决方案,因为该语言的运行时环境需要状态和错误处理(这是我使用Either)。

1 个答案:

答案 0 :(得分:4)

您的Expr数据类型不需要是可升级的,以便您为它构建一个quasiquoter,在这种情况下,实现Lift实例是不可能的。原因是你不能“查看”Runtime Expr值,因为StateT值本质上是函数,并且没有通用的方法来找出函数的作用。

你需要做的是构建用手构建Expr“的AST,即不使用[| |] -quoters(或者更具体地说,您可以使用[| |] -quotes来帮助您构建AST,但不能直接引用Expr数据)。

所以从本质上讲,你需要一个类型为

的解析器
parseExpQ :: Parser ExpQ

生成Exp,表示构建Expr所需的Haskell代码。对于Fn构造函数,您需要使用DoE构造函数构建do-block或使用InfixE>>=构建绑定链。

如果这听起来太复杂,那么另一种选择是将函数体表示为表达式(或语句列表,具体取决于您的玩具语言的语义)。即。

data Expr = Num Int
          | Str T.Text
          | Fn [T.Text] [Statement]

定义Statement类型,以便可以将其解释为产生与之前使用StateT Either相同的效果。