ghci - 默认混淆

5

在检查不同整型的大小(minBoundmaxBound)和“十进制表示下的长度”时,我偶然看到了一些奇怪的行为。

使用GHCi:

Prelude> :{
Prelude| let mi = minBound
Prelude|     ma = maxBound
Prelude|     le = fromIntegral $ length $ show ma
Prelude|  in [mi,ma,le] :: [Int]
Prelude| :}
[-9223372036854775808,922372036854775807,2]
                                         ^

在我最不期望的地方,我得到了 19
我的第一个猜测是maxBound默认为(),因此产生了2,但我不理解这一点,因为ma应该是一个Int,通过显式类型注释(:: [Int])- 通过引用透明度,所有名为ma的符号应该相等。
如果我把上面的语句放在一个文件中并加载到GHCi中,我会得到正确的结果。
那么我为什么会得到错误的结果?

我认为你的第一个猜测非常接近,ma的类型直到后面才被固定为Int,所以le使用的是ma :: Bounded a => a,它在显示中默认为(). 如果你再用ma = maxBound :: Int试一次,应该会得到19。 - jkeuhlen
1
类型怎么可能不固定? - epsilonhalbe
当你期望相反的行为时,任何行为都可能令人困惑。 - Reid Barton
1个回答

13

令人困惑的是,这仍然是单态限制在起作用(或者说是GHCi中缺少单态限制)。由于GHCi没有启用单态限制,因此您对mima的定义不会像您认为的那样专门化为Int - 而是像mi,ma :: Bounded a = > a这样保持普通,并且会实例化a变量两次。

  • 一次在fromIntegral $ length $ show ma中作为()(如您所观察到的,这是一个默认值)
  • 一次在[mi, ma, le] :: [Int]中作为Int

如果您希望mima实际上是Int类型,请直接注释它们。

Prelude> :{
Prelude| let mi, ma :: Int
Prelude|     mi = minBound
Prelude|     ma = maxBound
Prelude|     le = fromIntegral $ length $ show ma
Prelude|  in [mi,ma,le]
Prelude| :}
[-9223372036854775808,9223372036854775807,19]

或在 GHCi 手动开启单态限制

Prelude> :set -XMonomorphismRestriction
Prelude> :{
Prelude| let mi = minBound
Prelude|     ma = maxBound
Prelude|     le = fromIntegral $ length $ show ma
Prelude| in [mi,ma,le] :: [Int]
Prelude| :}
[-9223372036854775808,9223372036854775807,19]

我认为这可能是由于MonomorphismRestriction引起的,但我尝试了-XNoMono... - 这已经是GHCi的默认设置,并且再次得到了错误的结果。 - epsilonhalbe
3
这是我在 Stack Overflow 上回答的仅有的一个问题,其中某人实际上想要使用 MonomorphismRestriction(与其他情况相反)。 :) - Alec
值得注意的是,这种情况正是为什么单态化限制通常是个好主意 - 除非你提供一个更一般化的签名或者你明确地有一个函数(即在 = 左边有参数),否则你可能不会期望你的 let 绑定是多态的。在这种情况下,由于这个原因,你不希望 mima 是多态的。 - Alec
这种混淆也可以通过MonoLocalBinds来避免,它防止了let泛化。这是同一个问题的两种不同的防混淆解决方案。 - Antal Spector-Zabusky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接