接1万个单子_像我一样解释免费的单子五部分1

接1万个单子

In Cats documentation:

在Cats文档中:

A free monad is a construction that allows you to build a monad from any Functor.

免费的monad是一种允许您从任何Functor生成monad的构造。

Another definition says it is an idea to switch from effect functions (that might be impure) to plain data structures representing our domain logic.

另一个定义是从效果函数(可能不纯)切换到表示我们域逻辑的普通数据结构的想法。

Simply put, it is like a wrapper that wraps any ADT into a monadic structure DSL that you able to create a program out of it. It separates your program DSL with its interpreter so that you can pick and choose various interpreters.

简而言之,它就像一个包装器,将任何ADT包装到Monadic结构DSL中,您可以用它来创建程序。 它使用解释器将程序DSL分开,以便您可以选择各种解释器。

This post will start with a simple Todo application (no concurrency and all that), from the imperative standpoint, and slowly transform it into a Free monad style.

从命令的角度出发,本文将从一个简单的Todo应用程序开始(无并发性),然后逐步将其转换为Free monad风格。

创建代数 (Creating Algebra)

The above, we created the Todo case class, with a simple CRUD.

上面,我们用一个简单的CRUD创建了Todo案例类。

The initial way to embed DSL to other programming languages was by using ADT to encode every sentence in your programming languages as a form of the ADT.

将DSL嵌入其他编程语言的最初方法是使用ADT将您的编程语言中的每个句子编码为ADT的一种形式。

势在必行 (Imperative Program)

We will want to do a series of operations such as Create a Todo. Then, we can Find the id belongs to the Todo List. Read the Todo List, to show all the results of the Todo list that we had, and Mark the Todo that we have finished.

我们将要执行一系列操作,例如Create Todo 。 然后,我们可以Find ID属于待办事项列表。 Read待办事项列表,以显示我们拥有的待办事项列表的所有结果,并标记我们已完成的待办事项。

The imperative way of doing such things can be to construct a List of these ADT like this:

执行此类操作的必要方法可以是构造如下的这些ADT的列表:

Therefore, the program above is just a description. To execute the plan, we need some executing interpreter to run it.

因此,以上程序仅是一个描述 。 要执行计划,我们需要一些执行解释器来运行它。

Let’s create an “execute” interpreter:

让我们创建一个“执行”解释器:

We use pattern matching to do all kinds of action when we run the program’s description above. This is where we all the side-effect and all the mutation happens. Therefore, we will loop through the program and run execute on each of the descriptions:

当我们运行程序的上面的描述时,我们使用模式匹配来执行各种操作。 这是我们所有副作用和所有突变发生的地方。 因此,我们将遍历程序并在每个描述上运行execute

So far, so good. However, the ADT that we describe above is not very useful. We cannot, for instance, Find an id, and based on that id marked the Todo list. Ideally, we want to do a sequential operation, such as creating a Todo List, Reading all the list of todos, and marking the finished ones.

到目前为止,一切都很好。 但是,我们上面描述的ADT并不是很有用。 例如,我们无法Find ID,并根据该ID标记了Todo列表。 理想情况下,我们希望执行顺序操作,例如创建待办事项列表,读取待办事项的所有列表并标记完成的待办事项。

We need to find a way to get the previous operation’s value and do some other sequential process based on the prior operation’s evaluated value. Sounds like a Monad, right?

我们需要找到一种方法来获取前一个操作的值,并根据前一个操作的评估值执行一些其他顺序处理。 听起来像单声道,对吗?

Ideally, we want to do something like this:

理想情况下,我们想要执行以下操作:

val program = for {
todo <- Create("Do Laundry")
listTodos <- Read
idZero <- Find(0L)
_ <- Mark(todo.id)
} yield ()

The problem now is that since the program is not a List anymore, how do we create the interpreter?

现在的问题是,由于该程序不再是列表,我们如何创建解释器?

Since we are creating a general data structure on the program, we want to have some sort of “wrapper” to wrap these data structures with a Monadic bind to construct a monadic type of program.

由于我们要在程序上创建通用数据结构,因此我们希望有某种“包装器”以Monadic绑定包装这些数据结构,以构造Monadic类型的程序。

大改写 (Big Rewrite)

We need to return our algebra to “return” some value to capture it in the monadic bind:

我们需要返回代数以“返回”一些值以将其捕获到单子绑定中:

Let’s write a wrapper of our program so that it can have that monadic bind function. The monadic operation can be translated to something like this:

让我们编写程序的包装,使其具有该monadic bind函数。 单子运算可以转换为以下形式:

We introduce FlatMap and Pure to bind our original algebraic type to Monad:

我们引入FlatMapPure将我们的原始代数类型绑定到Monad:

Imagine that F[_] being like Todo, but it can be any type of F[_]. It doesn't have any constraint, and FlatMap and Pure are analogous to flatMap and pure in Monad, where it binds the context into a sequential operation.

想象F[_]就像Todo一样,但是它可以是任何类型的F[_] 。 它没有任何约束和FlatMapPure类似于flatMappure的单子,它结合上下文为顺序操作。

Free is a recursive data structure where each subsequent computation can access the previous calculation. This is all we need to build the programs using a straightforward data structure that is free to its interpretation.

Free是一种递归数据结构,其中的每个后续计算都可以访问先前的计算。 这就是我们使用直接解释的自由数据结构来构建程序所需要的。

How can we make the above Free ADT works with the for-comprehension above?

我们如何使以上Free ADT与上述理解兼容?

Ideally, we want to come into this conclusion:

理想情况下,我们希望得出以下结论:

To do so:

为此:

  1. Free has to be a monad. It needs to have some flatMap and map so that scala can detect and do "for-comprehension".

    免费必须是单子。 它需要一些flatMapmap以便Scala可以检测并“理解”。

  2. We want the program to do flatMap on Free, not the "Action" ADT that we defined. That leaves the action just a data structure that we can wire to our interpreter later on.

    我们希望程序在Free上执行flatMap ,而不是我们定义的“ Action” ADT。 剩下的动作只是一个数据结构,我们以后可以将其连接到解释器。

免费创建Monad (Creating Free as a Monad)

Let’s construct Free function, which it needs to have a map and a flatMap method.

让我们构造Free函数,它需要一个mapflatMap方法。

The code flatMap above recursively doing flatMap until it hits Pure, and apply the func to a. f andThen (a => a.flatMap(func)) means it compose the function by applying the input to f first. The return value of f(a) is a Free[F[_], B]. The return value, then, is applied to the consequent function a => flatMap(func). If you still don't get it, slap the above code into IntelliJ, and try solving the type yourself. The more you look at the input type and return type, the more you know how to create the function above.

代码flatMap上述递归做flatMap直到碰到Pure ,以及应用funcaf andThen (a => a.flatMap(func))表示它首先将输入应用于f来组成函数。 f(a)的返回值是Free[F[_], B] 。 然后,将返回值应用于后续函数a => flatMap(func) 。 如果仍然无法理解,请将上面的代码拍入IntelliJ,然后尝试自己解决类型。 您越看输入类型和返回类型,就越了解如何创建上面的函数。

Once we have the map and flatMap function ready, we can start constructing our program.

准备好mapflatMap函数后,就可以开始构建程序了。

However, how do we construct Free?

但是,我们如何构造Free?

解除免费的东西 (Lifting Free stuff)

We want to lift the Action to a Free[F[_],A].

我们要liftAction ,以一个Free[F[_],A]

Let’s create a lift a function that will do that:

让我们创建一个lift功能的函数:

Then, we can create our program like this:

然后,我们可以像这样创建程序:

We can also just create a DSL to make it more readable way by creating an implicit conversion:

我们还可以通过创建隐式转换来创建DSL,使其更具可读性:

口译员 (Interpreter)

Now that we have created our program, we also need to bind the program with an interpreter somehow.

现在我们已经创建了程序,我们还需要以某种方式将程序与解释器绑定。

Let’s look back at the first interpreter that we defined:

让我们回顾一下我们定义的第一个解释器:

The above code looks good, but how do we connect the interpreter above with our Free Monad?

上面的代码看起来不错,但是如何将上面的解释器与Free Monad连接起来?

We want to do something like this — given the following Free[F,A] data structure, we want to traverse the Free structure, evaluating each step, and thread the result to the next subsequent computation. We want to fold the List of the program description.

我们想要做这样的事情-给定以下Free[F,A]数据结构,我们想遍历Free结构,评估每个步骤,然后将结果传递到下一个后续计算中。 我们要折叠程序描述的List

Ultimately, we will do the same for Free, by creating a pattern matching for FlatMap and Return:

最终,我们将通过为FlatMapReturn创建匹配的模式来免费进行相同的操作:

I put a ??? above because we don't have any access to the regular interpreter that we created. If only if we also supply the interpreter as another argument to evaluate the Action:

我放了一个??? 上面的内容,因为我们无权访问我们创建的常规解释器。 如果仅当我们还将解释器作为另一个参数来评估Action

We can run our program with the existing interpreter like this:

我们可以使用现有的解释器来运行程序,如下所示:

runProgram(program)

结论 (Conclusion)

We have come a long way by first introducing the traditional imperative way of creating a program description. We realized that we have no access to the previously computed value within a regular’ List’ of description and cannot make a sequentially like computation that we want to.

首先介绍创建程序描述的传统命令式方法已经走了很长一段路。 我们意识到,我们无法访问常规“描述”列表中的先前计算的值,并且无法进行我们想要的顺序计算。

Then, we created a Free structure that enabled us to wrap our existing ADT into something more monadic. We introduce Free ADT by having FlatMap and Pure, which is analogous to monadic bind for flatMap and pure. Besides, we also created a way to lift the Action type to a Free variety to comprehend them.

然后,我们创建了一个Free结构,使我们能够将现有的ADT包装成更单调的东西。 我们通过拥有FlatMapPure来引入Free ADT,这类似于flatMappure的 单键绑定。 此外,我们还创建了一个办法liftAction类型的Free多种理解他们。

Lastly, we touch on using the existing interpreter that we created, execute, and execute the Free program.

最后,我们使用我们创建的现有解释器, execute并执行Free程序。

In part 2 of this series, I want to dive further into how we can generalize the Free ADT and create a free structure to create a DSL in any program ultimately. Stay tuned!

在本系列的第2部分中,我想进一步探讨如何推广Free ADT并创建一个自由结构以最终在任何程序中创建DSL。 敬请关注!

The full source code is here.

完整的源代码在这里

Thanks for reading! If you enjoy this post, feel free to subscribe to my newsletter to get notified on essays about Career in Tech, interesting links, and content!

谢谢阅读! 如果您喜欢这篇文章,请随时 订阅 我的新闻通讯,以获取有关技术职业,有趣的链接和内容的文章的通知!

You can follow me also follow me on Medium for more posts like this.

您可以在媒体上关注我,也可以关注我,以获取更多类似的帖子。

Originally published at https://edward-huang.com.

最初在 https://edward-huang.com上 发布

翻译自: https://levelup.gitconnected.com/explain-free-monad-like-i-am-five-part-1-5bee794074bd

接1万个单子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值