为什么我可以在没有显式或隐式定义的情况下使用 `>>=`?

Why can I use `>>=` without an explicit or implicit definition?

我定义了一个类型 X

newtype X i o = X { runX :: Int -> i -> IO o }

我已经用

创建了 FunctorApplicativeMonad 的实例
instance Functor (X i) where
  fmap f a = X $ \ i o -> liftA f $ runX a i o

instance Applicative (X i) where
  pure x  = X $ \ _ _ -> return x
  a <*> b = X $ \ i o -> liftA2 (<*>) (runX a i o) (runX b i o)

instance Monad (X i) where
  return = pure
  a >> b = X $ \ i o -> runX a i o >> runX b i o

您可能会说,到目前为止,我一直无法为 >>= 提出一个定义,因此将其排除在外。我预计这会在编译时出错,但实际上,它所做的只是发出警告。好吧,所以它不会检查 class 的所有方法是否都已定义,但我确实不能 use >>=。不对,又错了。令我非常惊讶的是,GHCi 愉快地计算 let x = pure 5 >>= pureControl.Monad 没有导出 >>= 的默认定义,我当然没有定义,这怎么可能?

根据您更正的定义,如果我尝试定义然后使用 x,我会得到预期的运行时异常:

λ> let x = pure 5 >>= pure :: X Int Int
λ> runX x 5 5
*** Exception: foo.hs:12:10-20: No instance nor default method for class operation GHC.Base.>>=

您看不到的原因可能有两个。

首先是您只是 运行 let 但从未尝试评估结果。因为 Haskell 是惰性的,所以 let x = ... 实际上什么都不做。 x 只会在您实际尝试使用它时(例如,runX)进行评估,所以那是您遇到错误的时候。

另一种可能是您使用了 let 而未指定类型:

λ> let x = pure 5 >>= pure
λ> x
5

在这里,x 在它使用的 monad m 中是多态的。要打印对像这样的多态术语有用的东西,ghci 默认 mIO,它可以正常工作并给你 5,但不会告诉你任何关于你的自定义 monad 的有用信息。