`fail “zero”` as a way to generate Nothing in simp

2019-08-26 12:03发布

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

2条回答
看我几分像从前
2楼-- · 2019-08-26 12:19

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.

查看更多
The star\"
3楼-- · 2019-08-26 12:19

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 Monads 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.

查看更多
登录 后发表回答