Haskell 无法推断(b~a)

Haskell 无法推断(b~a),haskell,Haskell,我有下面的代码,我正试图为解释器编写一个抽象语法树,&我不想把所有内容都塞进同一个数据类型,所以我打算编写一个具有基本行为的typeclass(在本例中是AST) 当我在typeclassAST中删除reduce的默认实现时,它可以很好地编译,但当我提供一个返回它自己的实现时,它会抱怨。我得到以下编译器错误 src/Data/AbstractSyntaxTree.hs:13:18: Could not deduce (b ~ a) from the context (AST a)

我有下面的代码,我正试图为解释器编写一个抽象语法树,&我不想把所有内容都塞进同一个
数据
类型,所以我打算编写一个具有基本行为的typeclass(在本例中是
AST

当我在typeclass
AST
中删除
reduce
的默认实现时,它可以很好地编译,但当我提供一个返回它自己的实现时,它会抱怨。我得到以下编译器错误

src/Data/AbstractSyntaxTree.hs:13:18:
  Could not deduce (b ~ a)
  from the context (AST a)
    bound by the class declaration for `AST'
    at src/Data/AbstractSyntaxTree.hs:(11,1)-(13,20)
  or from (AST b)
    bound by the type signature for reduce :: AST b => a -> Env -> b
    at src/Data/AbstractSyntaxTree.hs:12:13-36
    `b' is a rigid type variable bound by          
        the type signature for reduce :: AST b => a -> Env -> b
        at src/Data/AbstractSyntaxTree.hs:12:13
    `a' is a rigid type variable bound by
        the class declaration for `AST'
        at src/Data/AbstractSyntaxTree.hs:11:11
  In the expression: ast
  In an equation for `reduce': reduce ast _ = ast
AST
reduce
行为将评估
AST
,偶尔返回不同类型的
AST
,有时返回相同类型的
AST

编辑:关于所有a的数据表达式。AST a=>Expr a
&
GADT
s 我最初使用的是
dataexpr=forall a。AST a=>Expr a
,因为我想表示这样的类型

(+ 2 true) -> Compound [Expr (Ref "+"), Expr 2, Expr true]

(+ a 2) -> Compound [Expr (Ref "+"), Expr (Ref "a"), Expr 2]

(eval (+ 2 2)) -> Compound [Expr (Ref "eval"), 
                     Compound [
                       Expr (Ref "+"), 
                       Expr 2, 
                       Expr 2]]

((lambda (a b) (+ a b)) 2 2) -> Compound [Expr SomeLambdaAST, Expr 2, Expr 2]
因为我是从文本生成AST的,所以我觉得在GADT中表示严格类型的AST是一个负担,尽管我确实看到它们在Haskell中的DSL这样的情况下有何用处

但由于我是从文本生成AST(可能包含上面的一些示例),所以可能有点难以预测我最终会得到什么AST。我也不想在
s&
之间玩杂耍,也许
s。这就是我上次所做的&那是一场混乱&我放弃了在哈斯克尔尝试这一点

但是我又不是最有经验的Haskell程序员,所以也许我看错了,也许我可以用更严格的打字来实现AST,所以我会看一看,看看我是否能想出GADT,但我有疑问&我有一种感觉,它可能会像上次那样结束

最终,我现在只是想通过一个有趣的、可完成的项目来学习Haskell,所以我不介意我的第一个Haskell项目是否真的不是地道的Haskell。让某些东西发挥作用是一个更高的优先事项,这样我就可以在语言中找到自己的路,并为它展示一些东西

更新: 我采纳了@cdk&@bheklir的建议,抛弃了存在主义类型,尽管我选择了一种更简单的类型,而不是使用GADTs(也由@cdk&@bheklir建议)。它可能是一个更强的类型,但我只是想熟悉Haskell,所以我在几个小时后放弃了&使用了一个简单的数据类型,如:P

import qualified Data.Map as M

type Environment = [M.Map String AST]

data AST
  = Compound [AST]
  | FNum Double
  | Func Function
  | Err  String
  | Ref  String

data Function
  = NativeFn ([AST] -> AST)
  | LangFn   [String] AST

-- previously called reduce
eval :: Environment -> AST -> AST
eval env ast = case ast of
  Ref ref -> case (lookup ref env ) of
    Just ast -> ast
    Nothing  -> Err ("Not in scope `" ++ ref ++ "'")

  Compound elements -> case elements of
    []              -> Err "You tried to invoke `()'"
    function : args -> case (eval env function) of
      Func fn -> invoke env fn args
      other   -> Err $ "Cannot invoke " ++ (show other)

  _ -> ast

-- invoke & lookup are defined else where 

尽管我可能仍然会关注gadt,因为它们看起来非常有趣&这让我找到了一些关于在haskell中实现抽象语法树的有趣阅读材料。

您在理解错误消息的哪一部分方面有困难?我想这很清楚

reduce
的类型为

reduce :: AST b => a -> Env -> b
第一个参数的类型为
a
,GHC希望
reduce
返回
b
类型的内容,这可能与
a
完全不同。GHC抱怨您试图返回值
a
,而它期望
b
,这是正确的

“类型类的存在量化”是一种反模式(如贝克利尔所指出的)。更好的方法是为
AST
创建代数数据类型:

data AST a
现在
reduce
变成了一个简单的函数:

reduce :: Env -> AST a -> AST b
如果希望
reduce
能够返回不同类型的
AST
,可以使用

reduce :: Env -> AST a -> Either (AST a) (AST b)

但我不认为这是你真正想要的。我的建议是查看创建AST的
GADT
风格,并重新评估您的方法。

您在理解错误信息的哪一部分方面有困难?我想这很清楚

reduce
的类型为

reduce :: AST b => a -> Env -> b
第一个参数的类型为
a
,GHC希望
reduce
返回
b
类型的内容,这可能与
a
完全不同。GHC抱怨您试图返回值
a
,而它期望
b
,这是正确的

“类型类的存在量化”是一种反模式(如贝克利尔所指出的)。更好的方法是为
AST
创建代数数据类型:

data AST a
现在
reduce
变成了一个简单的函数:

reduce :: Env -> AST a -> AST b
如果希望
reduce
能够返回不同类型的
AST
,可以使用

reduce :: Env -> AST a -> Either (AST a) (AST b)

但我不认为这是你真正想要的。我的建议是查看创建AST的
GADT
风格,并重新评估您的方法。

您的默认实现无法编译,因为它的定义错误

reduce :: AST b => a -> b -> Env -> b
reduce ast _ = ast
现在,
ast
具有类型
a
,reduce函数返回类型
b
,但根据您的实现,您返回类型为
a
ast
,但编译器需要
b

即使是这样的事情也能奏效:

reduce :: AST b => a -> b -> Env -> b
reduce _ ast _  = ast

默认实现无法编译,因为它的定义错误

reduce :: AST b => a -> b -> Env -> b
reduce ast _ = ast
现在,
ast
具有类型
a
,reduce函数返回类型
b
,但根据您的实现,您返回类型为
a
ast
,但编译器需要
b

即使是这样的事情也能奏效:

reduce :: AST b => a -> b -> Env -> b
reduce _ ast _  = ast

您错误地解释了此类型签名(以OO程序员常见的方式):

这并不意味着reduce可以选择它喜欢的任何类型(即AST的成员)并返回该类型的值。如果是这样,您的实现将是有效的。相反,这意味着对于调用者喜欢的任何类型b(即AST的成员),reduce必须能够返回该类型的值。b有时很可能与a相同,但这是呼叫者的选择,而不是reduce的选择

如果您的实现返回类型为a的值,那么只有当b始终相等时,这才是真的