开发者

Composing two error-raising functions in Haskell

开发者 https://www.devze.com 2022-12-16 15:13 出处:网络
The problem I have been given says this: In a similar way to mapMaybe, def开发者_运维知识库ine

The problem I have been given says this:

In a similar way to mapMaybe, def开发者_运维知识库ine the function: composeMaybe :: (a->Maybe b) -> (b -> Maybe c) -> (a-> Maybe c) which composes two error-raising functions.

The type Maybe a and the function mapMaybe are coded like this:

data Maybe a = Nothing | Just a

mapMaybe g Nothing = Nothing
mapMaybe g (Just x) = Just (g x)

I tried using composition like this:

composeMaybe f g = f.g

But it does not compile.

Could anyone point me in the right direction?


The tool you are looking for already exists. There are two Kleisli composition operators in Control.Monad.

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c

When m = Maybe, the implementation of composeMaybe becomes apparent:

composeMaybe = (>=>)

Looking at the definition of (>=>),

f >=> g     = \x -> f x >>= g

which you can inline if you want to think about it in your own terms as

composeMaybe f g x = f x >>= g

or which could be written in do-sugar as:

composeMaybe f g x = do 
    y <- f x
    g y

In general, I'd just stick to using (>=>), which has nice theoretical reasons for existing, because it provides the cleanest way to state the monad laws.


First of all: if anything it should be g.f, not f.g because you want a function which takes the same argument as f and gives the same return value as g. However that doesn't work because the return type of f does not equal the argument type of g (the return type of f has a Maybe in it and the argument type of g does not).

So what you need to do is: Define a function which takes a Maybe b as an argument. If that argument is Nothing, it should return Nothing. If the argument is Just b, it should return g b. composeMaybe should return the composition of the function with f.


Here is an excellent tutorial about Haskell monads (and especially the Maybe monad, which is used in the first examples).


composeMaybe :: (a -> Maybe b)
             -> (b -> Maybe c)
             -> (a -> Maybe c)
composeMaybe f g = \x ->

Since g takes an argument of type b, but f produces a value of type Maybe b, you have to pattern match on the result of f x if you want to pass that result to g.

                         case f x of
                              Nothing -> ...
                              Just y  -> ...


A very similar function already exists — the monadic bind operator, >>=. Its type (for the Maybe monad) is Maybe a -> (a -> Maybe b) -> Maybe b, and it's used like this:

Just 100 >>= \n -> Just (show n) -- gives Just "100"

It's not exactly the same as your composeMaybe function, which takes a function returning a Maybe instead of a direct Maybe value for its first argument. But you can write your composeMaybe function very simply with this operator — it's almost as simple as the definition of the normal compose function, (.) f g x = f (g x).


Notice how close the types of composeMaybe's arguments are to what the monadic bind operator wants for its latter argument:

ghci> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

The order of f and g is backward for composition, so how about a better name?

thenMaybe :: (a -> Maybe b) -> (b -> Maybe c) -> (a -> Maybe c) 
thenMaybe f g = (>>= g) . (>>= f) . return

Given the following definitions

times3 x = Just $ x * 3

saferecip x
  | x == 0 = Nothing
  | otherwise = Just $ 1 / x

one can, for example,

ghci> saferecip `thenMaybe` times3 $ 4
Just 0.75
ghci> saferecip `thenMaybe` times3 $ 8
Just 0.375
ghci> saferecip `thenMaybe` times3 $ 0
Nothing
ghci> times3 `thenMaybe` saferecip $ 0
Nothing
ghci> times3 `thenMaybe` saferecip $ 1
Just 0.3333333333333333
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号