4

I found a weird feature of Haskell which made me believe I am thinking in the wrong way. I think in Haskell, there should be some "non-existent" monad. This is because of the following.

Prelude> return 1
1
Prelude> return 1 >>= \x -> if even x then return True else return False
False

>>= is of type m a -> (a -> m b) -> m b where m can be any monad. My theory is this: since return 1 evaluates to a mere 1, return 1 can be thought of as a 1 lifted to a value wrapped inside of a "non-existent" monad. Haskell captured this fact and evaluated the entire expression

return 1 >>= \x -> if even x then return True else return False

to False since it has to yield a "non-existent False" which is a mere False.

However, I have never heard about such "non-existent" monads before.

I am sure I am theorizing it in the wrong way, because if "non-existent 1" is just 1, the following expression must be type consistent, but it is not. It is a modified version of the above one.

1 >>= \x -> if even x then return True else return False

<interactive>:57:1: error:
    • Could not deduce (Integral a0)
      from the context: (Monad m, Integral a, Num (m a))
        bound by the inferred type for ‘it’:
                   forall (m :: * -> *) a. (Monad m, Integral a, Num (m a)) => m Bool
        at <interactive>:57:1-56
      The type variable ‘a0’ is ambiguous
    • In the ambiguity check for the inferred type for ‘it’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      When checking the inferred type
        it :: forall (m :: * -> *) a.
              (Monad m, Integral a, Num (m a)) =>
              m Bool

So, there must be a contradiction in my theory. What am I missing?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Namudon'tdie
  • 306
  • 1
  • 10

1 Answers1

14

return 1 isn't just 1. What you think is the "non-existent monad" is really IO. When you use return 1, GHCi is assuming that you mean IO, and then helpfully executing the IO expression and displaying the result. You get a type error with 1 >>= ... because 1 isn't a monad.

As @bradrn mentions in the comments, in general GHCi automatically runs any IO a value you give it, and then prints out the value it returns; if you give GHCi a non-IO type value, it will just print that value (if it can).

As an aside, there is a monad called Identity which essentially does work as a "non-existent monad" as you put it, in that return x is isomorphic to x no matter what you put in it. It doesn't get used automatically, though; you'd need to manually wrap and unwrap it to use it.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 8
    I think your answer explains another behavior that I observed: GHCI displays the same result for `return (Just 1)` and `Just 1 >>= return`. The former expression itself evaluates to `IO (Just 1)` and it is displayed as `Just 1` since it is wrapped with a `IO` monad. In the latter case the expression evaluated to `Just 1` and GHCI just printed the value out. – Namudon'tdie Sep 12 '19 at 03:02
  • 3
    That is correct @Namudon'tdie. In general, GHCi automatically runs any `IO a` value you give it, and then prints out the value it returns; if you give it a non-`IO` type, it will just print the result (if it can). – bradrn Sep 12 '19 at 03:10