I'm reading a revealing example of using a bind operator:
Just 5 >>= (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )
returns Just 6
.
I'm confused by the behaviour of fail
and its usefulness in the example.
When looking at the code I thought fail "zero"
may have a meaning:
- program never gets to that point
- laziness
- something else.
Then I realised that after a type cohesion, an exception becomes Nothing
(documented here). Still confusing for me that without type enforcement fail
is just an error in program.
Prelude> fail "zero" :: Maybe Int
Nothing
Prelude> fail "abc" :: [Int]
[]
Prelude> fail "zero"
*** Exception: user error (zero)
My question is about usefulness of fail "zero"
in this example.
Is it a proper reading (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )
attempts to be simple case for a -> Maybe a
function?
What prevents us from using (\ x -> if (x == 0) then Nothing else Just (x + 1) )
if we just needed an illustration of a -> Maybe a
?
I found this version below a much easier and shorter way to grasp same example.
Prelude> g x = if (x == 0) then Nothing else Just (x + 1)
Prelude> Just 0 >>= g
Nothing
Prelude> Just 1 >>= g
Just 2
Is it a proper reading (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )
attempts to be simple case for a -> Maybe a
function?
Yes.
What prevents us from using (\ x -> if (x == 0) then Nothing else Just (x + 1) )
if we just needed an illustration of a -> Maybe a
?
Nothing.
To elaborate on Daniel's entirely correct answer, fail
is a function from the MonadFail
typeclass. It was originally in the Monad
typeclass, but it didn't really belong there, as some Monad
s don't have a conception of failure. For those that do have a conception of failure, fail
has the type MonadFail m => String -> m a
. That is, for any type that implements the MonadFail
interface, and any type a
, fail
can take a string and produce an instance of that type paramaterized by a
.
For Maybe a
, fail s = Nothing
.
For [a]
, fail s = []
.
For IO a
, fail
raises an exception containing the string.
Many people write instances of MonadFail
for types like Either String a
, where fail s = Left s
. The main reason this is not part of the standard library is because Either
is a fairly abstract type, and although it's common to treat Right
as success and Left
as failure, it can be reasonably used as a Monad
with the semantics flipped. (For instance, if you want to try N things, and move on to the next thing if one thing fails, but take the first result, then you need to use Left
as the success case.)
Edit:
Also, when you write some code like
do
Just y <- getMaybe x
return y
That gets desugared to:
do
case getMaybe x of
Just y -> return y
_ -> fail "Failed pattern match"
This means that a failed pattern match will throw an exception in IO a
, but be an empty list in [a]
, and be a Nothing
in a Maybe a
.