3

I have two functions:

f :: a -> Maybe a
g :: a -> a

I want to create such function:

h :: a -> Maybe a

h x
| isJust(f x) = Just (g $ fromJust(f x))
| otherwise   = Nothing

How can I do it in more elegant way?

Savenkov Alexey
  • 678
  • 4
  • 11
  • 7
    The elegant way was posted below. Still, I want to remind that using `isJust/fromJust` is arguably the least elegant way. Indeed, if one forgets the `isJust` check, `fromJust` can crash your program. A better way would be to use pattern matching instead, e.g. `case f x of Nothing -> Nothing ; Just y -> Just $ g y`. Also see [boolean blindness](https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/) for more info. – chi Dec 25 '15 at 23:00
  • 1
    That isn't type correct. `g (fromJust (f x)) : a`. I think you ment `Just (g (fromJust (f x))`. – Thomas M. DuBuisson Dec 26 '15 at 00:02

3 Answers3

12

Since you've tagged this question with :

h :: a -> Maybe a
h = fmap g . f

For an explanation:

f            ::                          a -> Maybe a
g            ::        a ->       a
fmap g       ::  Maybe a -> Maybe a
(.)          :: (Maybe a -> Maybe a) -> (a -> Maybe a) -> (a -> Maybe a)
(.) (fmap g) ::                         (a -> Maybe a) -> (a -> Maybe a)
fmap g . f   ::                                           (a -> Maybe a)
h            ::                                            a -> Maybe a

Note that (.)'s and fmap g's types are actually more general:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
-- b in this case is Maybe a
-- c in this case is Maybe a

fmap g :: Functor f => f a -> f a
-- f in this case is Maybe

However, you could also pattern match on the result of f:

h x = 
  case f x of
    Just k -> Just (g k)
    _      -> Nothing

Note that your original example wouldn't even compile, since g's return type isn't correct.

Zeta
  • 103,620
  • 13
  • 194
  • 236
  • I think this is the most idiomatic way, and expresses the idea the most clearly of all options. – dfeuer Dec 26 '15 at 00:43
5

Having

fmap2 :: (Functor g, Functor f) => (a -> b) -> g (f a) -> g (f b)
fmap2 = fmap . fmap

here is a funny way:

h :: a -> Maybe a
h = fmap2 g f

fmap2 g f ~> fmap (fmap g) f ~> fmap g . f ~> \x -> fmap g (f x)

The Functor ((->) r) instance is used here: fmap can be used instead of (.).

Community
  • 1
  • 1
effectfully
  • 12,325
  • 2
  • 17
  • 40
  • Given that the user is (probably) new to Haskell, would you add some explanation? Not everyone is familiar with `Functor ((->) r)`. Using `b ~ a`, `f a ~ Maybe a` and `g c ~ a -> c` would make this a lot clearer for them. – Zeta Dec 26 '15 at 17:27
  • @Zeta, I wanted to make it look like a puzzle. – effectfully Dec 26 '15 at 17:50
  • so, in `fmap . fmap` the left `fmap` is in `(a ->)`, because `fmap g` is always a function, whatever the functor. the right `fmap` here happens to be in `Maybe`. (and in owl combinator, it's in (r ->) too. nice.) – Will Ness Dec 28 '15 at 22:49
4

Why not simply this:

h :: a -> Maybe a
h x = fmap g (f x)

Or the operator version:

h :: a -> Maybe a
h x = g <$> f x
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Sibi
  • 47,472
  • 16
  • 95
  • 163