以下程序类型检查我是否在命令行上指定它(例如ghci file.hs
):
import Data.Ratio
foo = let x = [1..]
y = (1%2) + (head x)
in y
但是,如果我以交互方式输入它,我将收到类型错误:
Prelude> import Data.Ratio
Prelude Data.Ratio> let x = [1..]
Prelude Data.Ratio> let y = (1%2) + (head x)
<interactive>:1:23:
Couldn't match expected type `Ratio a0' with actual type `Integer'
似乎x
被急切地输入为[Integer]
,而不是更为笼统的(Num t, Enum t) => [t]
。
我能做些什么吗?是否存在交互模式与批处理模式不同的其他情况?
答案 0 :(得分:10)
没有参数的绑定,即x = ...
形式的绑定受monomorphism restriction的约束,这意味着GHC将尝试使用任何可用的类型信息使其成为非多态的,并且重新开始type defaulting解决任何含糊之处。 (实际上,GHCi使用a slightly more permissive set of defaulting rules,但这对这个问题并不重要。)
由于在编写let x = [1..]
时没有其他类型信息可用,因此键入默认会导致类型被推断为[Integer]
,因为Integer
是默认的数字类型。
有几种方法可以解决这个问题:
使用类型签名。这总是有效,但在处理复杂类型时有时会很乏味。
> let x = [1..] :: [Rational]
使用参数编写绑定。这不适用于您的情况,但在编写无点函数定义时有时会看到此问题。
> let f = (+)
> :t f
f :: Integer -> Integer -> Integer
> let f x y = x + y
> :t f
f :: Num a => a -> a -> a
为类型检查器提供更多信息。在您的情况下,我们可以通过在一个let
语句中编写两个绑定来避免此问题。然后,GHC可以使用第二个绑定中的类型信息来正确推断x
应该具有类型[Rational]
。
> let x = [1..]; y = 1%2 + head x
> :t x
x :: [Ratio Integer]
禁用单态限制。如果您期望某些内容的类型,则可能会产生严重的性能影响。一个Integer
,虽然它实际上是Num a => a
,因为后者必须每次重新计算,而前者可以共享。这是限制存在的主要原因。
然而,在翻译中,这通常不是一个问题,所以方便通常是值得的。
> :set -XNoMonomorphismRestriction
> let x = [1..]
> :t x
x :: (Num t, Enum t) => [t]
如果您希望在默认情况下启用此功能,则可以将其添加到.ghci
file。
答案 1 :(得分:4)
您可以通过将x
定义为以下内容来做一些事情:
let x = [1..] :: [Ratio Int]
如:
Data.Ratio Prelude> let x = [1..] :: [Ratio Int]
Data.Ratio Prelude> let y = (1%2) + (head x)
Data.Ratio Prelude> y
3 % 2
Data.Ratio Prelude>