我正在尝试理解一些Haskell源代码,有时我遇到过这种结构:
x <*> y <$> z
e.g。
(+) <*> (+1) <$> a
有人可以向我解释这个结构吗?我知道它转换为fmap a (+ a + 1)
,但我无法建立连接
答案 0 :(得分:4)
让我们从:
开始x <*> y <$> z
添加括号,它变为:
(x <*> y) <$> z
鉴于(<$>) :: Functor f => (a -> b) -> f a -> f b
,我们有:
x <*> y :: a -> b
z :: Functor f => f a
鉴于(<*>) :: Applicative g => g (c -> d) -> g c -> g d
,我们有:
x :: Applicative g => g (c -> d)
y :: Applicative g => g c
x <*> y :: Applicative g => g d
结合最后几个结果,我们得到:
g d ~ a -> b
g ~ (->) a
d ~ b
x :: a -> c -> b
y :: a -> c
x <*> y :: a -> b
因此:
(\x y z -> x <*> y <$> z) :: Functor f => (a -> c -> b) -> (a -> c) -> f a -> f b
现在知道正在使用函数实例中的(<*>)
,我们也可以替换它的定义:
x <*> y <$> z
(\r -> x r (y r)) <$> z
在您的示例中,x = (+)
,y = (+1)
和z = a
,我们得到......
(\r -> r + (r + 1)) <$> a
...将a
中的每个值添加到自身加一:
GHCi> (+) <*> (+1) <$> [0..3]
[1,3,5,7]
GHCi> ((+) <*> (+1) <$> (*5)) 2
21
答案 1 :(得分:3)
因此,在x <*> y <$> z
中,即fmap (x<*>y) z
,您将函数x<*>y
应用于函数值z
。 <*>
实际上对fmapping一无所知 - 两个运算符在完全独立的仿函数上工作!这是在这里实现的第一件重要的事情。
接下来是,如果x<*>y
的结果是函数,则Applicative
的{{1}}实例实际上是function functor。我希望人们停止使用它,因为它确实是一个更令人困惑的实例,而且通常不是最好的抽象选择。
具体而言,<*>
只是一种巧妙的方式来组合函数f<*>g
和f
,同时还将初始输入直接传递给g
。它起作用:
f
即
(<*>) :: (f ~ (x->))
=> f (a -> b) -> f a -> f b
就数据流而言,这是一项操作:
(<*>) :: (x ->(a -> b)) -> (x -> a) -> (x -> b)
≡ (x -> a -> b) -> (x -> a) -> x -> b
(f <*> g) x = f x $ g x
我希望用arrow combinators来表达这一点:
────┬─────▶ f ──▶
│ │
└─▶ g ──┘
所以 ┌───id──┐
────&&& uncurry f ──▶
└─▶ g ──┘
。当然,这不是紧凑的,事实上比明确的lambda版本f<*>g ≡ id &&& g >>> uncurry f
更冗长,坦率地说它可能是最好的。然而,箭头版本是这三个版本中最常见的版本,并且可以说是表达最佳状态。它如此冗长的主要原因是,在这里讨价还价并不合适;我们可以定义一个运算符
\x -> f x $ g x
然后
(≻>>) :: (x->(a,b)) -> (a->b->c) -> x -> c
g≻>>h = uncurry h . g
例如,我们有
x <*> y <$> z
≡ fmap (id &&& y ≻>> x) z
≡ fmap (\ξ -> x ξ $ y ξ) z
我首先误解了你的问题。模式 (+) <*> (+1) <$> a
≡ fmap (id &&& (+1) ≻>> (+)) z
≡ fmap (\x -> 1 + x+1) z
≡ fmap (+2) z
<$>
比您的<*>
<*>
更为常见,以下内容可能对其他人有用。
<$>
也可以写成f <$> y <*> z
,而liftA2
对于IMO而言比相应的liftA2 f y z
更容易理解。
<*>
它的作用是,它在值上采用组合器函数,并从中生成容器上的组合器函数。除了列表实例之外,它与liftA2 :: (a -> b -> c) -> f a -> f b -> f c
有点类似,它不仅将zipWith
列表中的每个元素与 {{1}中的相应元素组合在一起} list,但将a
列表中的每个元素与一起列入b
列表中的所有元素,并将结果连接起来。
a