What is the default type evaluation of MonadPlus in Haskell?

Yeah, this is a not super-well documented corner of ghci. When you enter an expression into ghci, it uses the expression's type to decide what to do:

  1. IO (): Run the action, do nothing further.
  2. Show a => IO a: Run the action and print the result.
  3. any other IO a: Run the action, do nothing further.
  4. anything else: wrap the whole expression with print.

How does it decide which of these types a thing has? Easy: it tries to unify the type of your expression which each of the above signatures in turn and solve all resulting constraints. (For the cognoscenti: this is in addition to the extended defaulting rules! This explains why it appears to be defaulting the m, even though neither the standard defaulting rules nor the extended defaulting rules say what default to use.)

So, since your expression does not unify with IO () but does unify with Show a => IO a, ghci finds m ~ IO (and a ~ Int) during unification, discovers that there is a MonadPlus IO (and a Show Int) instance to resolve the constraints, runs your action, and prints the result.


GHCi (but not GHC in general) will, in the absence of a signature specifying otherwise, specialise polymorphic type constructors to IO whenever possible. IO actions at the prompt, in turn, are executed and have their results monadically bound to the it variable, which is then printed (i.e. do { it <- action; print it }) as long as there is a Show instance for the result type (cf. Daniel Wagner's answer). For more details, have a look at the I/O actions at the prompt and The it variable sections of the User's Guide.

In your specific case, it happens that there is a MonadPlus instance for IO. You get return 0 from it because mplus for IO only executes the second action if the first one throws an exception. One demonstration:

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)