如何在 Haskell 中使用 (=<<) 编写 (<*>)
How to write (<*>) using (=<<) in Haskell
有人可以解释为什么 (<*>)
和 (=<<)
的以下表示有效:
f <*> a = (\f' -> return . f' =<< a) =<< f
我觉得这是一种刻意写成的迟钝方式。给你这个代码的人是想惹你生气。下面是 ap
的通常定义,简洁易懂:
ap f a = do
f' <- f
a' <- a
return (f' a')
我们可以 运行 通过通常的 do
脱糖转换,将 <-
替换为 >>=
:
ap f a =
f >>= \f' ->
a >>= \a' ->
return (f' a')
现在,注意最里面的项是\a' -> return (f' a')
,可以写成return . f'
。
ap f a =
f >>= \f' ->
a >>= return . f'
那么,由于(=<<) = flip (>>=)
,我们可以通过交换参数将>>=
替换为=<<
:
ap f a = f >>= (\f' -> return . f' =<< a) -- reverse inner bind
ap f a = (\f' -> return . f' =<< a) =<< f -- reverse the other bind
给你。
名称 f
和 a
的使用非常令人困惑,因为名称具有暗示性("f" 让人想起 "function",但事实并非如此)。素数名称也可能令人困惑。当您仍在学习并且不确定默认运算符优先级时,不应省略括号。 do
的可选大括号和分号也是如此。
这两个名字应该是fs
和as
。更改它们可能是预期答案的一部分。因此你有
fs <*> as = (\f -> (return . f) =<< as) =<< fs
= fs >>= (\f ->
as >>= (return . f)) -- by definition of >>= and =<<
= fs >>= (\f ->
as >>= (\a -> (return . f) a)) -- by "eta expansion"
= fs >>= (\f ->
as >>= (\a -> return (f a))) -- by `(.)` reduction
= do { f <- fs
; a <- as
; return (f a) } -- by `do` syntax definition
因为这就是 do
语法的定义方式。而那个 是 the definition of ap
,<*>
应该是它的替代品。
不言而喻,这仅适用于不仅是 Applicative,而且还是 Monad 的类型。
有了monad comprehensions,最后一个写成
= [f a | f <- fs, a <- as]
这本身就很清楚了。
有人可以解释为什么 (<*>)
和 (=<<)
的以下表示有效:
f <*> a = (\f' -> return . f' =<< a) =<< f
我觉得这是一种刻意写成的迟钝方式。给你这个代码的人是想惹你生气。下面是 ap
的通常定义,简洁易懂:
ap f a = do
f' <- f
a' <- a
return (f' a')
我们可以 运行 通过通常的 do
脱糖转换,将 <-
替换为 >>=
:
ap f a =
f >>= \f' ->
a >>= \a' ->
return (f' a')
现在,注意最里面的项是\a' -> return (f' a')
,可以写成return . f'
。
ap f a =
f >>= \f' ->
a >>= return . f'
那么,由于(=<<) = flip (>>=)
,我们可以通过交换参数将>>=
替换为=<<
:
ap f a = f >>= (\f' -> return . f' =<< a) -- reverse inner bind
ap f a = (\f' -> return . f' =<< a) =<< f -- reverse the other bind
给你。
名称 f
和 a
的使用非常令人困惑,因为名称具有暗示性("f" 让人想起 "function",但事实并非如此)。素数名称也可能令人困惑。当您仍在学习并且不确定默认运算符优先级时,不应省略括号。 do
的可选大括号和分号也是如此。
这两个名字应该是fs
和as
。更改它们可能是预期答案的一部分。因此你有
fs <*> as = (\f -> (return . f) =<< as) =<< fs
= fs >>= (\f ->
as >>= (return . f)) -- by definition of >>= and =<<
= fs >>= (\f ->
as >>= (\a -> (return . f) a)) -- by "eta expansion"
= fs >>= (\f ->
as >>= (\a -> return (f a))) -- by `(.)` reduction
= do { f <- fs
; a <- as
; return (f a) } -- by `do` syntax definition
因为这就是 do
语法的定义方式。而那个 是 the definition of ap
,<*>
应该是它的替代品。
不言而喻,这仅适用于不仅是 Applicative,而且还是 Monad 的类型。
有了monad comprehensions,最后一个写成
= [f a | f <- fs, a <- as]
这本身就很清楚了。