ghci - 在交互模式下急切的编译?

Eri*_*ikR 5 haskell ghci

以下程序类型检查我是否在命令行上指定它(例如ghci file.hs):

import Data.Ratio
foo = let x = [1..]
          y = (1%2) + (head x)
       in y
Run Code Online (Sandbox Code Playgroud)

但是,如果我以交互方式输入它,我将收到类型错误:

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'
Run Code Online (Sandbox Code Playgroud)

它似乎x正在急切地打字,[Integer]而不是更普遍的(Num t, Enum t) => [t].

我能做些什么吗?是否存在交互模式与批处理模式不同的其他情况?

ham*_*mar 10

没有参数的绑定,即形式的绑定x = ...单形态限制的约束,这意味着GHC将尝试使用可用的任何类型信息使其成为非多态的,并且返回类型默认以解决任何歧义.(实际上,GHCi使用了一组稍微宽松的默认规则,但这对于这个问题并不重要).

由于在编写时没有其他类型信息可用let x = [1..],因此键入默认会导致类型被推断为[Integer],因为Integer它是默认的数字类型.

有几种方法可以解决这个问题:

  1. 使用类型签名.这总是有效,但在处理复杂类型时有时会很乏味.

    > let x = [1..] :: [Rational]
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用参数写出绑定.这不适用于您的情况,但您在编写无点函数定义时有时会看到此问题.

    > let f = (+)
    > :t f
    f :: Integer -> Integer -> Integer
    > let f x y = x + y
    > :t f
    f :: Num a => a -> a -> a
    
    Run Code Online (Sandbox Code Playgroud)
  3. 为类型检查器提供更多信息.在您的情况下,我们可以通过在一个let语句中编写两个绑定来避免此问题.然后GHC可以使用来自第二个绑定的类型信息来正确地推断x应该具有该类型[Rational].

    > let x = [1..]; y = 1%2 + head x
    > :t x
    x :: [Ratio Integer]
    
    Run Code Online (Sandbox Code Playgroud)
  4. 禁用单态限制.如果您期望某种类型的东西是例如a Integer,而实际上是a Num a => a,则可能会产生严重的性能影响,因为后者必须在每次共享时重新计算.这是限制存在的主要原因.

    但是,在解释器中,这通常不是问题,因此便利性通常是值得的.

    > :set -XNoMonomorphismRestriction
    > let x = [1..]
    > :t x
    x :: (Num t, Enum t) => [t]
    
    Run Code Online (Sandbox Code Playgroud)

    如果您希望在默认情况下启用此功能,则可以将其添加到.ghci文件中.