如何在Haskell中评估此通用抽象语法树?

时间:2019-05-21 17:40:55

标签: haskell functional-programming tree abstract-syntax-tree gadt

我一直在寻找是否可以制作一个由操作和叶节点组成的非常简单的AST。但更具体地说,我希望能够使用任何类型作为叶节点,而不是像这样在AST数据类型本身中明确指定它。

-- Instead of this
data Tree = Number Int | Word String | Operation Tree (Tree -> Tree -> Tree) Tree

-- I'd like something along the lines of this
data Tree a = Leaf a | Operation Tree (Tree -> Tree -> Tree) Tree

这不一定具有很大的实用性,但是我想看看是否有可能。到目前为止,我所管理的最接近的产品要求我对GADT的概念进行摸索:

{-# LANGUAGE GADTs #-}

data Tree l where
  Leaf :: l -> Tree l
  Operation :: Tree a -> (a -> b -> c) -> Tree b -> Tree c

let fivePlus2 = Operation (Leaf 5) (+) (Leaf 2)

eval (Leaf l) = l
eval (Operation left op right) = op (eval left) (eval right)

我可以运行eval fivePlus2并得到7的想法。但是,对Operation的定义评估(最后一行)导致以下非常模糊的错误:

<interactive>:187:38: error:
    • Couldn't match type ‘a’ with ‘p’
      ‘a’ is a rigid type variable bound by
        a pattern with constructor:
          Operation :: forall a b c.
                       Tree a -> (a -> b -> c) -> Tree b -> Tree c,
        in an equation for ‘eval’
        at <interactive>:187:7-29
      ‘p’ is a rigid type variable bound by
        the inferred type of eval :: Tree p -> p
        at <interactive>:187:1-60
      Expected type: Tree a -> a
        Actual type: Tree p -> p
    • In the first argument of ‘op’, namely ‘(eval left)’
      In the expression: op (eval left) (eval right)
      In an equation for ‘eval’:
          eval (Operation left op right) = op (eval left) (eval right)
    • Relevant bindings include
        op :: a -> b -> p (bound at <interactive>:187:22)
        left :: Tree a (bound at <interactive>:187:17)
        eval :: Tree p -> p (bound at <interactive>:187:1)

<interactive>:187:50: error:
    • Couldn't match type ‘b’ with ‘p’
      ‘b’ is a rigid type variable bound by
        a pattern with constructor:
          Operation :: forall a b c.
                       Tree a -> (a -> b -> c) -> Tree b -> Tree c,
        in an equation for ‘eval’
        at <interactive>:187:7-29
      ‘p’ is a rigid type variable bound by
        the inferred type of eval :: Tree p -> p
        at <interactive>:187:1-60
      Expected type: Tree b -> b
        Actual type: Tree p -> p
    • In the second argument of ‘op’, namely ‘(eval right)’
      In the expression: op (eval left) (eval right)
      In an equation for ‘eval’:
          eval (Operation left op right) = op (eval left) (eval right)
    • Relevant bindings include
        right :: Tree b (bound at <interactive>:187:25)
        op :: a -> b -> p (bound at <interactive>:187:22)
        eval :: Tree p -> p (bound at <interactive>:187:1)

老实说,我完全不知道这意味着什么,而我在这里不甚了解,最初是在F#中尝试过的,但发现它的表现力不足。我已经有一段时间没有进行函数式编程了,而且我还是Haskell的初学者,所以如果能像我5岁时那样解释答案,我将不胜感激。

如果事实证明不可能评估这种树,那很好,但是我非常想知道它背后的逻辑是什么。谢谢!

1 个答案:

答案 0 :(得分:13)

向顶级函数添加类型签名:

eval :: Tree l -> l

这通常被认为是良好的做法,但这对于GADT尤其重要,因为否则无法推断出该类型。