Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform a -> a -> Maybe a to take any combination of a's and Maybe a's

Tags:

haskell

I have a function f of type Integral => a -> a -> Maybe a. This means that

f 1 2

is valid code, but natural compositions like

f (f 3 4) 5
f 6 (f 7 8)
f (f 9 10) (f 11 12)

are not valid.

There are ways to make it work. For example,

f 6 =<< f 7 8

is the nicest way I know for case 2. For case 3,

join $ f <$> f 9 10 <*> f 11 12

is the best I can think of, but the extra join sticks out like a sore thumb. I don't have a good idea for case 1. Besides that, what bothers me is that the syntax in what I came up with is not "consistent" - I'm accomplishing essentially the same thing, but the syntax is very dissimilar.

Is there a standard/idiomatic construction which allows me to convert f to work as Maybe a -> a -> Maybe a, a -> Maybe a -> Maybe a, and Maybe a -> Maybe a -> Maybe a, with nice and consistent syntax?

like image 753
echinodermata Avatar asked Oct 23 '14 02:10

echinodermata


1 Answers

Original Solution

Let's create a utility function for the first case:

lift :: (a -> b -> Maybe c) -> Maybe a -> b -> Maybe c
lift _ Nothing  _ = Nothing
lift f (Just a) b = f a b

Case 1:

lift f (f 3 4) 5

Case 2:

f 6 =<< f 7 8

Case 3:

lift f (f 9 10) =<< f 11 12

Although it looks more consistent, yet in my humble opinion it still looks ugly. Perhaps somebody else could provide a better solution.

Recommended Solution

Edit: After thinking about my solution I realized that it can be generalized:

(<&>) :: (Applicative m, Monad m) => m (a -> m b) -> m a -> m b
f <&> a = join $ f <*> a
infixl 4 <&>

Case 1:

f <$> f 3 4 <&> pure 5

Case 2:

f <$> pure 6 <&> f 7 8

Case 3:

f <$> f 9 10 <&> f 11 12

This works for functions of an arbitrary arity:

f <$> a <&> b             -- f :: a -> a -> Maybe a

f <$> a <*> b <&> c       -- f :: a -> a -> a -> Maybe a

f <$> a <*> b <*> c <&> d -- f :: a -> a -> a -> a -> Maybe a

And so on....

Alternate Solution

Edit: I agree with @n.m. The best solution would be to change the type signature of your function to Maybe a -> Maybe a -> Maybe a and use Just wherever necessary.

Case 1:

f (f (Just 3) (Just 4)) (Just 5)

Case 2:

f (Just 6) (f (Just 7) (Just 8))

Case 3:

f (f (Just 9) (Just 10)) (f (Just 11) (Just 12))

The simplest solutions are always the best.

Edit: Actually, in retrospect my previous solution was much simpler.

like image 62
Aadit M Shah Avatar answered Sep 27 '22 20:09

Aadit M Shah