我正在阅读一些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教程中记得,有一种方法可以使用仿函数或应用仿函数来构造计算树,最终可以提供参数以从“仿函数框”中获取结果。但是我似乎无法在教程中找到该示例。我如何“巧妙地”重写上述内容?
答案 0 :(得分:5)
使用Applicative实例进行功能:
parent :: Sheep -> Maybe Sheep
parent = mplus <$> mother <*> father
答案 1 :(得分:4)
你可以把它写成
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)
或者为什么我偷了哈马尔的好答案:
现在,(->) 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
不是一个观点!)