SystemT编译器和Haskell中的无限类型处理

时间:2018-11-17 08:54:26

标签: haskell ocaml lambda-calculus combinatory-logic

我正在关注此博客文章:http://semantic-domain.blogspot.com/2012/12/total-functional-programming-in-partial.html

它显示了System T (a simple total functional language)的小型OCaml编译器程序。

整个管道采用OCaml语法(通过Camlp4元编程)将它们转换为OCaml AST,然后将其转换为SystemT Lambda微积分(请参阅:module Term),最后是SystemT组合器微积分(请参阅: module Goedel)。最后一步还包含OCaml元编程Ast.expr类型。

我正在尝试不使用模板Haskell将其翻译为Haskell。

对于SystemT Combinator表单,我将其编写为

{-# LANGUAGE GADTs #-}

data TNat = Zero | Succ TNat

data THom a b where
  Id :: THom a a
  Unit :: THom a ()
  ZeroH :: THom () TNat
  SuccH :: THom TNat TNat
  Compose :: THom a b -> THom b c -> THom a c
  Pair :: THom a b -> THom a c -> THom a (b, c)
  Fst :: THom (a, b) a
  Snd :: THom (a, b) b
  Curry :: THom (a, b) c -> THom a (b -> c)
  Eval :: THom ((a -> b), a) b -- (A = B) * A -> B
  Iter :: THom a b -> THom (a, b) b -> THom (a, TNat) b

请注意,Compose是前向合成,与(.)不同。

在将SystemT Lambda演算转换为SystemT组合器演算期间,Elaborate.synth函数尝试将SystemT Lambda演算变量转换为一系列组成的投影表达式(与De Brujin指数有关)。这是因为组合器演算没有变量/变量名称。这是通过使用Elaborate.lookup函数的Quote.find完成的。

问题是我将组合器演算编码为GADT THom a b。翻译Quote.find函数:

  let rec find x  = function
    | [] -> raise Not_found
    | (x', t) :: ctx' when x = x' -> <:expr< Goedel.snd >> 
    | (x', t) :: ctx' -> <:expr< Goedel.compose Goedel.fst $find x ctx'$ >>

进入Haskell:

find :: TVar -> Context -> _
find tvar [] = error "Not Found"
find tvar ((tvar', ttype):ctx)
  | tvar == tvar' = Snd
  | otherwise     = Compose Fst (find tvar ctx)

导致无限类型错误。

• Occurs check: cannot construct the infinite type: a ~ (a, c)
  Expected type: THom (a, c) c
    Actual type: THom ((a, c), c) c

问题源于使用Compose GADT中的FstSndTHom a b可能导致类型签名无限变化的事实。

本文没有出现此问题,因为它似乎使用了Ast.expr OCaml来包装基础表达式。

我不确定如何在Haskell中解决。我应该使用像这样存在的量化类型吗?

data TExpr = forall a b. TExpr (THom a b)

或者某种类型的Fix来适应无限类型问题。但是,我不确定这如何改变findlookup函数。

1 个答案:

答案 0 :(得分:2)

这个答案必须有点概括,因为有三种完全不同的家族可能的设计来解决该问题。您正在做的事情似乎接近于三种方法,但是这些方法是按照越来越复杂的顺序排列的。

原始帖子中的方法

翻译原始帖子需要模板Haskell和局部性; find将返回代表某个Q.Exp的{​​{1}},避免了像原始帖子一样的问题。就像在原始帖子中一样,在对所有Template Haskell函数的输出进行类型检查时,将捕获原始代码中的类型错误。因此,仍然在运行前 捕获类型错误,但是您仍然需要编写 tests 来查找宏输出错误类型表达式的情况。一个人可以提供更强有力的保证,但代价是复杂性会大大增加。

输入和输出中的依存类型/ GADT

如果您希望与该帖子有所不同,一种选择是在全文中使用“依赖类型”,并使 input 成为依赖类型。我宽松地使用该术语来包括实际的依赖类型的语言,实际的依赖Haskell(登陆时)以及当今在Haskell中伪造依赖类型的方式(通过GADT,单例以及其他方法)。 但是,您将无法编写自己的类型检查器并选择要使用的类型系统。通常,您最终会嵌入一个简单类型的lambda演算,并可以通过可生成给定类型项的多态Haskell函数来模拟多态。在依赖类型的语言中,这更容易,但是在Haskell中完全可以实现。

但是,老实说,在这条路中,更容易使用高阶抽象语法和Haskell函数,例如:

Hom a b

如果您可以使用这种方法(此处省略了详细信息),则可以从GADT(复杂度有限)中获得很高的保证。但是,这种方法在许多情况下太不灵活,例如,因为键入上下文仅对Haskell编译器可见,而对您的类型或术语不可见。

从未键入的术语到已键入的术语

第三个选项是依赖类型的,以使您的程序仍将弱类型的输入转换为强类型的输出。在这种情况下,您的类型检查器会将某种类型的data Exp a where Abs :: (Exp a -> Exp b) -> Exp (a -> b) App :: Exp (a -> b) -> Exp a -> Exp b Var :: String -> Exp a —- only use for free variables exampleId :: Exp (a -> a) exampleId = Abs (\x -> x) 项转换为GADT ExprTExp gamma t等项。由于您不知道Hom a bgamma(或ta)是什么,因此您确实需要某种存在性。

但是简单的存在性太弱了:要构建更大的类型正确的表达式,您需要检查产生的类型是否允许它。例如,要从两个较小的b中构建一个包含TExpr表达式的Compose,则需要(在运行时)检查它们的类型是否匹配。有了简单的存在,您就不能做到。因此,您还需要在值级别上具有类型的表示形式。

(就像往常一样)还有更多烦人的存在,因为您永远无法在返回类型中公开隐藏类型变量,也无法将其投影(与从属记录/ sigma类型不同)。 老实说,我不完全确定这最终能否奏效。这是Haskell中可能的局部草图,最多TExpr的一种情况。

find