[英]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.