这是kleisli组成的共同实现:
kleisli :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
kleisli = \f g x -> f x >>= g为什么它不期望在一元上下文中有一个值呢?我肯定有个很好的理由。我只是没看出来。
kleisli' :: Monad m => (a -> m b) -> (b -> m c) -> m a -> m c
kleisli' = \f g x -> x >>= f >>= g该类型看起来更好的可组合性,如果我们在调用站点上只有一个纯值,则可以使用return。
发布于 2020-04-24 18:26:58
Kleisli作文实际上是回答常见问题的最简单的方法之一:单曲对什么有用?
对于普通的函数,我们能做的最有用的事情之一就是组合它们。给定f :: a -> b和g :: b -> c,我们可以先执行f,然后对结果执行g,给出g . f :: a -> c。
只要我们只需要使用“普通”功能,那就太棒了。但是,一旦我们在“现实世界”中开始编程,我们可能会遇到这样的情况:如果我们希望我们的语言保持纯正和引用性透明,我们就无法继续使用这些函数。事实上,在这种情况下,其他语言没有Haskell那么有原则,而放弃了任何纯粹的伪装。考虑一下这些日常情况:
f有时可能无法返回值。在许多其他语言中,这将通过返回null来表示,但不能将其输入到g中。(当然,您可以通过调整g来处理null输入,但这很快就会重复。)在Haskell中,我们没有null,我们有Maybe类型的构造函数来显式地表示可能没有值。这意味着f需要有a -> Maybe b类型。出于同样的原因,g将具有b -> Maybe c类型。但是在这样做时,我们失去了组合这两个函数的能力,因为我们不能直接将Maybe b类型的值提供给期望输入b类型的值。
f的结果可能取决于某些副作用(例如来自用户的输入或数据库查询的结果)。这在不纯语言中是没有问题的,但是在Haskell中,为了保持纯度,我们必须以a -> IO b类型的函数的形式来实现这一点。同样,g将以相同的形式b -> IO c结束,我们已经失去了天真地组合这两个函数的能力。我相信你能看出这是怎么回事。在这两种情况下(以及更多的情况下,每个单体都可以提供一种),我们不得不将a -> b类型的简单函数替换为a -> m b类型的函数,以便解释特定类型的“副作用”--或者,如果您愿意的话,可以使用某种特定类型的“上下文”,它适用于函数结果。这样我们就失去了组成两种功能的能力,这是我们在“无副作用”世界中所拥有的。
真正的目的是克服这一点,让我们为这种“不纯的功能”恢复一种构图形式。当然,这正是Kleisli组合给我们的,a -> m b形式的函数的组合,它完全满足我们期望的函数组合的性质(即结合性,以及每个类型上的“恒等函数”,这里是return :: a -> m a)。
您提出的(a -> m b) -> (b -> m c) -> (m a -> m c)类型的“不完全组合”的建议是没有用的,因为产生的函数通常需要一个一元值作为输入,而在实践中产生一元值的主要方式是_output_s。您仍然可以在需要时做到这一点,只需采用“适当的”Kleisli组合,并通过>>=将一元值提供给它。
发布于 2020-04-24 17:55:48
从a到b的Kleisli箭头被定义为函数a -> m b。让我们不再使用a ~> b (保留假定的m )。组成这两支箭是什么意思?它应该有这样的类型:
(<=<) :: (b ~> c) -> (a ~> b) -> (a ~> c)现在,如果我们扩大这个范围:
(<=<) :: (b -> m c) -> (a -> m b) -> (a -> m c)然后你就有了。看起来你看的是翻转版的(>=>),但它的想法是一样的。
这些操作符是在Control.Monad中定义的。
标准库中也有一个更正式的Kleisli箭定义。
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }它附带了一个Category实例,该实例将此组合实现为(.)运算符(但您必须使用newtype包装)。
https://stackoverflow.com/questions/61413948
复制相似问题