我正在阅读一些Haskell教程,试图熟悉这门语言。我在Monad/MonadPlus教程中见过这个例子:
data Sheep = Sheep {name :: String, mother :: Maybe Sheep, father :: Maybe Sheep}
parent :: Sheep -> Maybe Sheep
parent s = mother s `mplus` father s
我试图用无点样式重写它(只是作为练习,不是说上面是错误的或不习惯的),但是我卡住了:显然我可以写一个自定义函数
partialPlus :: (MonadPlus m) => (a -> m b) -> (a -> m b) -> a -> m b
partialPlus f1 f2 = x -> f1 x `mplus` f2 x
,然后使用
parent = partialPlus mother father
,但我似乎记得从LYAH教程中,有一种方法可以使用函子或应用函子来构建计算树,最终可以提供参数以从"函子盒"中获得结果。然而,我似乎找不到教程中的例子。我如何"巧妙地"重写上面的内容?
为函数使用application实例:
parent :: Sheep -> Maybe Sheep
parent = mplus <$> mother <*> father
非常无点
可以写成
partialPlus :: MonadPlus m => (t -> m a) -> (t -> m a) -> t -> m a
partialPlus = liftM2 mplus
这是因为(->) t
的monad实例(您可以将其视为(t ->)
)。
更详细的类型:
liftM2 :: Monad func_t => (a -> b -> c) -> func_t a -> func_t b -> func_t c
给它一个极具偏见的名字func_t
来暗示函数从t到…
然后
liftM2 mplus :: (Monad func_t, MonadPlus m) => func_t (m a) -> func_t (m a) -> func_t (m a)
但是为什么使用Monad当你可以有更好的应用程序?
或者为什么我偷了hammar的好答案:
现在,有一个适用于(->) t
的实例,所以我们不妨写成
partialPlus = liftA2 mplus
,其工作原理完全相同。但这是个好消息,因为如果你可以使用liftA2
或liftA3
等,你也可以使用Control.Applicative
中的<$>
和<*>
。
一般来说,如果你有
this = do
x <- mx
y <- my
z <- mz
return (f x y z)
这是可表达的liftM3 f mx my mz
,但更漂亮的f <$> mx <*> my <*> mz
,特别是如果mx
和my
等实际上是更复杂的表达,因为我通常发现。<*>
和<*>
具有低优先级(4),因此大多数时候不需要括号。
在本例中,我们可以输入
partialPlus f1 f2 = mplus <$> f1 <*> f2
是无点的。(…只要你认为t -> m a
不是一个点!)