Haskell 什么是单态限制?
我对haskell编译器有时如何推断更少的类型感到困惑 多态性比我预期的要多,例如使用无点定义时 问题似乎是“单态限制”,它在 编译器的旧版本 考虑以下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 $
{-# 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