我想要类似J的fork功能。有没有办法实现这个功能?
这是所谓的应用风格(applicative style)。
h <$> f <*> g
使用Control.Applicative
中的<$>
和<*>
,将h
提升到(->) r
函子中:
liftA2 h f g
这背后的直觉是,如果
h :: a -> b -> c
-- then --
liftA2 h :: (r -> a) -> (r -> b) -> r -> c
所以提升的版本需要两个函数 r -> something
,而不是实际的 something
,然后提供一个 r
来从函数中获取 something
。
liftA*
和相应的 <$>
和 <*>
的组合是等价的。
虽然@kqr提供了更实用的解决方案,基于((->) a)
的Applicative
实例,但我们也可以用"管道式"的方法来谈论它。
+----- f ------+
/ \
<---- h +------< x
\ /
+----- g ------+
使用Control.Arrow
中的工具,可以提供一种非常组合式的无点编程方式。我们将使用一个在Haskell中常用的缺失函数diag
或dup
来创建此程序的最右边部分。
--+
\
+----- x dup :: x -> (x, x)
/ dup x = (x, x)
--+
然后使用Control.Arrow
中的(***)
组合器创建中间结果
----- f ----- (***) :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
f :: (a -> b)
g :: (c -> d)
----- g ----- f *** g :: (a, c) -> (b, d)
那么左侧正是uncurry
为我们所做的事情。
+-- uncurry :: (a -> b -> c) -> (a, b) -> c
/ h :: (a -> b -> c)
h uncurry h :: (a, b) -> c
\
+--
然后将它们全部连接在一起,我们可以用非常组合的方式删除所有的x
点。
m :: (a -> b -> c) -> (x -> a) -> (x -> b) -> x -> c
m h f g = uncurry h . (f *** g) . dup
+------ f ----+
/ \
<----- h +-----------< x (eta reduced)
\ /
+------ g ----+
liftA3 f g1 g2 g3
时,它的可扩展性如何? - kqrControl.Arrow
组合器 f &&& g = (f *** g) . dup
,然后我们有 liftA3 f g1 g2 g3 = uncurry ($) . first (uncurry f) . first (g1 &&& g2) . second g3 . dup
。呃。 - J. AbrahamsonliftA3
这种情况? - bheklilrliftM3
版本。它并不像我说的那么糟糕:liftM3 f g1 g2 g3 = uncurry ($) . (uncurry f . (g1 &&& g2)) &&& g3
。我们甚至可以在 Arrow (->)
实例中使用 apply :: uncurry ($)
来编写 apply . ((uncurry f . (g1 &&& g2)) &&& g3)
或者 apply . (liftA2 f g1 g2 &&& g3)
,这给我们提供了一个很好的递归关系,让我们生成 [liftAn | n <- [1..]]
。 - J. Abrahamson
pointfree
建议使用liftM*
的唯一原因不是因为在liftA*
出现之前就已经写好了吗?一旦applicative成为monad的超类,我们不是可以把liftM2
扔掉了吗? - kqrpointfree
在查找Control.Applicative
之前先查找了Control.Monad
。是否可能存在某些 Monad,其liftA*
的实现与liftM*
不同?我猜测行为应该是相同的,但由于 Applicatives 有时可以在 Monads 无法添加并行性的情况下添加并行性,因此我不会轻易放弃liftM*
。 - bheklilrMonoid e => Either e
可以使用一个整洁的Applicative
,但无法形成一个Monad
,但一旦 Applicative-Monad 提案出现,实例之间的差异将变得更加奇怪... - J. Abrahamson