具有类型类的Haskell函数参数类型推断

具有类型类的Haskell函数参数类型推断,haskell,types,pattern-matching,typeclass,Haskell,Types,Pattern Matching,Typeclass,我试图理解haskell错误消息,因为它们让新手程序员感到困惑。我能找到的最简单的例子是: Prelude> 1 + True <interactive>:2:3: No instance for (Num Bool) arising from a use of `+' Possible fix: add an instance declaration for (Num Bool) In the expression: 1 + True In an equation for

我试图理解haskell错误消息,因为它们让新手程序员感到困惑。我能找到的最简单的例子是:

Prelude> 1 + True
<interactive>:2:3:
No instance for (Num Bool)
  arising from a use of `+'
Possible fix: add an instance declaration for (Num Bool)
In the expression: 1 + True
In an equation for `it': it = 1 + True

如何确保仅当第二个参数也是(Num Bool)时(+)才能应用于(Num Bool)?

Num的约定是任何数值整数文本都可以转换为所需的类型

通过您的声明,Haskell在现实中尝试:

fromIntegral 1 + True
它可能会在第一个参数未定义的情况下调用布尔(+)。但这并不重要,因为你从来没有评估过

试着这样写:

(+) a b = if a then True else False

您可能会看到一个错误。

如果在
ghci
中运行以下命令,则错误消息表示
数据Bool
不是
类Num
的实例

Prelude> :info Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
    -- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
你可以看到一个
Num
是一个可以被添加
(+)
、乘以
(*)
、减去
(-)
,等等的东西

由于
Bool
不能这样做,因此它没有
Num
的实例。这解释了错误
无(Num Bool)
实例中的行,因为
(+)
只能用于
Num

您在
实例Num Bool中所做的是(+)a b=True
表示现在a
Bool
的行为也类似于a
Num
,因此您必须指定如何添加
(+)
、乘以
(*)
,等等


我希望我的解释足够简单。

您会收到此错误消息,因为
1
+
都是多态的——它们都可以用于不同的类型

看一看:

Prelude> :t 1
1 :: Num a => a
Prelude> :t (+)
(+) :: Num a => a -> a -> a
因此
1
+
对于
Num
类中的任何类型都是有意义的。因此,当您编写
1+Bool
时,如果
Bool
有一个
Num
实例,那么
1
实际上可能是一个
Bool
。事实上,你可以自己做:

instance Num Bool where
  fromInteger x = x /= 0
  (+) = (&&)
  ...
一旦您这样做,
1+True
将真正起作用。您还可以将数字文字用作布尔值:

*Main> 1 :: Bool
True
*Main> 1 + True
True

这也解释了为什么不管参数的顺序如何,都会出现相同的错误:代码中唯一的实际问题是
True
——如果这样做有效的话,其他一切都会发生。

Bool
肯定可以“像这样表现”(
instance Num Bool其中{(+)=(||);(*)=(&);negate=not;abs=id;signum=id;fromInteger=(/=0)}
),只是定义一个
Num
实例不是很明智,因为它允许更多的程序员错误被类型系统忽略。@Rhymoid:Side note-
(+)
最好被定义为独占的,否则会得到一个布尔环。@Rumca,从这个答案中可以看出,您已经确保两个参数都必须是
Bool
。让您困惑的是,当您在Haskell源文件中键入值
1
时,GHC会隐式地将其放入
fromInteger
调用中,这意味着
1
不一定是整数,而是可以在编译程序时转换为几乎任何类型。您可以自己测试这一点–询问ghci
\n->n+True
的类型。它会说
n
必须是
Bool
*Main> 1 :: Bool
True
*Main> 1 + True
True