gpt4 book ai didi

haskell - 需要一个类型,但有种类 ‘* -> Constraint’

转载 作者:行者123 更新时间:2023-12-02 10:47:35 25 4
gpt4 key购买 nike

我正在编写一个计算器来加减数字。我这里有两个抽象,一个Expr,它被建模为一棵树,以及一个类型类Operand,它包含树的左节点和右节点。操作数有一个函数combine,它将函数应用于左右节点:

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

这是什么意思?我知道这个问题可以在不将 Operand 定义为类型类的情况下解决,但我想使用 Operand 类型类,因为这是我现在正在学习的主题。我做错了什么?

最佳答案

在您的代码中,Operand 不是类型,而是类型的。不能有 Operand 类型的值,因为它不是数据类型。因此,您不能将其用作 Expr 定义的一部分。

神秘符号 * -> Constraint 表示标识符 Operand,如果应用于类型(表示为 *),将为您提供一个约束。编译器表示它需要该上下文中的类型(例如 IntStringMaybe Float 等),但您给出了它Operand,具有类型* -> Constraint

从你的代码中,我猜你真正想要做的是构造 Expr ,使其可以包含任何类型的值,只要因为这些类型有一个 Operand 实例。这是一个正确的假设吗?

如果是这样,那么方法是将这些值包装在存在类型中:

data SomeOperand = forall a. Operand a => SomeOperand a

或者 GADT 表示法中的相同内容:

data SomeOperand where
SomeOperand :: forall a. Operand a => a -> SomeOperand

这些符号的字面意思是类型 SomeOperand 恰好包装了一个值 a,该值必须具有 Operand 的实例。

现在您可以在 Expr 的定义中使用 SomeOperand:

data Expr = Node Operator SomeOperand SomeOperand | Value Integer

现在,当匹配 Expr 时,您将获得一个具有 Operand 实例的值,因此您将能够应用 combine 到它:

f :: Expr -> Expr
f (Node op (SomeOperand a) (SomeOperand b)) = Expr op (combine a) (combine b)
f (Value i) = Value i

请注意,除了将它们转换为相同类型的其他值之外,您实际上无法对此类操作数执行任何操作,这又让您无处可去。为了使其以任何方式有用,Operand 类必须具有一些方法来转换为类型本身以外的其他内容,例如:

class Operand a where
combine :: a -> a
showOperand :: a -> String

现在我可以使用 showOperand 达到有用的目的:

showExpr :: Expr -> Expr
showExpr (Node op (SomeOperand a) (SomeOperand b)) = showOperand a ++ show op ++ showOperand b
showExpr (Value i) = show i

关于haskell - 需要一个类型,但有种类 ‘* -> Constraint’,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52267261/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com