Haskell 什么是单态限制?

Haskell 什么是单态限制?,haskell,types,polymorphism,type-inference,monomorphism-restriction,Haskell,Types,Polymorphism,Type Inference,Monomorphism Restriction,我对haskell编译器有时如何推断更少的类型感到困惑 多态性比我预期的要多,例如使用无点定义时 问题似乎是“单态限制”,它在 编译器的旧版本 考虑以下haskell计划: {-# LANGUAGE MonomorphismRestriction #-} import Data.List(sortBy) plus = (+) plus' x = (+ x) sort = sortBy compare main = do print $ plus' 1.0 2.0 print $

我对haskell编译器有时如何推断更少的类型感到困惑 多态性比我预期的要多,例如使用无点定义时

问题似乎是“单态限制”,它在 编译器的旧版本

考虑以下haskell计划:

{-# LANGUAGE MonomorphismRestriction #-}

import Data.List(sortBy)

plus = (+)
plus' x = (+ x)

sort = sortBy compare

main = do
  print $ plus' 1.0 2.0
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]
如果我使用
ghc
编译此文件,则不会得到任何错误,可执行文件的输出为:

3.0
3.0
[1,2,3]
如果我将
主体更改为:

main = do
  print $ plus' 1.0 2.0
  print $ plus (1 :: Int) 2
  print $ sort [3, 1, 2]
我没有得到编译时错误,输出变成:

3.0
3
[1,2,3]
正如所料。但是,如果我尝试将其更改为:

main = do
  print $ plus' 1.0 2.0
  print $ plus (1 :: Int) 2
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]
我得到一个类型错误:

test.hs:13:16:
没有由文字“1.0”产生的(分数Int)实例
在“plus”的第一个参数中,即“1.0”
在“($)”的第二个参数中,即“加1.0 2.0”
在“do”块的stmt中:打印$plus 1.0 2.0
尝试使用不同类型调用两次
sort
时也会发生同样的情况:

main = do
  print $ plus' 1.0 2.0
  print $ plus 1.0 2.0
  print $ sort [3, 1, 2]
  print $ sort "cba"
产生以下错误:

test.hs:14:17:
没有由文字“3”产生的(Num Char)实例
在表达式中:3
在'sort'的第一个参数中,即'[3,1,2]'
在“($)”的第二个参数中,即“sort[3,1,2]”
  • 为什么
    ghc
    突然认为
    plus
    不是多态的,需要一个
    Int
    参数? 对
    Int
    的唯一引用是在
    plus
    的应用程序中,这有什么关系 当定义明显是多态的时候
  • 为什么
    ghc
    突然认为
    sort
    需要
    Num Char
    实例
此外,如果我尝试将函数定义放入它们自己的模块中,如:

{-# LANGUAGE MonomorphismRestriction #-}

module TestMono where

import Data.List(sortBy)

plus = (+)
plus' x = (+ x)

sort = sortBy compare
我在编译时遇到以下错误:

TestMono.hs:10:15:
没有因使用“比较”而导致的(Ord a0)实例
类型变量“a0”不明确
相关绑定包括
排序::[a0]->[a0](绑定在TestMono.hs:10:1)
注意:有几个潜在的例子:
实例积分a=>Ord(GHC.Real.Ratio a)
--在“GHC.Real”中定义
实例Ord()--在“GHC.Classes”中定义
实例(Ord a,Ord b)=>Ord(a,b)--在“GHC.Classes”中定义
…加上其他23个
在'sortBy'的第一个参数中,即'compare'
在表达式中:sortBy compare
在“排序”的等式中:sort=sortBy compare
  • 为什么
    ghc
    不能将多态类型
    orda=>[a]->[a]
    用于
    排序
  • 为什么
    ghc
    对待
    plus
    plus'
    的方式不同
    plus
    应具有 多态类型
    numa=>a->a->a
    ,我真的看不出这有什么不同 从
    sort
    的类型,但只有
    sort
    会引发错误
最后一件事:如果我对
sort
的定义进行注释,文件将编译。然而 如果我尝试将其加载到
ghci
并检查得到的类型:

*TestMono>:t plus
整数->整数->整数
*TestMono>:t plus'
加上“::Num a=>a->a->a
为什么
plus
的类型不是多态的


这是关于Haskell中单态限制的规范问题 如中所述。

什么是单态限制? Haskell wiki所述的目标是:

Haskell类型推理中的反直觉规则。 如果您忘记提供类型签名,有时会填写此规则 使用“类型默认”规则,使用特定类型的自由类型变量

这意味着,在某些情况下,如果您的类型不明确(即多态性) 编译器将选择将该类型实例化为不含糊的类型

我怎么修理它? 首先,您总是可以显式地提供一个类型签名,这将 避免触发限制:

plus :: Num a => a -> a -> a
plus = (+)    -- Okay!

-- Runs as:
Prelude> plus 1.0 1
2.0
或者,如果要定义函数,可以避免, 例如写:

plus x y = x + y
关掉它 可以简单地关闭限制,这样您就不必这样做 对代码进行任何修改都可以修复它。该行为由两个扩展控制:
MonomorphismRestriction
将在
NoMonomorphismRestriction
将禁用它

您可以将以下行放在文件的最顶端:

{-# LANGUAGE NoMonomorphismRestriction #-}
如果您使用的是GHCi,则可以使用
:set
命令启用扩展:

Prelude> :set -XNoMonomorphismRestriction
您还可以通过命令行告诉
ghc
启用扩展:

ghc ... -XNoMonomorphismRestriction
注意:与通过命令行选项选择扩展相比,您应该更喜欢第一个选项

有关此扩展和其他扩展的说明,请参阅

完整的解释 我将尝试在下面总结您需要知道的一切,以了解 单态限制是,为什么它被引入和它的行为

一个例子 以下面的简单定义为例:

plus = (+)
plus = (+)
您可能认为能够将每次出现的
+
替换为
plus
。特别是自从
(+)::Num a=>a->a->a
之后,您希望也有
plus::Num a=>a->a->a

不幸的是,情况并非如此。例如,如果我们在GHCi中尝试以下操作:

Prelude> let plus = (+)
Prelude> plus 1.0 1
我们得到以下输出:

:4:6:
没有由文字“1.0”产生的(分数整数)实例
在“plus”的第一个参数中,即“1.0”
在表达式中:加1.0 1
在“it”的方程式中:it=加1.0 1
在较新的GHCi版本中,您可能需要
:set-XMonomorphismRestriction

事实上,我们可以看到,
plus
的类型并不是我们所期望的:

Prelude> :t plus
plus :: Integer -> Integer -> Integer
发生的事情是编译器看到
plus
的类型f x = x + 1
<pattern> = expr
plus = (+)
plus = (+)

x :: Integer
x = plus 1 2

y :: Double
y = plus 1.0 2
Integer -> Integer -> Integer
plus = (+)
x = plus 1.0 2
  genericLength :: Num a => [b] -> a
  let len = genericLength xs
  in (len, len)
  let len :: Num a => a
      len = genericLength xs
  in (len, len)
f xs = (len, len)
  where
    len = genericLength xs
f :: Num a, Num b => [c] -> (a, b)
f :: Num a => [b] -> (a, a)
 [(n,s)] = reads t
 reads :: (Read a) => String -> [(a,String)]
plus = (+)
plus = (+)

x = plus 1.0 2.0
let x = read "<something>" in show x
show :: Show a => a -> String
read :: Read a => String -> a
let x = read "<something>" :: Int in show x
show 1
plus = (+)
minus = (-)

x = plus 1.0 1
y = minus 2 1
plus :: Fractional a => a -> a -> a
minus :: Num a => a -> a -> a
plus :: Double -> Double -> Double
minus :: Integer -> Integer -> Integer