【问题标题】:GHCi ignores type signatureGHCi 忽略类型签名
【发布时间】:2015-03-22 10:38:02
【问题描述】:
Prelude> let myprint = putStrLn . show
Prelude> :t myprint
myprint :: () -> IO ()

好的,这里没什么不寻常的。只是 GHCi 类型的默认规则,我猜...

Prelude> let myprint = (putStrLn . show) :: Show x => x -> IO ()
Prelude> :t myprint
myprint :: () -> IO ()

这是什么魔法??你直接忽略我的类型声明?! O_O

有什么方法可以说服 GHCi 做我真正想做的事吗?

【问题讨论】:

  • 疯狂猜测:可怕的单态限制。
  • @Jubobs 这也是我的猜测 - 除了显式类型签名没有禁用它吗?
  • 您使用的是旧版本的 GHC 吗?我在 7.8.3 上,我得到了你通常期望的类型签名 (Show a => a -> IO ()),即使没有使用签名表达式。获取() -> IO () 似乎是 GHC 的一个实际问题。
  • 在 GHCi 7.8.1 中默认关闭了单态限制(仅适用于 GHCi),请参阅downloads.haskell.org/~ghc/7.8.1/docs/html/users_guide/…
  • @MvanGeest 是因为SO问题的数量吗? ;-)

标签: haskell ghci


【解决方案1】:

表达式添加类型注释

e :: type

让编译器检查e 是否具有type,并使用type 来驱动类型变量实例化和实例选择。 然而,如果type 是多态的,它仍然可以在以后实例化。考虑例如

(id :: a -> a) "hello"

在上面,a 将被实例化为 String,尽管有我的注释。此外,

foo :: Int -> Int
foo = (id :: a -> a)

将使a 稍后实例化为Int。上面的id 注释没有给GHC 任何信息:它已经知道id 有那个类型。 我们可以在完全不影响类型检查的情况下删除它。也就是说,表达式idid :: a->a 不仅动态等价,而且静态等价。

同样的,表达式

putStrLn . show

(putStrLn . show) :: Show x => x -> IO ()

是静态等价的:我们只是用 GHC 可以推断的类型注释代码。换句话说,我们不会向 GHC 提供它不知道的任何信息。

注解类型检查后,GHC 可以进一步实例化x。在您的示例中,单态性限制就是这样做的。为防止这种情况发生,请为您正在引入的 binding 使用注释,而不是 表达式

myprint :: Show x => x -> IO ()
myprint = (putStrLn . show)

【讨论】:

    【解决方案2】:

    我们可以做以下事情,但有单态性限制:

    >let myprint :: Show x => x -> IO (); myprint = putStrLn . show
    >:t myprint
    myprint :: Show x => x -> IO ()
    

    这与let myprint = putStrLn . show :: Show x => x -> IO () 不同。在前一种情况下,我们有一个带有类型签名的绑定,在后一种情况下,我们在右侧有一个带有类型注释的let 绑定。单态性检查顶级类型签名,但不检查本地注释。

    【讨论】:

    • 我总是推荐 :set +m 标志,它可以让你在 GHCi 中进行多行输入。虽然我希望 GHCi 具有像 IPython 这样的多行输入的智能缩进(不过 Jupyter 项目正在努力解决这个问题),但它对于这样的实例真的很方便
    猜你喜欢
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 2017-05-19
    • 2015-01-09
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    相关资源
    最近更新 更多