我们有一个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)
。
Cofree
,Product
,Fix
并使用自定义Label
)?w
函数?答案 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) = ...
您可以将calcEnv
和calcResult
放在原始代数的形式中,如下所示:
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