`ZipList`

```(<*>) :: [a -> b] -> [a] -> [b]
```

... 也就是说, `(<*>)` 将一列表的函数应用到另一个列表上, 但详细情况是怎样的呢?

```Prelude> [(2*),(5*),(9*)] <*> [1,4,7]
[2,8,14,5,20,35,9,36,63]
```

```Prelude> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
Prelude> zipWith (\$) [(2*),(5*),(9*)] [1,4,7]
[2,20,63]
```

```newtype ZipList a = ZipList { getZipList :: [a] }
```

```instance Applicative ZipList where
(ZipList fs) <*> (ZipList xs) = ZipList (zipWith (\$) fs xs)
pure x                        = undefined -- TODO
```

```pure id <*> v = v
```

`(<*>)` 展开, 并假定我们使用上面的 `pure` 实现, 我们得到:

```ZipList [id] <*> ZipList xs = ZipList xs
ZipList (zipWith (\$) [id] xs) = ZipList xs
```

```ZipList (zipWith (\$) [id] [1..]) = ZipList [1..]
ZipList [1] = ZipList [1..]
[1] = [1..] -- 显然不等!
```

```instance Applicative ZipList where
(ZipList fs) <*> (ZipList xs) = ZipList (zipWith (\$) fs xs)
pure x                        = ZipList (repeat x)
```

```>>> import Control.Applicative
>>> ZipList [(2*),(5*),(9*)] <*> ZipList [1,4,7]
ZipList {getZipList = [2,20,63]}
>>> (,,) <\$> ZipList [1,4,9] <*> ZipList [2,8,1] <*> ZipList [0,0,9]
ZipList {getZipList = [(1,2,0),(4,8,0),(9,1,9)]}
>>> liftA3 (,,) (ZipList [1,4,9]) (ZipList [2,8,1]) (ZipList [0,0,9])
ZipList {getZipList = [(1,2,0),(4,8,0),(9,1,9)]}
```

副作用序列化

```Prelude> [(2*),(3*)] <*> [4,5]

--- ...

[8,10,12,15]
```

```liftA2 f u v = liftA2 (flip f) v u -- 可交换性
```

```f <\$> u <*> v = flip f <\$> v <*> u
```

```(*>) :: Applicative f => f a -> f b -> f b
```

`(*>)` 在将副作用合并的同时只保留右边参数的值. 它和 monad 的 `(>>)` 等价. 这是它的一个 `Maybe` 上的使用案例, 前者满足交换律:

```Prelude> Just 2 *> Just 3
Just 3
Prelude> Just 3 *> Just 2
Just 2
Prelude> Just 2 *> Nothing
Nothing
Prelude> Nothing *> Just 2
Nothing
```

```Prelude> (print "foo" *> pure 2) *> (print "bar" *> pure 3)
"foo"
"bar"
3
Prelude> (print "bar" *> pure 3) *> (print "foo" *> pure 2)
"bar"
"foo"
2
```

Haskell 采用了一种 `(<*>)` 和其它 Applicative 函数的实现惯例: 从左到右序列化 (即左边的额外信息优先). 尽管这种惯例能够澄清一些不清晰的地方, 有时它也意味着外表未必可信. 比如说 `(<*)` 函数并不等价于 `flip (*>)`, 因为和 `(*>)` 一样, `(<*)` 优先取左侧的副作用:

```Prelude> (print "foo" *> pure 2) <* (print "bar" *> pure 3)
"foo"
"bar"
2
```

```>>> [(2*),(3*)] <*> [4,5]
[8,10,12,15]
>>> [4,5] <**> [(2*),(3*)]
[8,12,10,15]
```

```newtype Backwards f a = Backwards { forwards :: f a }
```
```>>> Backwards [(2*),(3*)] <*> Backwards [4,5]
Backwards [8,12,10,15]
```

1. 为列表 functor 实现 `(<*>)` 和它的"副作用倒置版本"
`(<|*|>) :: Applicative f => f (a -> b) -> f a -> f b`, 且不使用任何来自 `Applicative` 或者 `Monad` 的函数.
2. 使用 do 代码块重写 `Monad` 的交换律, 不使用 `ap``liftM2`.
3. 以下的 `Applicative` 满足交换律吗?
a. `ZipList`
b. `((->) r)`
c. `State s` (使用这个定义: `newtype State s a = State { runState :: s -> (a, s) }`. 提示: 或许练习2会很有用.)
4. `[2,7,8] *> [3,9]` 的结果是什么? (在动手前先猜一下.)
5. 使用其它 `Applicative` 实现 `(<**>)`.
6. 正如我们所见, 有些 functor 可以有两种副作用序列化方向不同的 `(<*>)` 实现. 为什么 `(>>=)` 没有这个问题呢?

兵器谱

`Functor`, `Applicative`, `Monad`. 它们是紧密相连的, 同时也是 Haskell 中最重要的三个类型类. 虽然我们已经见过实际使用中 `Functor``Monad` 的许多例子, 甚至还见过一些 `Applicative` 的, 我们还没有把它们平行比较过. 如果我们暂且忽略 `pure`/`return`, 这三个类型类的代表函数分别为:

```fmap :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
```

```(<\$>) :: Functor t     =>   (a -> b) -> (t a -> t b)
(<*>) :: Applicative t => t (a -> b) -> (t a -> t b)
(=<<) :: Monad t       => (a -> t b) -> (t a -> t b)
```

• `fmap` 的域是任意函数.
• `(<*>)` 的域是形如 `t (a -> b)` 的函数.
• `(=<<)` 的域是形如 `a -> t b` 的函数.

`Functor`, `Applicative``Monad` 的本质不同在于它们的类型赋予我们的能力. 从 `fmap``(<*>)` 再到 `(>>=)`, 我们对于值的控制力和计算的灵活性逐渐增加, 但这是以结果性质的弱化为代价的. 我们将逐条浏览这个 "Haskell 兵器谱". 下文中, 我们分别使用额外信息来指代这些 `Functor` 中包裹的值和除了这个值以外其携带的信息.

`fmap` 的类型保证了不管用什么函数我们都无法改变值的额外信息. 在 `(a -> b) -> t a -> t b` 这个类型中, `(a -> b)` 函数和 `t a` 的额外信息 `t` (某种意义上, 非正式地, 我们用 `t` 表示 `t a` 的额外信息) 完全没有联系, 因此这个函数无法对额外信息造成任何影响. 由此可得, 操作列表 `xs` 的函数 `fmap f xs` 不会改变列表中元素的个数/列表的结构/列表携带的额外信息.

```Prelude> fmap (2*) [2,5,6]
[4,10,12]
```

```Prelude> [(2*),(3*)] <*> [2,5,6]
[4,10,12,6,15,18]
```

`t a` 结合的 `t (a -> b)` 自带了一个额外的信息. `(<*>)` 有着一个更微妙的限制. 尽管 `t (a -> b)` 本身携带了额外信息, 在其中的函数 `(a -> b)` 仍然不能改变外界 `Applicative` 的额外信息. 这意味着, `(<*>)` 对额外信息的改动仅仅是由额外信息本身所决定的, 在 `Applicative` 中的值不能对这个改动造成任何影响.

```Prelude> (print "foo" *> pure (2*)) <*> (print "bar" *> pure 3)
"foo"
"bar"
6
Prelude> (print "foo" *> pure 2) *> (print "bar" *> pure 3)
"foo"
"bar"
3
Prelude> (print "foo" *> pure undefined) *> (print "bar" *> pure 3)
"foo"
"bar"
3
```

`Monad` 却大为不同. `(>>=)` 接收一个 `a -> t b` 函数, 因此它能够从值中构造额外信息. 这意味着我们掌握了更多的灵活性:

```Prelude> [1,2,5] >>= \x -> replicate x x
[1,2,2,5,5,5,5,5]
Prelude> [0,0,0] >>= \x -> replicate x x
[]
Prelude> return 3 >>= \x -> print \$ if x < 10 then "太小了" else "没问题"
"太小了"
Prelude> return 42 >>= \x -> print \$ if x < 10 then "太小了" else "没问题"
"没问题"
```

`data AT a = L a | B (AT a) (AT a)`

1. `AT` 实现 `Functor`, `Applicative``Monad` 的 instance. 不要使用诸如 `pure = return` 这样的偷懒写法. `Applicative``Monad` 的 instance 应该相互匹配; 注意, `(<*>)` 应该和 `ap` 等价, 虽然后者是由 `Monad` instance 衍生出来的.
2. 使用 `Applicative``Monad` 实现下列函数; 若它们都无法提供相应的能力, 就两个都不用. 若 `Applicative``Monad` 都能够完成任务, 选择能力最小的那一个. 简要说明你选择的理由.
a. `fructify :: AT a -> AT a`, 其将树中的所有叶子替换成一个左右子树都等于该叶子的分枝.
b. `prune :: a -> (a -> Bool) -> AT a -> AT a`, `prune z p t` 表示, 若 `t` 左右子树中的某一个是叶子, 且满足 `p` 时, 将 `t` 替换成 `L z`.
c. `reproduce :: (a -> b) -> (a -> b) -> AT a -> AT b`, `reproduce f g t` 返回一棵根节点的左右子树分别为将 `f``g` 应用到 `t` 得到的值的树.
3. `AT` 可以有另一个 `Applicative` instance (不是指将副作用反向的版本). 试着实现它. 提示: 这个版本能够用来实现:
`sagittalMap :: (a -> b) -> (a -> b) -> AT a -> AT b`
若其参数是一个分枝, 则将两个函数分别应用到它的左右子树上去.
(你或许会感兴趣, "AT" 表示 "apple tree (苹果树)". 若读者们是植物学家, 还请谅解这个糟糕的隐喻.)

`Applicative` 的 monoid 形式

```class Functor f => Monoidal f where
unit  :: f ()
(*&*) :: f a -> f b -> f (a,b)
```

`Applicative` 法则和下列 `Monoidal` 法则等价:

```fmap snd \$ unit *&* v = v                    -- 左单位元
fmap fst \$ u *&* unit = u                    -- 右单位元
fmap asl \$ u *&* (v *&* w) = (u *&* v) *&* w -- 结合律
-- asl (x, (y, z)) = ((x, y), z)
```

`(\$)` 左边函数的作用只是在等价的类型之间进行转换, 例如 `b``((), b)`. 如果我们忽视这种转换, 这些法则就比 `Applicative` 的形式更加清晰了. 顺便一提, 如同 `Applicative` 一样, 有一条保证成立的额外推论.

```fmap (g *** h) (u *&* v) = fmap g u *&* fmap h v -- 自然性
-- g *** h = \(x, y) -> (g x, h y)
```

1. 使用 `pure``(<*>)` 实现 `unit``(*&*)`, 反过来也做一遍.
2. 使用 `Monoidal` 的函数描述满足交换律的 Applicative.
3. 为以下类型实现 `Monoidal` instance:
a. `ZipList`
b. `((->) r)`

 Applicative类型类 习题解答 高级Haskell Monoid类型类  >> Applicative类型类  >> 箭头  >> 理解箭头  >> Foldable类型类  >> Traversable类型类  >> 延续过渡风格（CPS）  >> 可变对象  >> 拉链  >> 适用函子  >> 独异点  >> Lens 和引用  >> 并行 编辑此章节 Haskell 库参考 >> 普通实务 >> 特殊任务 编辑书目结构
1. 这里类型签名表面上的相似有着更深的理论意义. 其中之一就是, 这三个类型类都有着恒等法则 (identity law) 和结合律法则并不是偶然.
2. 译注: 事实上, `IO` 远比这里描述的要复杂得多 (或者说, "运用了许多黑魔法"). 对本句有疑问的读者请不用深究.
3. 例如 [1] 中的脚注.