【问题标题】:Polymorphic signature for non-polymorphic function: why not?非多态函数的多态签名:为什么不呢?
【发布时间】:2012-02-29 20:33:27
【问题描述】:

举个例子,考虑平凡函数

f :: (Integral b) => a -> b
f x = 3 :: Int

GHC 抱怨它无法推断 (b ~ Int)。该定义与签名相匹配,因为它返回的东西是 Integral(即 Int)。为什么 GHC 会/应该强迫我使用更具体的类型签名?

谢谢

【问题讨论】:

  • 这个签名说函数可以返回 any Integral 类型,这显然不是真的。

标签: haskell polymorphism


【解决方案1】:

Haskell 中的类型变量是通用量化的,所以Integral b => b 不仅仅意味着 some Integral 类型,它意味着 any Integral 类型。换句话说,调用者 可以选择应该使用哪些具体类型。因此,当类型签名说我应该能够选择任何Integral 类型时,函数总是返回Int 显然是一个类型错误,例如IntegerWord64

有一些扩展允许您使用existentially quantified type variables,但它们使用起来更麻烦,因为它们需要一个包装类型(为了存储类型类字典)。大多数时候,最好避免它们。但如果你确实想使用存在类型,它看起来像这样:

{-# LANGUAGE ExistentialQuantification #-}

data SomeIntegral = forall a. Integral a => SomeIntegral a

f :: a -> SomeIntegral
f x = SomeIntegral (3 :: Int)

使用此函数的代码必须具有足够的多态性才能与任何Integral 类型一起使用。我们还必须使用 case 而不是 let 进行模式匹配,以防止 GHC 的大脑爆炸。

> case f True of SomeIntegral x -> toInteger x
3
> :t toInteger
toInteger :: Integral a => a -> Integer

在上面的示例中,您可以将x 视为具有exists b. Integral b => b 类型,即一些未知的Integral 类型。

【讨论】:

  • 以防万一,@hammar 在谈到“GHC 的大脑爆炸”时指的是这个有趣的错误消息:) stackoverflow.com/a/213441/211906
  • 是的,但不不不,永远不要像这样使用存在量化。设计它的方法不是:只让f 返回一个普通的旧Int(或任何合适的具体类型),它比Integral a => a 包含更多信息。抽象边界应该出现在函数参数上,而不是结果上。
【解决方案2】:

你的函数最通用的类​​型是

f :: a -> Int

使用类型注解,你只能要求你想要一个更多特定类型,例如

f :: Bool -> Int

但您不能声明 less 特定类型。 Haskell 类型系统不允许您做出代码不保证的承诺。

【讨论】:

    【解决方案3】:

    正如其他人所说,在 Haskell 中,如果函数返回 x 类型的结果,这意味着 调用者 可以决定实际类型是什么。不是函数本身。换句话说,该函数必须能够返回与签名匹配的任何可能的类型

    这与大多数 OOP 语言不同,像这样的签名意味着 函数 可以选择它返回的内容。显然这让一些人感到困惑......

    【讨论】:

    • 最好不要将f :: Foo a => Int -> a 视为Foo f(int);,而是将其视为更像T f<T>(int) where T : Foo;(使用C# 语法)。
    猜你喜欢
    • 2016-01-19
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    相关资源
    最近更新 更多