在Haskell中组成两个错误提示功能

Composing two error-raising functions in Haskell

我所遇到的问题是这样的:

In a similar way to mapMaybe, define
the function:
composeMaybe :: (a->Maybe b) -> (b -> Maybe c) -> (a-> Maybe c)
which composes two error-raising functions.

类型Maybe a和函数mapMaybe的编码如下:

1
2
3
4
data Maybe a = Nothing | Just a

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

我尝试使用这样的合成:

1
composeMaybe f g = f.g

但是不会编译。

有人能指出我正确的方向吗?


您要寻找的工具已经存在。 Control.Monad中有两个Kleisli合成运算符。

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

当m = Maybe时,composeMaybe的实现变得显而易见:

1
composeMaybe = (>=>)

查看(>=>)

的定义

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

如果您想以自己的方式考虑,可以内联为

1
composeMaybe f g x = f x >>= g

或可以在do -sugar中写为:

1
2
3
composeMaybe f g x = do
    y <- f x
    g y

通常,我只会坚持使用(>=>),它具有存在的理论上很好的理由,因为它提供了陈述monad法则的最简洁的方法。


首先:如果应该为g.f,而不是f.g,因为您想要一个函数,该函数具有与f相同的参数,并具有与g相同的返回值。但是,这是行不通的,因为f的返回类型不等于g的参数类型(f的返回类型中包含Maybe,而g的参数类型却不)。

因此,您需要做的是:定义一个以Maybe b作为参数的函数。如果该参数为Nothing,则应返回Nothing。如果参数为Just b,则应返回g bcomposeMaybe应该返回带有f的函数的组成。


这是有关Haskell monad(尤其是在第一个示例中使用的Maybe monad)的出色教程。


1
2
3
4
composeMaybe :: (a -> Maybe b)
             -> (b -> Maybe c)
             -> (a -> Maybe c)
composeMaybe f g = \\x ->

由于g接受类型为b的参数,但是f产生类型为Maybe b的值,因此如果要将结果传递给f x,则必须对f x的结果进行模式匹配。 x8>。

1
2
3
                         case f x of
                              Nothing -> ...
                              Just y  -> ...


一元绑定操作符>>=已经存在一个非常相似的函数。其类型(对于Maybe monad)为Maybe a -> (a -> Maybe b) -> Maybe b,其用法如下:

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

它与您的composeMaybe函数并不完全相同,后者需要一个函数返回Maybe而不是其第一个参数的直接Maybe值。但是您可以使用此运算符非常简单地编写composeMaybe函数,"它几乎与普通compose函数的定义(.) f g x = f (g x)一样简单。


请注意composeMaybe的参数类型与monadic绑定运算符对其后一个参数想要的距离有多近:

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

fg的顺序对于组合而言是向后的,那么更好的名称呢?

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

给出以下定义

1
2
3
4
5
times3 x = Just $ x * 3

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

例如,

1
2
3
4
5
6
7
8
9
10
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