简体   繁体   English

用`<= <`编写Monadic函数

[英]Composing Monadic Functions with `<=<`

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 mc . 据我了解,我给它2个函数和a ,然后我会得到一个mc

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. 对于foo 3 ,我希望Just 3因此。

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.

There are two errors here. 这里有两个错误。

First, (<=<) only composes monadic functions if they share the same monad. 首先, (<=<)只有在共享同一个monad时才编写monadic函数。 In other words, you can use it to compose two Maybe functions: 换句话说,你可以用它来组成两个Maybe函数:

(<=<) :: (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 m s must match. ...编译器将确保所有m必须匹配。

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 Maybe s to lists: 修复函数的一种简单方法就是定义一个将Maybe s转换为列表的辅助函数:

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 . ...如果你将(maybeToList .)视为类似于fmap并将(<=<)视为类似于(.)return类似于id那么这些是(maybeToList .)函数法则。

Then the solution becomes: 然后解决方案变为:

(maybeToList . f <=< g) x

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. m是静态的 - 你试图在定义中用[]Maybe替换m - 这不会进行类型检查。

You can use <=< to compose functions of the form a -> mb where m is a single monad. 您可以使用<=<来组成形式a -> mb函数,其中m单个 monad。 Note that you can use different type arguments though, you don't need to be constrained to the polymorphic a . 请注意,您可以使用不同的类型参数,但不需要将其约束为多态a

Here's an example of using this pattern constrained to the list monad: 以下是使用此模式约束到列表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"]

You can't mix monads together. 你不能把monad混合在一起。 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. Monad m只有一个Monad,而不是两个不同的Monad。 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM