Haskell 应为类型,但具有种类‘;*-&燃气轮机;约束’;
我正在写一个加减数字的计算器。这里我有两个抽象,一个是Haskell 应为类型,但具有种类‘;*-&燃气轮机;约束’;,haskell,Haskell,我正在写一个加减数字的计算器。这里我有两个抽象,一个是Expr,它被建模为一个树,另一个是typeclass操作数,它包含树的左右节点。操作数有一个函数组合,用于将函数应用于左右节点: module Data.Calculator where class Operand a where combine :: a -> a data Operator = Plus | Minus data Expr = Node Operator Operand Operand | Value
Expr
,它被建模为一个树,另一个是typeclass操作数
,它包含树的左右节点。操作数有一个函数组合
,用于将函数应用于左右节点:
module Data.Calculator where
class Operand a where
combine :: a -> a
data Operator = Plus | Minus
data Expr = Node Operator Operand Operand | Value Integer
instance Operand Expr where
combine (Node op left right) =
case op of
Plus -> (combine left) + (combine right)
Minus -> (combine left) - (combine right)
combine (Value a) = (Value a)
instance Num Expr where
(+) (Value left) (Value right) = Value (left + right)
(*) (Value left) (Value right) = Value (left * right)
abs (Value a) = Value (abs a)
fromInteger i = Value i
negate (Value a) = Value (negate a)
当我试图编译它时,我得到了错误
calculator/src/Data/Calculator.hs:7:35: error:
• Expecting one more argument to ‘Operand’
Expected a type, but ‘Operand’ has kind ‘* -> Constraint’
• In the type ‘Operand’
In the definition of data constructor ‘Node’
In the data declaration for ‘Expr’
|
| data Expr = Node Operator Operand Operand | Value Integer
这是什么意思?我知道这个问题可以在不将操作数定义为typeclass的情况下解决,但我想使用
操作数
typeclass,因为这是我现在正在学习的主题。我做错了什么?在代码中,操作数不是类型,而是类型的类。不能有操作数
类型的值,因为它不是数据类型。因此,不能将其用作Expr
定义的一部分
隐式符号*->约束
意味着标识符操作数
,如果应用于类型(表示为*
),将给您一个约束
。编译器说它在该上下文中需要一个类型(例如Int
或String
或可能是Float
,等等),但您给了它操作数
,它具有种类*->约束
从您的代码中,我猜您实际上想做的是构造Expr
,使其可以包含任何类型的值,只要这些类型具有操作数的实例。这是正确的假设吗
如果是这样,那么方法是将这些值包装为存在类型:
data SomeOperand = forall a. Operand a => SomeOperand a
或GADT符号中的相同内容:
data SomeOperand where
SomeOperand :: forall a. Operand a => a -> SomeOperand
这些符号字面上表示类型SomeOperand
只包含一个值a
,该值必须具有操作数的实例
现在,您可以在Expr
的定义中使用SomeOperand
:
data Expr = Node Operator SomeOperand SomeOperand | Value Integer
现在,当在Expr
上进行匹配时,您将获得一个具有操作数实例的值,因此您将能够对其应用合并
:
f :: Expr -> Expr
f (Node op (SomeOperand a) (SomeOperand b)) = Expr op (combine a) (combine b)
f (Value i) = Value i
请注意,除了将这些操作数转换为同一类型的其他值之外,您实际上无法使用这些操作数执行任何操作,这同样让您无所事事。为了使其在任何方面都有用,类操作数
必须有一些方法来转换为类型本身以外的内容,例如:
class Operand a where
combine :: a -> a
showOperand :: a -> String
现在我可以使用showOperator
来达到一个有用的目的:
showExpr :: Expr -> Expr
showExpr (Node op (SomeOperand a) (SomeOperand b)) = showOperand a ++ show op ++ showOperand b
showExpr (Value i) = show i
需要为“Operand”增加一个参数,而“Operand”需要一个类型,但“Operand”具有种类“*->”约束“
请参阅EditsWakly realted:您定义了一个typeclassOperand
。对于两种不同类型的T1、T2
,您是否希望至少有两个实例实例操作数T1
和实例操作数T2
?如果您认为您将只使用操作数Expr
,这表明您不应该使用类型类,而只需定义combine::Expr->Expr
。你想实现什么?我怀疑你在考虑面向对象编程,将combine
作为基类Operand
和Expr
继承自Operand
的成员函数。类型类与OO类不同——特别是,它们不是类型。