<*> 是如何派生自 pure and (>>=) 的?
How does <*> derived from pure and (>>=)?
class Applicative f => Monad f where
return :: a -> f a
(>>=) :: f a -> (a -> f b) -> f b
(<*>)
可以从 pure and (>>=)
:
推导出来
fs <*> as =
fs >>= (\f -> as >>= (\a -> pure (f a)))
对于行
fs >>= (\f -> as >>= (\a -> pure (f a)))
我对 >>=
的用法感到困惑。我认为它需要一个函子 f a
和一个函数,然后 return 另一个函子 f b
。但是在这种表情中,我感到迷茫。
Applicative 是一个 Functor。 Monad 也是一个 Functor。我们可以看到 "Functorial" 值代表其“包含”的计算 ⁄ 产生 pure 值(如 IO a
,Maybe a
, [] a
, 等等), 作为各种计算的⁄隐喻的寓言。
函子描述 ⁄ 表示概念 ⁄ 计算类型,而函子值是具体化的计算,即“运行” ⁄ 解释 单独 步骤因此类似于著名的附加 间接 步骤,据称可以解决任何计算问题。
fs
和 as
都是您的函子值,bind((>>=)
,或 do
符号 <-
) “获取”函子中的进位值。 绑定 虽然属于 Monad。
我们可以在 Monad 中实现什么(使用 return
作为 pure
的同义词)
do { f <- fs ; -- fs >>= ( \ f -> -- fs :: F (a -> b) -- f :: a -> b
a <- as ; -- as >>= ( \ a -> -- as :: F a -- a :: a
return (f a) -- return (f a) ) ) -- f a :: b
} -- :: F b
( 或 MonadComprehensions,
[ f a | f <- fs, a <- as ]
),我们从 Applicative 的 <*>
中得到,它表达了相同的计算组合,但没有 Monad 的全部功能。 ,Applicative as
不依赖于那里的值 f
,由 fs
表示的计算“产生”。 Monadic Functors 允许这种依赖,
[ bar x y | x <- xs, y <- foo x ]
但 Applicative Functors 禁止这样做。
对于 Applicative,所有“计算”(如 fs
或 as
)必须“提前”知道;使用 Monad 它们可以被计算——纯粹地——基于前面“计算步骤”的结果(就像 foo x
正在做的:for (each) value x
computation xs
will produce, new computation foo x
will (purely) calculated, the computation that将依次产生(一些)y
(s)。
如果您想查看类型在 >>=
表达式中的对齐方式,这里是您的表达式及其子表达式的命名,因此可以用它们的类型进行注释,
exp = fs >>= g -- fs >>=
where g f = xs >>= h -- (\ f -> xs >>=
where h x = return (f x) -- ( \ x -> pure (f x) ) )
x :: a
f :: a -> b
f x :: b
return (f x) :: F b
h :: a -> F b -- (>>=) :: F a -> (a -> F b) -> F b
xs :: F a -- xs h
-- <-----
xs >>= h :: F b
g f :: F b
g :: (a -> b) -> F b -- (>>=) :: F (a->b) -> ((a->b) -> F b) -> F b
fs :: F (a -> b) -- fs g
-- <----------
fs >>= g :: F b
exp :: F b
两个 (>>=)
应用程序的类型适合:
(fs :: F (a -> b)) >>= (g :: (a -> b) -> F b)) :: F b
(xs :: F a ) >>= (h :: (a -> F b)) :: F b
因此,整体类型确实
foo :: F (a -> b) -> F a -> F b
foo fs xs = fs >>= g -- foo = (<*>)
where g f = xs >>= h
where h x = return (f x)
最后我们可以把monadic bind看作是do
的一个实现,对待do
表示法
do {
抽象地,公理地,由形式的行组成
a <- F a ;
b <- F b ;
......
n <- F n ;
return (foo a b .... n)
}
(其中 a
、F b
等表示相应 类型 的 值 ),例如它描述了 F t
类型的整体组合计算,其中 foo :: a -> b -> ... -> n -> t
。当 <-
右侧表达式的 none 不依赖于前面左侧的变量时,它本质上不是 Monadic,而只是这个 do
块的应用计算正在描述。
由于 Monad 法则,仅用两行 <-
就足以定义 do
块的含义。对于函子,只允许一行 <-
(fmap f xs = do { x <- xs; return (f x) }
).
因此,Functors/Applicative Functors/Monads ,嵌入式领域特定语言,因为计算描述本身就是[=95=我们语言的 ]values(do
符号中箭头右侧的值)。
最后,送你一个类型曼陀罗:
T a
T (a -> b)
(a -> T b)
-------------------
T (T b)
-------------------
T b
这包含三合一:
F a A a M a
a -> b A (a -> b) a -> M b
-------------- -------------- -----------------
F b A b M b
让我们从我们正在实现的类型开始:
(<*>) :: Monad f => f (a -> b) -> f a -> f b
(正常类型<*>
当然有Applicative
约束,但这里我们尝试用Monad
来实现Applicative
)
所以在fs <*> as = _
中,fs
是一个"f of functions"(f (a -> b)
),而as
是一个"f of a
s".
我们将从绑定开始 fs
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= _
如果你真的编译它,GHC 会告诉我们漏洞 (_
) 的类型:
foo.hs:4:12: warning: [-Wtyped-holes]
• Found hole: _ :: (a -> b) -> f b
Where: ‘a’, ‘f’, ‘b’ are rigid type variables bound by
the type signature for:
(Main.<*>) :: forall (f :: * -> *) a b.
Monad f =>
f (a -> b) -> f a -> f b
at foo.hs:2:1-45
有道理。 Monad 的 >>=
在左边接受一个 f a
,在右边接受一个函数 a -> f b
,所以通过在左边绑定一个 f (a -> b)
,右边的函数接收一个 (a -> b)
函数 "extracted" 来自 fs
。如果我们可以编写一个函数,可以将它用于 return 和 f b
,那么整个绑定表达式将 return f b
我们需要满足类型签名的 <*>
.
所以它看起来像:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> _)
我们在那里可以做什么?我们有f :: a -> b
,还有as :: f a
,我们需要做一个f b
。如果您习惯 Functor
,那很明显;只是 fmap f as
。 Monad
意味着 Functor
,所以这确实有效:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> fmap f as)
我认为, 更容易理解 Applicative
可以使用 Monad
中的工具实现的方式。
那么为什么您的示例使用另一个 >>=
和 pure
而不是 fmap
来编写?这有点让人想起 Monad
没有 而 有 Applicative
和 Functor
作为超类的日子。 Monad
总是 "morally" 暗示这两者(因为您可以仅使用 Monad
的功能实现 Applicative
和 Functor
),但是 Haskell 没有总是需要有这些实例,这导致书籍、教程、博客文章等解释 如何 仅使用 Monad
来实现这些。给出的示例行只是根据 >>=
和 pure
(return
)1.[= 内联 fmap
的定义91=]
我会像没有fmap
一样继续解压,这样就会导致你混淆的版本。
如果我们不打算使用 fmap
来组合 f :: a -> b
和 as :: f a
,那么我们需要绑定 as
以便我们有一个表达式属于 a
类型以将 f
应用于:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> _))
我们需要在那个洞里面做一个 f b
,我们有 f :: a -> b
和 a :: a
。 f a
给我们一个 b
,所以我们需要调用 pure
把它变成一个 f b
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> pure (f a)))
这就是这条线的作用。
- 绑定
fs :: f (a -> b)
以访问 f :: a -> b
- 在可以访问
f
的函数内部,它绑定 as
以访问 a :: a
- 在可以访问
a
的函数内部(它仍然在可以访问 f
的函数内部),调用 f a
来创建一个 b
,并对结果调用 pure
使其成为 f b
1 你可以使用>>=
和pure
实现fmap
作为fmap f xs = xs >>= (\x -> pure (f x))
,也就是fmap f xs = xs >>= pure . f
].希望您能看到示例的内部绑定只是内联第一个版本。
您可以根据 (>>=)
和 return
定义 (<*>)
,因为所有 monad 都是应用函子。您可以在 Functor-Applicative-Monad Proposal 中阅读更多相关信息。特别是,pure = return
和 (<*>) = ap
是在给定现有 Monad 定义的情况下实现应用定义的最短方法。
查看 (<*>)
、ap
和 (>>=)
的类型签名:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
ap :: Monad m => m (a -> b) -> m a -> m b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>)
和 ap
的类型签名几乎相同。由于 ap
是使用 do
表示法编写的,因此它等同于 (>>=)
的某些用法。我不确定这是否有帮助,但我发现 ap
的定义可读。这是一个重写:
ap m1 m2 = do { x1 <- m1; x2 <- m2; return (x1 x2) }
≡ ap m1 m2 = do
x1 <- m1
x2 <- m2
return (x1 x2)
≡ ap m1 m2 =
m1 >>= \x1 ->
m2 >>= \x2 ->
return (x1 x2)
≡ ap m1 m2 = m1 >>= \x1 -> m2 >>= \x2 -> return (x1 x2)
≡ ap mf ma = mf >>= (\f -> ma >>= (\a -> pure (f a)))
这是你的定义。您可以证明此定义支持 applicative functor laws,因为并非根据 (>>=)
和 return
定义的所有内容都如此。
class Applicative f => Monad f where
return :: a -> f a
(>>=) :: f a -> (a -> f b) -> f b
(<*>)
可以从 pure and (>>=)
:
fs <*> as =
fs >>= (\f -> as >>= (\a -> pure (f a)))
对于行
fs >>= (\f -> as >>= (\a -> pure (f a)))
我对 >>=
的用法感到困惑。我认为它需要一个函子 f a
和一个函数,然后 return 另一个函子 f b
。但是在这种表情中,我感到迷茫。
Applicative 是一个 Functor。 Monad 也是一个 Functor。我们可以看到 "Functorial" 值代表其“包含”的计算 ⁄ 产生 pure 值(如 IO a
,Maybe a
, [] a
, 等等), 作为各种计算的⁄隐喻的寓言。
函子描述 ⁄ 表示概念 ⁄ 计算类型,而函子值是具体化的计算,即“运行” ⁄ 解释 单独 步骤因此类似于著名的附加 间接 步骤,据称可以解决任何计算问题。
fs
和 as
都是您的函子值,bind((>>=)
,或 do
符号 <-
) “获取”函子中的进位值。 绑定 虽然属于 Monad。
我们可以在 Monad 中实现什么(使用 return
作为 pure
的同义词)
do { f <- fs ; -- fs >>= ( \ f -> -- fs :: F (a -> b) -- f :: a -> b
a <- as ; -- as >>= ( \ a -> -- as :: F a -- a :: a
return (f a) -- return (f a) ) ) -- f a :: b
} -- :: F b
( 或 MonadComprehensions,
[ f a | f <- fs, a <- as ]
),我们从 Applicative 的 <*>
中得到,它表达了相同的计算组合,但没有 Monad 的全部功能。 as
不依赖于那里的值 f
,由 fs
表示的计算“产生”。 Monadic Functors 允许这种依赖,
[ bar x y | x <- xs, y <- foo x ]
但 Applicative Functors 禁止这样做。
对于 Applicative,所有“计算”(如 fs
或 as
)必须“提前”知道;使用 Monad 它们可以被计算——纯粹地——基于前面“计算步骤”的结果(就像 foo x
正在做的:for (each) value x
computation xs
will produce, new computation foo x
will (purely) calculated, the computation that将依次产生(一些)y
(s)。
如果您想查看类型在 >>=
表达式中的对齐方式,这里是您的表达式及其子表达式的命名,因此可以用它们的类型进行注释,
exp = fs >>= g -- fs >>=
where g f = xs >>= h -- (\ f -> xs >>=
where h x = return (f x) -- ( \ x -> pure (f x) ) )
x :: a
f :: a -> b
f x :: b
return (f x) :: F b
h :: a -> F b -- (>>=) :: F a -> (a -> F b) -> F b
xs :: F a -- xs h
-- <-----
xs >>= h :: F b
g f :: F b
g :: (a -> b) -> F b -- (>>=) :: F (a->b) -> ((a->b) -> F b) -> F b
fs :: F (a -> b) -- fs g
-- <----------
fs >>= g :: F b
exp :: F b
两个 (>>=)
应用程序的类型适合:
(fs :: F (a -> b)) >>= (g :: (a -> b) -> F b)) :: F b
(xs :: F a ) >>= (h :: (a -> F b)) :: F b
因此,整体类型确实
foo :: F (a -> b) -> F a -> F b
foo fs xs = fs >>= g -- foo = (<*>)
where g f = xs >>= h
where h x = return (f x)
最后我们可以把monadic bind看作是do
的一个实现,对待do
表示法
do {
抽象地,公理地,由形式的行组成
a <- F a ;
b <- F b ;
......
n <- F n ;
return (foo a b .... n)
}
(其中 a
、F b
等表示相应 类型 的 值 ),例如它描述了 F t
类型的整体组合计算,其中 foo :: a -> b -> ... -> n -> t
。当 <-
右侧表达式的 none 不依赖于前面左侧的变量时,它本质上不是 Monadic,而只是这个 do
块的应用计算正在描述。
由于 Monad 法则,仅用两行 <-
就足以定义 do
块的含义。对于函子,只允许一行 <-
(fmap f xs = do { x <- xs; return (f x) }
).
因此,Functors/Applicative Functors/Monads do
符号中箭头右侧的值)。
最后,送你一个类型曼陀罗:
T a
T (a -> b)
(a -> T b)
-------------------
T (T b)
-------------------
T b
这包含三合一:
F a A a M a
a -> b A (a -> b) a -> M b
-------------- -------------- -----------------
F b A b M b
让我们从我们正在实现的类型开始:
(<*>) :: Monad f => f (a -> b) -> f a -> f b
(正常类型<*>
当然有Applicative
约束,但这里我们尝试用Monad
来实现Applicative
)
所以在fs <*> as = _
中,fs
是一个"f of functions"(f (a -> b)
),而as
是一个"f of a
s".
我们将从绑定开始 fs
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= _
如果你真的编译它,GHC 会告诉我们漏洞 (_
) 的类型:
foo.hs:4:12: warning: [-Wtyped-holes]
• Found hole: _ :: (a -> b) -> f b
Where: ‘a’, ‘f’, ‘b’ are rigid type variables bound by
the type signature for:
(Main.<*>) :: forall (f :: * -> *) a b.
Monad f =>
f (a -> b) -> f a -> f b
at foo.hs:2:1-45
有道理。 Monad 的 >>=
在左边接受一个 f a
,在右边接受一个函数 a -> f b
,所以通过在左边绑定一个 f (a -> b)
,右边的函数接收一个 (a -> b)
函数 "extracted" 来自 fs
。如果我们可以编写一个函数,可以将它用于 return 和 f b
,那么整个绑定表达式将 return f b
我们需要满足类型签名的 <*>
.
所以它看起来像:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> _)
我们在那里可以做什么?我们有f :: a -> b
,还有as :: f a
,我们需要做一个f b
。如果您习惯 Functor
,那很明显;只是 fmap f as
。 Monad
意味着 Functor
,所以这确实有效:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> fmap f as)
我认为, 更容易理解 Applicative
可以使用 Monad
中的工具实现的方式。
那么为什么您的示例使用另一个 >>=
和 pure
而不是 fmap
来编写?这有点让人想起 Monad
没有 而 有 Applicative
和 Functor
作为超类的日子。 Monad
总是 "morally" 暗示这两者(因为您可以仅使用 Monad
的功能实现 Applicative
和 Functor
),但是 Haskell 没有总是需要有这些实例,这导致书籍、教程、博客文章等解释 如何 仅使用 Monad
来实现这些。给出的示例行只是根据 >>=
和 pure
(return
)1.[= 内联 fmap
的定义91=]
我会像没有fmap
一样继续解压,这样就会导致你混淆的版本。
如果我们不打算使用 fmap
来组合 f :: a -> b
和 as :: f a
,那么我们需要绑定 as
以便我们有一个表达式属于 a
类型以将 f
应用于:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> _))
我们需要在那个洞里面做一个 f b
,我们有 f :: a -> b
和 a :: a
。 f a
给我们一个 b
,所以我们需要调用 pure
把它变成一个 f b
:
(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
= fs >>= (\f -> as >>= (\a -> pure (f a)))
这就是这条线的作用。
- 绑定
fs :: f (a -> b)
以访问f :: a -> b
- 在可以访问
f
的函数内部,它绑定as
以访问a :: a
- 在可以访问
a
的函数内部(它仍然在可以访问f
的函数内部),调用f a
来创建一个b
,并对结果调用pure
使其成为f b
1 你可以使用>>=
和pure
实现fmap
作为fmap f xs = xs >>= (\x -> pure (f x))
,也就是fmap f xs = xs >>= pure . f
].希望您能看到示例的内部绑定只是内联第一个版本。
您可以根据 (>>=)
和 return
定义 (<*>)
,因为所有 monad 都是应用函子。您可以在 Functor-Applicative-Monad Proposal 中阅读更多相关信息。特别是,pure = return
和 (<*>) = ap
是在给定现有 Monad 定义的情况下实现应用定义的最短方法。
查看 (<*>)
、ap
和 (>>=)
的类型签名:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
ap :: Monad m => m (a -> b) -> m a -> m b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>)
和 ap
的类型签名几乎相同。由于 ap
是使用 do
表示法编写的,因此它等同于 (>>=)
的某些用法。我不确定这是否有帮助,但我发现 ap
的定义可读。这是一个重写:
ap m1 m2 = do { x1 <- m1; x2 <- m2; return (x1 x2) }
≡ ap m1 m2 = do
x1 <- m1
x2 <- m2
return (x1 x2)
≡ ap m1 m2 =
m1 >>= \x1 ->
m2 >>= \x2 ->
return (x1 x2)
≡ ap m1 m2 = m1 >>= \x1 -> m2 >>= \x2 -> return (x1 x2)
≡ ap mf ma = mf >>= (\f -> ma >>= (\a -> pure (f a)))
这是你的定义。您可以证明此定义支持 applicative functor laws,因为并非根据 (>>=)
和 return
定义的所有内容都如此。