FP中的代数效应是什么意思? [英] What does algebraic effects mean in FP?

查看:215
本文介绍了FP中的代数效应是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

参考:

我搜索了很多链接,但似乎没人能具体解释它.谁能给一些代码(使用javaScript)来解释它?

解决方案

什么是代数效应?

TL; DR:简而言之,代数效应是一种例外机制,它允许throw ing函数继续其操作.

尝试将代数效应视为某种try/catch机制,其中catch处理程序不仅处理异常",而且能够提供一些输入 >引发异常的功能.然后,将catch处理程序的输入用于throwing函数中,该函数将继续运行,就像没有异常一样.

一些示例伪代码:

让我们考虑一个需要一些数据来执行其逻辑的函数:

function throwingFunction() {
    // we need some data, let's check if the data is here
    if (data == null) {
        data = throw "we need the data"
    }
    // do something with the data
}

然后我们有调用此功能的代码:

function handlingFunction() {
    try {
        throwingFunction();
    } catch ("we need the data") {
        provide getData();
    }
}

如您所见,throw语句是一个表达式,用于评估catch处理程序提供的数据(我在这里使用了关键字provide,今天的任何编程语言中都不存在afaik). /p>

为什么如此重要?

代数效应是一个非常笼统的基本概念.可以从以下事实中看出这一点:许多现有概念可以用代数效应表示.

try/catch

如果我们喜欢的编程语言中有代数效应但没有异常,那么我们只需在catch处理程序中省略provide关键字,瞧瞧,我们就会有一个异常机制.

换句话说,如果我们有代数效应,则不需要任何例外.

async/await

再次查看上面的伪代码.假设必须通过网络加载所需的数据.如果数据还没有,我们通常会返回一个Promise并使用async/await进行处理.这意味着我们的函数成为异步函数,只能从异步函数中调用.但是,代数效应也可以实现这种行为:

function handlingFunction() {
    try {
        throwingFunction();
    } catch ("we need the data") {
        fetch('data.source')
            .then(data => provide data);
    }
}

谁说必须立即使用provide关键字?

换句话说,如果我们在async/await之前具有代数效应,则无需将语言与它们混淆.此外,代数效果不会呈现我们的函数 colorful -从语言的角度来看,我们的功能不会变得不同步.

面向方面的编程

假设我们希望在代码中包含一些日志语句,但是我们还不知道它将是哪个日志库.我们只需要一些常规的日志语句(我在这里用关键字effect替换了关键字throw,以使其更具可读性-请注意,effect并不是我所知道的任何语言的关键字):

function myFunctionDeepDownTheCallstack() {
    effect "info" "myFunctionDeepDownTheCallstack begins"

    // do some stuff

    if (warningCondition) {
        effect "warn" "myFunctionDeepDownTheCallstack has a warningCondition"
    }

    // do some more stuff

    effect "info" "myFunctionDeepDownTheCallstack exits"
}

然后我们可以在几行中连接任何一个日志框架:

try {
    doAllTheStuff();
}
catch ("info" with message) {
    log.Info(message);
}
catch ("warn" with message) {
    log.Warn(message);
}

通过这种方式,日志语句和实际执行日志记录的代码是分开的.

如您所见,throw关键字实际上并不适合非常普通的代数效果.更合适的关键字是effect(此处使用)或perform.

更多示例

使用代数效应可以轻松实现其他现有语言或库结构:

  • 具有yield的迭代器.具有代数效应的语言不需要yield语句.
  • React Hooks(这是库级别的构造示例-这里的其他示例是语言构造).

今天的支持

AFAIK开箱即用的语言支持支持代数的语言并不多(如果您知道有此功能的示例,请添加注释).但是,有些语言允许创建代数效果库,一个示例是带有function*yield关键字的Javascript(即 redux-saga 使用Javascript生成器来创建一些代数效果:

function* myRoutineWithEffects() {
    // prepare data load
    let data = yield put(loadDataAction());
    // use the data
}

这将通过在加载数据时暂停例程来加载数据.然后,结果通过yield关键字传递到例程,然后可以在其中使用.

Ref:

I have searched a lot of links, but it seems that no one could explain it specifically. Could anyone give some code(use javaScript) to explain it?

解决方案

What is an Algebraic Effect?

TL;DR: In short, Algebraic Effects are an exception mechanism which lets the throwing function continue its operation.

Try to think of Algebraic Effects as some sort of try / catch mechanism, where the catch handler does not just "handle the exception", but is able to provide some input to the function which threw the exception. The input from the catch handler is then used in the throwing function, which continues as if there was no exception.

Some sample pseudo code:

Let's consider a function which needs some data to perform its logic:

function throwingFunction() {
    // we need some data, let's check if the data is here
    if (data == null) {
        data = throw "we need the data"
    }
    // do something with the data
}

Then we have the code that invokes this function:

function handlingFunction() {
    try {
        throwingFunction();
    } catch ("we need the data") {
        provide getData();
    }
}

As you see, the throw statement is an expression evaluating to the data provided by the catch handler (I used the keyword provide here, which afaik does not exist in any programming language of today).

Why is this important?

Algebraic Effects are a very general and basic concept. This can be seen by the fact that many existing concepts can be expressed in Algebraic Effects.

try/catch

If we had Algebraic Effects but no Exceptions in our favorite programming language, we could just omit the provide keyword in the catch handler, and voilà, we would have an exception mechanism.

In other words, we would not need any Exceptions if we had Algebraic Effects.

async/await

Look again at the pseudo code above. Let's assume the data which we need has to be loaded over the network. If the data is not yet there, we would normally return a Promise and use async/await to handle it. This means that our function becomes an asynchronous function, which can only be called from asynchronous functions. However, Algebraic Effects are capable of that behavior too:

function handlingFunction() {
    try {
        throwingFunction();
    } catch ("we need the data") {
        fetch('data.source')
            .then(data => provide data);
    }
}

Who said that the provide keyword has to be used immediately?

In other words, had we had Algebraic Effects before async/await, there would be no need to clutter up the languages with them. Furthermore, Algebraic Effects would not render our functions colorful - our function does not become aynchronous from the language viewpoint.

Aspect-Oriented Programming

Let's say we want to have some log statements in our code, but we don't yet know which logging library it will be. We just want some general log statements (I replaced the keyword throw with the keyword effect here, to make it a bit more readable - note that effect is not a keyword in any language I know):

function myFunctionDeepDownTheCallstack() {
    effect "info" "myFunctionDeepDownTheCallstack begins"

    // do some stuff

    if (warningCondition) {
        effect "warn" "myFunctionDeepDownTheCallstack has a warningCondition"
    }

    // do some more stuff

    effect "info" "myFunctionDeepDownTheCallstack exits"
}

And then we can connect whichever log framework in a few lines:

try {
    doAllTheStuff();
}
catch ("info" with message) {
    log.Info(message);
}
catch ("warn" with message) {
    log.Warn(message);
}

This way, the log statement and the code that actually does the logging are separated.

As you can see, the throw keyword is not really suited in the context of the very general Algebraic Effects. More suitable keywords would be effect (as used here) or perform.

More examples

There are other existing language or library constructs that could be easily realized using Algebraic Effects:

  • Iterators with yield. A language with Algebraic Effects does not need the yield statement.
  • React Hooks (this is an example of a construct at the library level - the other examples here are language constructs).

Today's support

AFAIK there are not many languages with support for Algebraic Effects out of the box (please comment if you know examples that do). However, there are languages which allow the creation of algebraic effects libraries, one example being Javascript with its function* and yield keywords (i.e. generators). The library redux-saga uses Javascript generators to create some Algebraic Effects:

function* myRoutineWithEffects() {
    // prepare data load
    let data = yield put(loadDataAction());
    // use the data
}

This would load data by pausing the routine while the data is loading. Then, the results are passed to the routine through the yield keyword and can then be used there.

这篇关于FP中的代数效应是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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