48

我是 Haskell 的新手,我正在努力调试我的代码。修复错误会导致其他错误...

这是我的代码。

import Data.Maybe

data Op = Add | Sub | Mul | Div | And | Or | Not | Eq | Less | Great
    deriving (Eq, Show)

data Exp = Literal Value
     | Primitive Op [Exp]
     | Variable String
     | If Exp Exp Exp
     | Let [(String, Exp)] Exp
    deriving (Show, Eq)

data Value = Number Int
       | Bool Bool
       | String String
    deriving (Eq, Show)

type Env = [(String, Value)]

eval :: Env -> Exp -> Value
eval e (Literal v) = v
eval e (Variable x) = fromJust (lookup x e)   --22

prim :: Op -> [Value] -> Value
prim Add [Number a, Number b] = Number (a + b)
prim And [Bool a, Bool b] = Bool (a && b)
prim Sub [Number a, Nuamber b] = Number (a - b)   -- No instance for (Fractional Int) arising from a use of `/'
prim Mul [Number a, Number b] = Number (a * b)
prim Div [Number a, Number b] = Number (a / b)
prim Or [Bool a, Bool b] = Bool (a || b)
prim Not [Bool a] = Bool (not a)
prim Eq [Number a, Number b] = Bool (a == b)
prim Eq [String a, String b] = Bool (a == b) 
prim Less [Number a, Number b] = Bool (a < b)
prim Less [String a, String b] = Bool (a < b)
prim Great [Number a, Number b] = Bool (a > b)
prim Great [String a, String b] = Bool (a > b) --37

main = do
    eval [("y", (Number 40))] (Let [("x", (Literal (Number 2)))] (prim Add [(Variable "x"), (Variable "y")])) -- Couldn't match expected type `Exp' with actual type `Value'

我现在收到两个我在评论中写的错误。如果您知道我的代码有什么问题,请分享您的想法并节省我的时间...

非常感谢你。

4

2 回答 2

67
-- No instance for (Fractional Int) arising from a use of `/'

大概是来自这一行,而不是您的评论:

prim Div [Number a, Number b] = Number (a / b)

a并且bInts。除法运算符是(/) :: Fractional a => a -> a -> a(您可以通过启动 ghci 并输入来找到它:t (/),或者通过在 Hoogle 上查找它)。

如果您还没有看过类似的类型Fractional a => a -> a -> a,您应该分两部分阅读:

  1. 上下文Fractional a
  2. 方式a -> a -> a

这就像一个常规a -> a -> a类型,所以它接受两个某种类型的参数并返回相同类型的结果。添加上下文的唯一区别Fractional a是用于的类型a必须是Fractional类型类的实例;它不能随意覆盖您喜欢的任何类型。

如果您还没有了解类型类,请不要担心。它们很容易掌握,但不是刚开始时应该深入研究的东西;你稍后会找到他们。

Int不是Fractional类型类的成员,因此/运算符不适用于Ints。

原因是常规的数学除法不适用于这种类型的整数。3 / 2要么 give 1.5,在这种情况下它不适合 type Int -> Int -> Int,要么 give 1or 2,在这种情况下它不是正确的数学除法。有一个div实现整数除法的函数,可以像a `div` b中缀符号一样使用。

-- Couldn't match expected type `Exp' with actual type `Value'

此消息是关于您自己的类型,在您编写的单个表达式中。并且实际的完整错误消息将为您提供有关表达式的哪个部分包含错误的更多上下文。只需自上而下地进行,自己检查事物的类型,错误很快就会出现在你身上。

在这种情况下,您可以到这里:

Let [("x", (Literal (Number 2)))] (prim Add [(Variable "x"), (Variable "y")])

Let需要两个参数, a[(String, Exp)]和 an Exp。列表很好,但第二个参数是(prim Add [(Variable "x"), (Variable "y")]). 甚至没有深入研究它的子结构以查看它是否正确,primhas type Op -> [Value] -> Value,所以它不可能给你一个Exp.

如何解决这个问题取决于您;看起来您需要对整个表达式/值的区别进行一些重构。prim给你一个Value,你可以简单地在 a 中应用 wrapLiteral来解决你得到的类型错误,但是你遇到了prim应该采用 anOp和 a的问题[Value],但你似乎已经给了它 anOp和 a [Exp](包含变量)。我认为您需要考虑使用prim来计算原始应用程序的结果,使用Primitive构造函数Exp表示原始应用程序和使用之间的区别eval将(在环境中)任意表达式(可能包含几个原始应用程序)评估为一个值。

于 2013-02-01T23:42:25.103 回答
29

您遇到的问题是 Haskell 具有用于整数和“小数”除法的不同功能。整数除法会截断,小数除法不会。所以而不是

prim Div [Number a, Number b] = Number (a / b)

你想做

prim Div [Number a, Number b] = Number (a `div` b)

错误消息实际上意味着该函数(/)Fractional类的一部分。这基本上是一个不同类型可以实现的接口。要获取有关它的信息,请启动 ghci 并执行

Prelude> :i (/)
class Num a => Fractional a where
(/) :: a -> a -> a
...
-- Defined in `GHC.Real'
infixl 7 /

Prelude> :i Int
data Int = GHC.Types.I# GHC.Prim.Int#   -- Defined in `GHC.Types'
instance Bounded Int -- Defined in `GHC.Enum'
instance Enum Int -- Defined in `GHC.Enum'
instance Eq Int -- Defined in `GHC.Classes'
instance Integral Int -- Defined in `GHC.Real'
instance Num Int -- Defined in `GHC.Num'
instance Ord Int -- Defined in `GHC.Classes'
instance Read Int -- Defined in `GHC.Read'
instance Real Int -- Defined in `GHC.Real'
instance Show Int -- Defined in `GHC.Show'

第一个为您提供有关该函数的信息(/):它告诉您它在类中Fractional。然后,当您:i Int显示Int. 请注意,这Int不是一个实例,Fractional因此您不能使用(/)with Int

另一个提示:反引号 (`) 将函数转换为中缀运算符,因此

a `div` b

是相同的

div a b
于 2013-02-01T23:33:34.750 回答