什么时候应该使用 Kleisli? [英] When should one use a Kleisli?

查看:23
本文介绍了什么时候应该使用 Kleisli?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近偶然发现了 Kleisli 的概念,我阅读的每个教程/链接/参考资料都通过以下结构激发了 Kleisli 的使用:

I recently stumbled on the concept of a Kleisli and every tutorial/link/reference that I read motivates the use of Kleisli via the following constructs:

  1. 组合返回 monad 的函数:f: a ->m[b]g: b ->m[c] - 我认为 monad 的 定义 已经捕捉到了这种情况 - do/bind/for/flatMap 做到了这一点.无需依靠 Kleisli 构造来实现这一目标.所以这不可能是 Kleisli IMO 的主要"用例.
  2. 插入配置:这表明如果多个对象(类型、案例/数据类等)需要Config 注入 然后可以使用 Kleisli 构造来抽象出可重复注入.有很多方法可以实现这一点(例如在 Scala 中使用 implicits),可能不需要调用 Kleisli.同样,IMO 这并不是一个主要"用例.
  3. Monad Transformers:我对此没有深入的理解,但这是我的解释:如果您需要组合 monad",您需要一个构造允许您参数化 monad 本身.例如,M1[M2[M1[M2[a]]]] 可以转换为 [M1[M2[a]]]可以(我可能是错的)跨越一元边界扁平化以与 a ->M3[b](比如说).对于这个可以我们可以使用 Kleisli 三元组并调用该构造,因为如果您要从头开始,您可以重新发明 Kleisli. 似乎是证明使用 Kleisli 合理的一个很好的候选者.这是正确的吗?
  1. Composing functions that return monads: f: a -> m[b] with g: b -> m[c] - I think the very definition of a monad already captures this case - do/bind/for/flatMap do that. One needn't lean on the Kleisli construct to achieve this. So this cannot be the "primary" use case of a Kleisli IMO.
  2. Inserting configuration: This one states that if multiple objects (types, case/data classes etc.,) need to have a Config injected then a Kleisli construct can be used to abstract away the repeatable injection. There are numerous ways of achieving this (for example with implicits in Scala) that invoking a Kleisli may not be necessary. Again, IMO this doesn't stand out as a "primary" use case.
  3. Monad Transformers: I don't have a solid understanding of this but here's my interpretation: If you have the need of "composing monads" you need a construct that allows you to parameterize the monads themselves. For example M1[M2[M1[M2[a]]]] could be transformed to [M1[M2[a]]] which could (I may be wrong) be flattened across monadic boundaries to be composable with an a -> M3[b] (say). For this one could us a Kleisli triple and invoke the construct since if you were to do it from scratch you may just reinvent the Kleisli. This seems to be a good candidate for justifying the use of a Kleisli. Is this correct?

我相信上面的 #1-#2 是二次使用".也就是说,如果您碰巧使用了 Kleisli 构造,您可以获得用于组合返回 monad 和配置注入的函数的模式.但是,它们不能激发问题鼓吹 Kleislis 的力量.

I believe #1-#2 above are "secondary uses". That is, if you do happen to use the Kleisli construct, you can also get patterns for composing functions that return monads as well as config injection. However, they cannot be motivating problems advocating the power of Kleislis.

在使用最不强大的抽象来解决手头问题的假设下,哪些激励问题可以用来展示它们的用途?

Under the assumption of using the least powerful abstraction to solve the problem at hand, what motivating problems can be used to showcase their use?

替代论点:我完全有可能完全错了,我对 Kleislis 的理解是不正确的.我缺乏必要的范畴论背景,但 可能是 Kleisli 是一个正交结构,可以用来代替 monad 并且它们 (Kleisli) 是一个范畴论我们通过它查看函数世界的问题的镜头(即,Klesli 简单地包装了一个一元函数 a -> M[b],现在我们可以在更高的抽象层次上工作,其中函数是操作的对象与使用的对象).因此,Kleisli 的使用可以简单地理解为Functional Programming with Kleisli".如果这是真的,那么应该会出现这样一种情况,即 Kleisli 可以比现有结构更好地解决问题,我们又回到了激励机制的问题上.问题.同样有可能的是,如果问题本身只是一个镜头,它为同一问题提供不同的解决方案,那么它本身就没有这样的激励性问题.是哪个?

Alternate Thesis: It's entirely possible that I am totally wrong and my understanding of Kleislis is incorrect. I lack the necessary category theory background, but it could be that a Kleisli is an orthogonal construct that can be used in place of monads and they (Kleisli) are a category theoretic lens through which we view the problems of the functional world (i.e., a Klesli simply wraps a monadic function a -> M[b] and now we can work at a higher level of abstraction where the function is the object of manipulation vs an object of usage). Thus, the use of Kleisli can be simply understood to be "Functional Programming with Kleisli". If this is true, then there ought to be a situation where a Kleisli can solve a problem better than existing constructs and we circle back to the issue of a motivating problem. It's equally likely, that there isn't such a motivating problem per se, if it's simply a lens which offers different solutions to the same problem. Which is it?

获得一些能够重建 Kleislis 需求的输入会非常有帮助.

It'd be really helpful to get some input be able to reconstruct the need for Kleislis.

推荐答案

Kleisli aka ReaderT is 从实际的角度来看 #2(以及我稍后展示的 #3) - 依赖注入同一个组件分成几个功能.如果我有:

Kleisli aka ReaderT is from practical point of view #2 (and as I show later #3) - dependency injection of one the same component into several functions. If I have:

val makeDB: Config => IO[Database]
val makeHttp: Config => IO[HttpClient]
val makeCache: Config => IO[RedisClient]

然后我可以通过这种方式将事物组合为一个 monad:

then I could combine things as a monad this way:

def program(config: Config) = for {
  db <- makeDB(config)
  http <- makeHttp(config)
  cache <- makeCache(config)
  ...
} yield someResult

但是手动传递东西会很烦人.因此,我们可以将 Config => 作为类型的一部分,并在没有它的情况下进行我们的 monadic 组合.

but passing things manually would be annoying. So instead we could make that Config => part of the type and do our monadic composition without it.

val program: Kleisli[IO, Config, Result] = for {
  db <- Kleisli(makeDB)
  http <- Kleisli(makeHttp)
  cache <- Kliesli(makeCache)
  ...
} yield someResult

如果我的所有函数首先都是 Kleisli,那么我就可以跳过 for comprehension 的 Kleisli(...) 部分.

If all of my functions were Kleisli in the first place, then I would be able to skip that Kleisli(...) part of the for comprehension.

val program: Kleisli[IO, Config, Result] = for {
  db <- makeDB
  http <- makeHttp
  cache <- makeCache
  ...
} yield someResult

这里还有另一个可能流行的原因:无标签决赛和 MTL.您可以定义您的函数以某种方式使用 Config 来运行并使其成为契约,但无需指定您确切拥有的 F[_] 的方式和类型:

And here comes another reason why this might be popular: tagless final and MTL. You could define that your function somehow uses Config to run and make it its contract, but without specifying how and what kind of F[_] you exactly have:

import cats.Monad
import cats.mtl.ApplicativeAsk

// implementations will summon implicit ApplicativeAsk[F, Config]
// and Monad[F] to extract Config and use it to build a result
// in a for comprehension
def makeDB[F[_]: Monad: ApplicativeAsk[*, Config]]: F[Database]
def makeHttp[F[_]: Monad: ApplicativeAsk[*, Config]]: F[HttpClient]
def makeCache[F[_]: Monad: ApplicativeAsk[*, Config]]: F[RedisClient]

def program[F[_]: Monad: ApplicativeAsk[*, Config]]: F[Result] = for {
  db <- makeDB
  http <- makeHttp
  cache <- makeCache
  ...
} yield result

如果你定义 type F[A] = Kleisli[IO, Cache, A] 并提供必要的隐式(这里:Monad[Kleisli[IO, Cache, *]]code> 和 ApplicativeAsk[Kleisli[IO, Cache, *], Cache]) 你将能够像前面使用 Kleisli 的例子一样运行这个程序.

If you define type F[A] = Kleisli[IO, Cache, A] and provide necessary implicits (here: Monad[Kleisli[IO, Cache, *]] and ApplicativeAsk[Kleisli[IO, Cache, *], Cache]) you will be able to run this program the same way as the previous example with Kleisli.

但是,您可以将 cats.effect.IO 切换为 monix.eval.Task.你结合了几个 monad 转换器,例如ReaderTStateTEitherT.或者 2 个不同的 Kleisli/ReaderT 注入 2 个不同的依赖项.而且因为 Kleisli/ReaderT 是简单类型",您可以与其他 monad 组合,您可以根据需要将内容堆叠在一起.使用无标记 final 和 MTL,您可以将程序的声明性需求与定义将要使用的实际类型的部分分开,并且您可以使用更小、更简单的构建块来构建它们.

BUT, you could switch cats.effect.IO to monix.eval.Task. You combine several monad transformers e.g. ReaderT and StateT and EitherT. Or 2 different Kleisli/ReaderT to inject 2 different dependencies. And because Kleisli/ReaderT is "just simple type" that you can compose with other monads, you can stack things together to your needs. With tagless final and MTL, you can separate the declarative requirement of your program where you write down what each component needs to work (and then be able to use with extension methods), from the part where you define the actual type which will be used, and which you can build from smaller, simpler building blocks.

据我所知,这种简单性和可组合性是许多人使用 Kleisli 的原因.

As far as I can tell this simplicity and composability is why many people use Kleisli.

也就是说,在这种情况下,有其他方法可以设计解决方案(例如,ZIO 以不需要 monad 转换器的方式定义自己),而许多人只是按照不需要任何东西的方式编写代码类似于 monad 转换器.

That said, there are alternative approaches to designing solutions in such cases (e.g. ZIO defines itself in such a way that monad transformers should not be required) while many people simply write their code the way that wouldn't make them require anything monad transformer-like.

至于你对范畴论的担忧Kleisli是

是否每个 monad 都来自一个附属?"问题的两个极值解之一

one of two extremal solutions to the question "Does every monad arise from an adjunction?"

但是,我无法指出许多每天都在使用它并且根本不关心这种动机的程序员.至少我个人不认识任何人将其视为偶尔有用的实用程序".

however I wouldn't be able to point at many programmers who use it daily and bother with this motivation at all. At least I don't know personally anyone who treats this as anything else than "occasionally useful utility".

这篇关于什么时候应该使用 Kleisli?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆