为什么有些Haskell函数不抽象具体的整数类型?

Nik*_*nov 0 polymorphism haskell types

例如,函数length抽象具体的sequence(Foldable),但不抽象具体的整数类型Int:

length :: Foldable t => t a -> Int
Run Code Online (Sandbox Code Playgroud)

拥有以下类型签名会更有用或更方便吗?

length' :: Foldable t, Integral i => t a -> i
Run Code Online (Sandbox Code Playgroud)

lef*_*out 5

是的,或许首先要注意的是,更多的多态性并不总是一件好事.如果所有函数都具有高度多态的参数结果,那么编译器几乎没有信息来启动类型推断,因此您最终必须键入更多笨拙的本地签名.

现在length,为什么你想要任何其他Integral类型的结果,但Int至少不是在64位机器上:

  • 较小的类型Word16通常不会在Haskell中提供很多性能或内存优势,因为会有一些拳击somwhere,然后你有一个只有16位信息的64位指针...有点傻.
  • 基本上不可能有一个列表(更不用说数组或映射)如此之大,以至于无法用63位字测量其长度.即使是一个疯狂的懒惰列表,它在任何时候都永远不会完全存在于内存中.
    现在,严格来说,Int只保证29在某些极端情况下可以用尽,但实际上这仅适用于32位平台,无论如何在内存和性能方面都有所限制,因此您不希望处理如此庞大的数据.
  • 就此而言,性能耗尽可能成为问题的任何应用程序都应该优化为比列表或其他更有效的东西Foldable(由于参数化而总是装箱); 未装箱的矢量或ByteString表现更好.
  • 如果您需要结果Integer或其他更昂贵的东西,不仅仅是因为长度而是因为上下文的原因,最好只计算长度Int在结束时转换一次,而不是拖慢整个缓慢添加名单.

也就是说,我确实有时希望签名实际上是实际存在的签名

genericlength :: (Foldable t, Num i) => t a -> i
Run Code Online (Sandbox Code Playgroud)

...注意,该函数不需要知道它的结果将是积分的.事实上,使结果合理化通常非常有用,所以我们可以随便写一下

average :: Fractional n => [n] -> n
average l = sum l / length l
Run Code Online (Sandbox Code Playgroud)

而不需要额外的fromIntegral.

原因不是这样的Prelude.length吗?我不知道,似乎颇具历史性.理由可能正如我在开始时所说的那样,你不需要太多的多态性.再说一遍,IMO通常最好将函数结果尽可能地变为多态,而是更严格地约束参数,因为这样结果总是会绑定到可以用于类型推断的东西.