6

我有以下代码:

import Control.Monad

coin :: MonadPlus m => m Int
coin = return 0 `mplus` return 1

如果我对coin :: Maybe Int解释器进行评估,它会优先级Just 0。这是正常的,因为将 Maybe 实现为 MonadPlus 的实例。

如果我对解释器进行评估coin :: [Int],它会打印[0, 1],因为mpluson list 的实现是append.

coin但是,如果我在没有任何类型装饰器的情况下评估0. 为什么?解释器“转换”什么类型coin来评估它?

此代码摘自:http ://homes.sice.indiana.edu/ccshan/rational/S0956796811000189a.pdf

4

2 回答 2

9

是的,这是 ghci 的一个没有很好记录的角落。当你在 ghci 中输入一个表达式时,它使用表达式的类型来决定做什么:

  1. IO (): 运行动作,什么也不做。
  2. Show a => IO a:运行动作和print结果。
  3. 任何其他IO a:运行动作,不做任何进一步的事情。
  4. 其他任何东西:用 . 包裹整个表达式print

它如何决定一个事物具有这些类型中的哪一种?简单:它尝试统一上述每个签名的表达式类型,并解决所有产生的约束。(对于行家来说:这是对扩展默认规则的补充!这解释了为什么它似乎默认了m,即使标准默认规则和扩展默认规则都没有说明要使用什么默认值。)

因此,由于您的表达式不与 统一,IO ()但与 统一Show a => IO a,ghci 在统一期间找到m ~ IO(and a ~ Int),发现有一个MonadPlus IO(and a Show Int) 实例来解决约束,运行您的操作并打印结果。

于 2018-10-28T13:28:18.037 回答
4

GHCi(但不是一般的 GHC)将在没有另外指定的签名的情况下,IO尽可能将多态类型构造函数专门化。IO依次执行提示处的操作并将其结果单子绑定到it变量,然后do { it <- action; print it }只要存在Show结果类型的实例(参见Daniel Wagner 的答案),就会打印(即)该变量。有关更多详细信息,请查看提示符处的 I/O 操作和用户指南的 it 变量部分。

在您的具体情况下,碰巧有一个MonadPlusIO. 您可以从中得到return 0,因为mplusforIO仅在第一个操作引发异常时才执行第二个操作。一个示范:

GHCi> readLn `mplus` readLn :: IO Integer
0
0
GHCi> readLn `mplus` readLn :: IO Integer
foo
1
1
GHCi> readLn `mplus` readLn :: IO Integer
foo
bar
*** Exception: user error (Prelude.readIO: no parse)
于 2018-10-28T13:24:58.183 回答