繁体   English   中英

Haskell中的类型类行为

[英]Typeclass behavior in Haskell

作为Haskell的初学者,我很难理解为什么会失败

-- this works fine of course
f :: Float
f = 1.0

f :: Num a => a
f = 1.0
-- Could not deduce (Fractional a) arising from the literal ‘1.0’
--      from the context: Num a
--        bound by the type signature for:
--                   f :: forall a. Num a => a

我的困惑源于他们都有Num的例子。 因此,由于Int,Integers,Doubles等都有Num类型类的实例,为什么我不能在f填充任何数值?

例如, negate它有一个签名negate :: Num a => a -> a将与廉政局的浮动双打等工作

任何见解将不胜感激。

这里的问题是,当你写f :: Num a => a ,这意味着f必须为所有可能的实例运行a使得Num a实例存在。 特别是,这意味着在其他地方写入(f :: Int)应该可以正常工作,因为instance Num Int肯定存在。 但是,为f返回的值是1.0 ,它不是整数: 1.0 :: Fractional p => p 错误消息基本上是说“知道a是一个Num并没有告诉我们a是一个Fractional ,所以分数字面1.0没有办法让类型为a ”。

想想它的一种方法是, 调用者就可以选择什么a应该是:这就是为什么这种形式的类型签名被称为普遍量化

您可能正在考虑存在量化: f返回某种Num ,但“调用者”不知道Num返回了什么。 这通常不如通用量化那么有用,并且在Haskell中有点笨拙,但可以像这样完成:

{-# LANGUAGE ExistentialQuantification #-} -- This is a GHC extension
-- A SomeNum value contains some kind of Num. Can't see what kind from the "outside".
data SomeNum = forall a. Num a => SomeNum a
f :: SomeNum
f = SomeNum 5.0

在Haskell中,像f :: a这样的签名是(概念上更清晰的) f :: forall a. a语法f :: forall a. a f :: forall a. a ,其中forall很像“类型级lambda”(而不是f :: exists a. a你可能一直在考虑)。 事实上,如果你看一下GHC Core,你会看到所有这些类型的应用程序都是显式的:每当你使用一个通用量化的函数时,“调用者”显式地传入要用于每个类型变量的类型。

但是,我建议您不要在此阶段尝试使用存在量化:通常有更好/更容易的替代方案。 我只是想解释它,以帮助显示存在与普遍性之间的区别。

为什么我不能在f填充任何数值?

例如,具有签名negate :: Num a => a -> a将适用于IntFloatDouble s等。

正因为如此!

你不能在f定义中填充任何数值,因为你不能在negate定义中填充任何数值。

假设我们试图定义negate如下

negate :: Num a => a -> a
negate x = 10.5 - 10.5 - x

这会对Int工作吗? 也就是说, negate 42 :: Int 不,因为10.5不是Int

由于negate的类型承诺它适用于任何数字类型,包括Int ,但它实际上不适用于Int ,因此承诺被破坏。 静态类型检查因此拒绝该代码。

同样,如果接受了类型检查

f :: Num a => a
f = 10.5

然后所有这些都应该工作: f + 8 :: Intf / 2 :: Doublef - 4 :: Integer 但是10.5不适合Int (也不适合Integer )。

这里的问题是f :: Num a => a允许调用者选择任何数字类型a 由于类型允许调用者选择, f选择,但必须适应调用者做出的任何选择。 因此, f不能使用仅适用于某些数字类型的代码,而不能使用其他类型的代码。

如果f仅适用于Fractional类型( Num类型的子集),则f的类型必须向调用者通告其选择仅限于小数类型。 这是通过使用f :: Fractional a => a来完成的。

如果你只是写

f = 10.5

你回来了f :: Fractional a => a没有问题。

当你明确声明它的类型为Num a => a ,Haskell必须统一声明的和实际的类型,而它不能,因为FractionalNum子类

>> :i Fractional
class Num a => Fractional a where
  --  ^^^                              -- Fractional is Num's subclass
  ...........
  fromRational :: Rational -> a
  ...........

每个Fractional都是一个Num ,但不是每个Num都是Fractional


fromRational的类型来自fromRational :: Fractional a => Rational -> a

这表明像10.5这样的浮点文字实际上是从fromRational (10.5 :: Rational)读取的,就像整个数字文字(如10被读作fromInteger (10 :: Integer)

实际上, Haskell教程中的第10.3节内容如下:

整数(不带小数点)实际上等同于fromInteger到数字值作为Integer 类似地,浮动数字(带小数点)被视为fromRational到数字值作为Rational 因此, 7具有类型(Num a) => a ,并且7.3具有类型(Fractional a) => a

当你给编译器类型声明一样f :: Num a => a ,你不只是说f有类型类Num但是这一切 ,你知道它在这里。 由于您输入的是1.0而不是1 ,因此编译器断定您还需要Fractional 你的声明说你没有,所以这不能编译。 如果你说它是Fractional a => a ,它会编译,而Fractional表示Num

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM