How does the Haskell compiler "know" that IO cannot be unwrapped?

Just and Nothing are data constructors for the type Maybe a. IO has no data constructors to speak of (in GHC it actually has constructors but they're really implementation details of GHC, and other implementations might define IO differently).

unwrapIO (IO str) = str doesn't make sense in the same way unwrapMaybe (Maybe str) = str doesn't make sense. IO and Maybe are not data constructors, so you cannot pattern-match on them.


It's because the data constructor of IO is not exported. I mean, you can think it's not exported.

You can prevent your own type from being unwrapped by using the same strategy.

module Test (Test, test) where

data Test a = MkTest a

test :: a -> Test a
test = MkTest

You can create a value of Test using test, but you cannot unwrap it using pattern-match because MkTest is not exported.