使用`< =<`组合Monadic函数

时间:2014-08-29 01:35:09

标签: haskell

我正在尝试理解<=<函数:

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

根据我的理解,我给它2个函数和a,然后我会得到m c

那么,为什么这个例子不编译?

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

对于foo 3,我希望Just 3为结果。

但是我收到了这个错误:

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.

3 个答案:

答案 0 :(得分:12)

这里有两个错误。

首先,如果(<=<)共享同一个monad,Maybe只编写monadic函数。换句话说,您可以使用它来组成两个(<=<) :: (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c) 函数:

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

...或两个列表函数:

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

...但你不能编写一个列表函数,也许这样就可以运行了。原因是当你有这样的类型签名时:

m

...编译器将确保所有(f <=< g) x 必须匹配。

第二个错误是您忘记为您的作文加上括号。你可能想要的是:

f <=< (g x)

...如果省略括号,编译器会将其解释为:

Maybe

修复函数的一种简单方法就是定义一个将maybeToList :: Maybe a -> [a] maybeToList Nothing = [] maybeToList (Just a) = [a] 转换为列表的辅助函数:

maybeToList . return = return

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

此函数实际上具有以下两个不错的属性:

(maybeToList .)

...如果您将fmap视为与(<=<)类似,并将(.)视为类似于returnid,则属于仿函数法律(maybeToList . f <=< g) x

然后解决方案变为:

{{1}}

答案 1 :(得分:4)

请注意,在

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

m是静态的 - 您正试图在定义中将[]Maybe替换为m - 这不会输入检查。

您可以使用<=<撰写a -> m b形式的函数,其中m monad。请注意,您可以使用不同的类型参数,但不需要将其约束为多态a

以下是使用此模式约束到列表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"]

答案 2 :(得分:1)

你不能将monad混合在一起。当你看到签名

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

Monad m只是一个Monad,而不是两个不同的Monad。如果是,签名就像是

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

但实际情况并非如此,事实上一般来说实际上是不可能的。您可以执行类似

的操作
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