繁体   English   中英

Haskell中的x <*> y <$> z

[英]x <*> y <$> z in Haskell

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

x <*> y <$> z

例如

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

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

让我们从:

x <*> y <$> z

添加括号,它变为:

(x <*> y) <$> z

鉴于(<$>) :: Functor f => (a -> b) -> fa -> fb ,我们有:

x <*> y :: a -> b
z :: Functor f => f a

鉴于(<*>) :: Applicative g => g (c -> d) -> gc -> gd ,我们有:

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

因此,在x <*> y <$> z ,即fmap (x<*>y) z ,您将函数x<*>y应用于functor-value z <*>实际上对fmapping一无所知 - 两个运算符在完全独立的仿函数上工作! 这是在这里实现的第一件重要事情。

接下来是,如果x<*>y的结果是函数,那么<*>Applicative实例实际上是函数函子 我希望人们会停止使用它,因为它确实是一个更令人困惑的实例,通常不是最好的抽象选择。

具体地说, f<*>g只是组成函数fg一种巧妙方式,同时也将初始输入直接传递给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

就数据流而言,就是这个操作:

────┬─────▶ f ──▶
    │       │
    └─▶ g ──┘

我宁愿用箭头组合器表达这个:

     ┌───id──┐
────&&&     uncurry f ──▶
     └─▶ g ──┘

所以f<*>g ≡ id &&& g >>> uncurry f 当然,这不是那么紧凑,事实上比明确的lambda版本更简洁\\x -> fx $ gx ,坦率地说这可能是最好的。 然而,箭头版本是这三个版本中最常见的版本,并且可以说是表达最佳状态。 它如此冗长的主要原因是,currying在这里并不合适; 我们可以定义一个运算符

(≻>>) :: (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 fyz ,而liftA2比同等的<*>更容易理解。

liftA2 :: (a -> b -> c) -> f a -> f b -> f c

它的作用是,它在值上采用组合器函数,并从中生成容器上的组合器函数。 它类似于一个位zipWith除了列表实例,它不仅结合了每个元素a与在相应的元素列表b列表中,但在结合每个元素a清单所有要素一次b名单,并连接结果。

Prelude> Control.Applicative.liftA2 (+) [0,10..30] [0..3]
[0,1,2,3,10,11,12,13,20,21,22,23,30,31,32,33]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM