问题描述
我最近偶然发现了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:
- 组成返回单子的函数:
f: a -> m[b]
与g: b -> m[c]
-我认为单子的定义已经捕获了这种情况-do/bind/for/flatMap
做到了.无需依靠Kleisli构造即可实现这一目标.因此,这不能成为Kleisli IMO的主要"用例. - 插入配置:该声明指出,如果多个对象(类型,案例/数据类等)需要进行
Config
injected 注入,则采用Kleisli构造可用于提取掉可重复的进样.有许多方法可以实现这一点(例如,使用Scala中的implicit
),可能不需要调用Kleisli.再次,IMO这并不作为主要"用例脱颖而出. - Monad变形金刚:我对此没有深刻的了解,但这是我的解释:如果您需要组成monads",则 使您可以参数化单子晶体.例如,可以将
M1[M2[M1[M2[a]]]]
转换为[M1[M2[a]]]
,可以将 (我可能是错误的)跨过Monadic边界展平以与a -> M3[b]
组合(例如) .为此,我们可以用一个Kleisli三元组并调用该构造,因为如果您要从头开始做它,您可能只是发明 Kleisli. 这似乎是证明使用Kleisli的正当理由.这是正确的吗?
- Composing functions that return monads:
f: a -> m[b]
withg: 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. - 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 withimplicit
s in Scala) that invoking a Kleisli may not be necessary. Again, IMO this doesn't stand out as a "primary" use case. - 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 ana -> 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以及配置注入.但是,他们不能提倡问题来提倡克莱斯利斯的力量.
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是可以用来代替monads的正交构造,而它们(Kleisli)是范畴理论我们可以通过它来查看功能世界的问题(例如,Klesli只是包装了一个单子函数a -> M[b]
,现在我们可以在更高层次的抽象中工作,其中函数是操作的对象)与使用的对象).因此,可以将Kleisli的使用简单地理解为"使用Kleisli进行功能编程".如果这是真的,那么应该是一种情况,Kleisli可以比现有构造更好地解决问题,而我们回过头来讨论激励"问题.问题.同样可能的是,如果仅仅是一个 lens ,它为同一问题提供了不同的解决方案,那么本质上就不会存在这样一个激励性的问题.是哪一个?
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又名ReaderT 是从实用的观点#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]
然后我可以通过以下方式将事物组合为单子:
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 =>
设置为该类型的一部分,并在没有它的情况下进行单声道组合.
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,那么我可以跳过该部分的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
这可能会流行的另一个原因是:无标签的final和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, *]]
和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变压器,例如ReaderT
和StateT
和EitherT
.或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是
As for your concern about category theory Kleisli is
但是,我无法指出许多每天使用它的程序员,并且根本不愿意使用这种动机.至少我个人不认识任何将其视为偶尔有用的工具"以外的东西的人.
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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!