【问题标题】:Why doesn't f=(+) need a type annotation?为什么 f=(+) 不需要类型注释?
【发布时间】:2015-02-12 00:24:22
【问题描述】:

我的意思是,例如,

f :: (Enum a) => a -> a --without this line, there would be an error
f = succ

这是因为succ需要它的参数是可枚举的(succ :: (Enum a) => a -> a

但对于(+)

f = (+) --ok

虽然(+) 的声明是(+) :: (Num a) => a –> a –> a

我的意思是,我为什么不需要将f 声明为f :: (Num a) => a –> a –> a

【问题讨论】:

    标签: haskell functional-programming


    【解决方案1】:

    因为默认。 Num 是一个“可默认”类型类,这意味着如果您不对其进行约束,编译器将对您打算将其用作哪种类型进行一些智能猜测。尝试将该定义放入模块中,然后运行

    :t f
    

    ghci;它应该告诉你(IIRC)f :: Integer -> Integer -> Integer。编译器不知道你想使用哪个a,所以它猜测Integer;既然这样行得通,它就符合这个猜测。

    为什么它不推断f 的多态类型?由于 dreaded[1] 单态性限制。当编译器看到

    f = (+)
    

    它认为'f 是一个值',这意味着它需要一个(单态)类型。 Eta-将定义扩展为

    f x = (+) x
    

    你会得到多态类型

    f :: Num a => a -> a -> a
    

    如果您对第一个定义进行 eta-expand,则类似

    f x = succ x
    

    你不再需要类型签名了。

    [1] GHC 文档中的实际名称!

    【讨论】:

    【解决方案2】:

    我的意思是,我为什么不需要将f 声明为(+) :: (Num a) => a –> a –> a

    如果您声明了f 的签名,您确实需要这样做。但是如果你不这样做,编译器会“猜测”签名本身——在这种情况下,这并不是很了不起,因为它基本上可以复制和粘贴(+) 的签名。而这正是它要做的。

    ...或者至少它应该做什么。确实如此,前提是您打开了-XNoMonomorphism 标志。否则,好吧,dreaded monomorphism restriction 介入,因为f 的定义是ConstantApplicativeForm = Value 的形状;这使得编译器将签名简化为它可以找到的下一个最佳非多态类型,即Integer -> Integer -> Integer。为了防止这种情况,您实际上应该为所有顶级函数手动提供正确的签名。这也避免了很多混乱,许多错误变得不那么混乱了。

    单态性限制是原因

    f = succ
    

    不会单独工作:因为它也有这种 CAF 形状,所以编译器不会尝试推断正确的多态类型,而是尝试找到一些具体的实例来生成单态签名。但与Num 不同的是,Enum 类不提供默认实例。

    可能的解决方案,按偏好排序:

    1. 总是添加签名。你真的应该。
    2. 启用-XNoMonomorphismRestriction
    3. f a = succ af a b = a+b 的形式编写函数定义。因为有明确提到的参数,所以这些参数不符合 CAF 的条件,因此单态限制不会生效。

    【讨论】:

      【解决方案3】:

      Haskell 默认 Num 约束为 IntInteger,我忘记了。

      【讨论】:

        猜你喜欢
        • 2017-12-15
        • 1970-01-01
        • 1970-01-01
        • 2019-07-22
        • 1970-01-01
        • 1970-01-01
        • 2021-07-26
        • 2013-01-05
        相关资源
        最近更新 更多