Composing Monadic Functions with `<=<`

耗尽温柔 提交于 2020-01-02 13:25:20

问题


I'm trying to understand the <=< function:

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

As I understand it, I give it 2 functions and an a, and then I'll get an m c.

So, why doesn't this example compile?

import Control.Monad

f :: a -> Maybe a
f = \x -> Just x

g :: a -> [a]
g = \x -> [x]

foo :: Monad m => a -> m c
foo x = f <=< g x

For foo 3, I would expect Just 3 as a result.

But I get this error:

File.hs:10:15:
    Couldn't match expected type `a0 -> Maybe c0'
                with actual type `[a]'
    In the return type of a call of `g'
    Probable cause: `g' is applied to too many arguments
    In the second argument of `(<=<)', namely `g x'
    In the expression: f <=< g x Failed, modules loaded: none.

回答1:


There are two errors here.

First, (<=<) only composes monadic functions if they share the same monad. In other words, you can use it to compose two Maybe functions:

(<=<) :: (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c)

... or two list functions:

(<=<) :: (b -> [c]) -> (a -> [b]) -> (a -> [c])

... but you cannot compose a list function and maybe function this way. The reason for this is that when you have a type signature like this:

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

... the compiler will ensure that all the ms must match.

The second error is that you forgot to parenthesize your composition. What you probably intended was this:

(f <=< g) x

... if you omit the parentheses the compiler interprets it like this:

f <=< (g x)

An easy way to fix your function is just to define a helper function that converts Maybes to lists:

maybeToList :: Maybe a -> [a]
maybeToList  Nothing = []
maybeToList (Just a) = [a]

This function actually has the following two nice properties:

maybeToList . return = return

maybeToList . (f <=< g) = (maybeToList . f) <=< (maybeToList . g)

... which are functor laws if you treat (maybeToList .) as analogous to fmap and treat (<=<) as analogous to (.) and return as analogous to id.

Then the solution becomes:

(maybeToList . f <=< g) x



回答2:


Note that, in

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

m is static -- You're trying to substitute both [] and Maybe for m in the definition -- that won't type check.

You can use <=< to compose functions of the form a -> m b where m is a single monad. Note that you can use different type arguments though, you don't need to be constrained to the polymorphic a.

Here's an example of using this pattern constrained to the list monad:

f :: Int -> [Int]
f x = [x, x^2]

g :: Int -> [String]
g 0 = []
g x = [show x]

λ> :t g <=< f
g <=< f :: Int -> [String]
λ> g <=< f $ 10
["10","100"]



回答3:


You can't mix monads together. When you see the signature

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

The Monad m is only a single Monad, not two different ones. If it were, the signature would be something like

(<=<) :: (Monad m1, Monad m2) => (b -> m2 c) -> (a -> m1 b) -> a -> m2 c

But this is not the case, and in fact would not really be possible in general. You can do something like

f :: Int -> Maybe Int
f 0 = Just 0
f _ = Nothing

g :: Int -> Maybe Int
g x = if even x then Just x else Nothing

h :: Int -> Maybe Int
h = f <=< g


来源:https://stackoverflow.com/questions/25560448/composing-monadic-functions-with

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!