4

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

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

现在,在我决定为我的玩具语言使用准引用器会很方便之前,我从未将 Template Haskell 用于任何事情,所以我承认我可能会遗漏一些明显的东西。

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

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

  • 使他们的Expr数据类型成为 TH:s 的实例,Lift然后简单地 [| 引用 |] 解析产生的表达式
  • 导出DataTypeable等价于Expr,然后应用于dataToExpQ相同的解析器结果

在这两种情况下,我都遇到了Runtime Expr. 对于第一种情况,问题是我不知道如何实现:

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

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

我想真正的问题是我对 TH 的了解还不够好,但是到目前为止,没有多少教程或示例可以帮助我解决这个问题。

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

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

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

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

4

1 回答 1

4

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

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

所以本质上,你需要一个类型的解析器

parseExpQ :: Parser ExpQ

它产生的Exp代表构建Expr. 对于Fn构造函数,您需要使用DoE-constructor 构建 do-block 或使用InfixEand构建绑定链>>=

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

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

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

于 2012-07-20T12:47:18.760 回答