具有类型类的Haskell函数参数类型推断
我试图理解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
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
表示现在aBool
的行为也类似于aNum
,因此您必须指定如何添加(+)
、乘以(*)
,等等
我希望我的解释足够简单。您会收到此错误消息,因为
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