x * * y< $>在哈斯克尔的z

时间:2017-04-15 15:47:21

标签: haskell functor applicative

我正在尝试理解一些Haskell源代码,有时我遇到过这种结构:

x <*> y <$> z

e.g。

(+) <*> (+1) <$> a

有人可以向我解释这个结构吗?我知道它转换为fmap a (+ a + 1),但我无法建立连接

2 个答案:

答案 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<*>gf,同时还将初始输入直接传递给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