Haskell:使用算法W标记带有类型信息的AST

时间:2017-09-09 20:43:14

标签: haskell abstract-syntax-tree type-inference hindley-milner recursion-schemes

我们有一个AST定义:

data Term a 
  = Lam String a
  | App a a
  | Var String
  deriving(Read,Show,Eq,Functor,Foldable,Traversable)

用于类型推理的F代数:

type Wrapped m a = Enviroment -> m a
type Result m = Wrapped (Type,Substitution)
w ::
  (MonadState Int, MonadError TypeError) 
  => Term (Result m)
  -> Result m
w term env = ...

我们可以使用cata

获得运行推理的结果
infer :: 
  (MonadState Int, MonadError TypeError) 
  => Fix Term 
  -> Result m
infer ast = cata hm ast

但是现在我希望结果是原始的AST注释每个表达式的类型信息,所以现在infer' :: Fix Term -> Wrapped (Labeled Term Type)

  1. 我应该使用哪种数据结构来注释树(CofreeProductFix并使用自定义Label)?
  2. 如何使用递归方案实现此功能,而无需修改原始的w函数?

1 个答案:

答案 0 :(得分:4)

这个答案确实修改了函数w,但它仍然旨在保持“主力”函数与递归方案机制分离。

让我们按原样保留Term类型,并假设我们有一个类型E用于向下计算的环境,而类型R用于向上计算的最终注释从树叶。

我们还假设我们有这两个函数:

calcEnv :: E -> Term x -> E -- calculates the environment which will be passed downwards

calcResult :: E -> Term R -> IO R -- effectfully calculates the result flowing upwards

为简单起见,我使用IO作为monad。

(请注意,我假设“计算环境”不会产生影响。我不是这种情况,然后这个解决方案不会这样做。)

我们分两个阶段工作。首先,我们构造一个树,其中节点已经用它们的环境注释。我们使用anamorphism,而不是从catamorphism返回函数的“技巧”。

import qualified Control.Comonad.Trans.Cofree as COFREEF

annotate :: E -> Fix Term -> Cofree Term E
annotate = curry (ana coalg)
    where
    coalg :: (E, Fix Term) -> COFREEF.CofreeF Term E (E, Fix Term)
    coalg (environment, Fix term) =
        let environment' = calcEnv environment term
        in  environment COFREEF.:< fmap ((,) environment') term

(请记住type instance Base (Cofree f a) = CofreeF f a。这就是COFREEF.:< comes from的位置。它基本上是一对纯值,另一个值包含在仿函数中。)

在下一阶段,我们有效地使用叶子中带注释的树来生成最终结果 - 带有R注释的树:

calculate :: Cofree Term E -> IO (Cofree Term R)
calculate = cata alg
    where
    alg :: COFREEF.CofreeF Term E (IO (Cofree Term R)) -> IO (Cofree Term R)
    alg (environment COFREEF.:< termio) = do
        term :: Term (Cofree Term R) <- sequenceA termio
        result :: R <- calcResult environment (fmap extract term)
        return $ result :< term

我分两个阶段进行,因为我在将“返回函数”技巧与返回带注释的树组合时遇到了麻烦。

一个变形后跟一个变形被称为hylomorphism。我们可以使用hylo定义组合函数,如下所示:

together :: E -> Fix Term -> IO (Cofree Term R)
together = curry (hylo alg coalg)
    where
    coalg (environment, Fix term) = ...
    alg (environment COFREEF.:< termio) = ...

您可以将calcEnvcalcResult放在原始代数的形式中,如下所示:

w :: Term (E -> IO R) -> E -> IO R
w term environment = do
    let downwards = calcEnv environment term
    tr :: Term R <- sequenceA $ fmap ($ downwards) term
    calcResult environment tr