6

I have the following construct in my code:

f :: Maybe A -> X
f a = case a of
  Nothing -> x
  (Just b) -> case b of
    Nothing -> y
    (Just c) -> case c of
      Nothing -> z
      (Just d) -> d

I'm not seeing an obvious way to simplify this instead of using nested maybe functions, which wouldn't make the whole thing look much better. Are there any clever - but still understandable - tricks that would help make this construct more "elegant"?

Philip Kamenarsky
  • 2,757
  • 2
  • 24
  • 30
  • 2
    You need a better structure for the problem then nested `Maybe` – Ankur Oct 29 '13 at 11:30
  • It seems a little odd that you don't care about the values of b and c, just whether or not they have values. This makes them effectively Boolean. Is the layer of Maybes arbitrarily deep or a fixed depth? – itsbruce Oct 29 '13 at 12:35
  • Your model is basically wrong. You need to be chaining (i.e. mapping over) functions/functors, not doing this imperative style. – itsbruce Oct 29 '13 at 13:20
  • @itsbruce: What does that mean, in plain English? (Aka, what’s the translation function?) – Evi1M4chine Jan 04 '18 at 21:15
  • **The answers here are useless**, because they arrogantly imply, that you have merely a nested Maybe, and the given example is not just a simplified idea of a much more complex (set of) function(s) that returns Maybe values too, as is commeon. – Evi1M4chine Jan 04 '18 at 21:17

3 Answers3

13

Why did the code construct a Maybe (Maybe (Maybe X)) value in the first place? Unpacking such a value isn't nice, but the real question is, why there even is such a value. Maybe the code would better avoid all those nested Maybes.

If you really need to have such a value and need to do different things in all the Just/Nothing cases you'll have to write them all down. But instead of several nested case statements you could combine them into one big pattern match:

f Nothing                = x
f (Just Nothing))        = y
f (Just (Just Nothing))  = z 
f (Just (Just (Just d))) = d
sth
  • 222,467
  • 53
  • 283
  • 367
8

Despite your constraint about not using maybe, I think this looks quite nice:

f = maybe x (maybe y (maybe z id))

or even better, as @pat suggests in his comment:

f = maybe x . maybe y . maybe z $ id
nickie
  • 5,608
  • 2
  • 23
  • 37
4

UPDATED 2

Monad Either is for you

import Data.Maybe (maybe)

maybeE :: e -> Maybe a -> Either e a
maybeE e = maybe (Left e) Right

f :: Maybe (Maybe (Maybe d)) -> Either e d
f a  =   maybeE x a 
     >>= maybeE y
     >>= maybeE z

UPDATED 3

If we want to have not Either type, we could rewrite function:

import Data.Either(either)

either' = either id id

f :: Maybe (Maybe (Maybe d)) -> d
f a = either' $ maybeE x a 
            >>= maybeE y
            >>= maybeE z
viorior
  • 1,783
  • 11
  • 16
  • I'm not sure how the `Maybe` monad would help here, `a` has a type of `Maybe (Maybe (Maybe ...)))`, and after every `Maybe` layer I have to return a different value if it evaluates to `Nothing`, (`x y z` in the above example). – Philip Kamenarsky Oct 29 '13 at 10:48
  • This could be re-written so that f takes an arbitrary-size list of Eitherss as the single parameter. Would be better, I think. But that's a comment aimed at the OP, not you, viorior. – itsbruce Oct 29 '13 at 13:20
  • @itsbruce what would the type of `f` be if you allowed it to take an arbitrary-size list of `Either`s? Oh, you mean instead of the nested `Maybes`. – pat Oct 29 '13 at 15:50
  • Precisely! :) It should be `f :: [something] -> d` where `something` can probably just be functors to be mapped over, frankly. This kind of thing doesn't need more complexity than that. – itsbruce Oct 29 '13 at 16:07
  • I ended up using your `maybeE` combinator for my needs, but the other answers are very informative too! – Philip Kamenarsky Oct 30 '13 at 10:07
  • @PhilipK Just out of curiosity, what makes `maybeE x a >>= maybeE y >>= maybeE z` preferable over `maybe x . maybe y . maybe z $ id` to you? (I'm trying to improve some newbie Haskell material, so I appreciate every indication I can get about how people think.) – kqr Oct 31 '13 at 06:51
  • @kqr: as it turns out, I simplified the actual code way too much, if I had to use `maybe` it would look something like `maybe x (\a -> maybe y (maybe ...) (doSomethingWith a))`. In comparison, a monadic do block looks quite nice. – Philip Kamenarsky Oct 31 '13 at 12:39