(评论)
(comments)

原始链接: https://news.ycombinator.com/item?id=40480056

编程中的代数效应允许开发人员以函数方式处理副作用。 我们使用处理程序来管理副作用,而不是在遇到错误时抛出异常。 这是使用异常示例的简单解释: 当函数“Foo”遇到特定的命名函数“Bar”时,它会在其范围内向上搜索处理程序“Baz”。 一旦找到,“Baz”版本的“Bar”就会使用原始参数和“Foo”的其余部分执行。 重复此过程直到“Foo”完成。 在我们的示例中,异常实现会忽略“resume”并仅包含一个函数“throw”。 我们定义一个“printExceptions”处理程序来捕获并打印消息。 通过此设置,当“getPage”失败时,控件会跳转到“printExceptions”,它会在不中断正常程序流程的情况下处理错误。 此外,我们可以创建其他效果,例如“流”,它生成潜在的无限和/或异步数据流,而不是仅仅依赖异常,从而使我们能够有效地处理大型数据集。 其他示例包括 Unison 语言中的“Each.toList”,我们在“each”函数内维护状态信息的同时映射范围,并相应地处理每个元素,而不会中断主流执行。 这些处理程序可以随意恢复功能,使代码更加灵活和高效。

相关文章

原文






























































































You can think of algebraic effects kind of like:

From callsite Foo, I call out to a function with a known name, Bar, with one or more parameters. The runtime (or sufficiently smart compiler) searches upwards in scope, for a handler Baz that provides the function with that name. Baz's Bar is then called, with both the provided parameters, and, crucially, a function that is "the rest of Foo."

So, an implementation of Exception with effects would ignore the resume, and look something like:

    define_effect Exception {
      // only one function provided by Exception, but could have more
      throw(String)
    }
    define_handler printExceptions {
      throw(msg, resume_func): { println(msg) }
    }
    define_func getPage(url) {
      request = http.get(url)
      if not request.ok { throw("Could not download") }
      return page
    }
    // main entrypoint
    withHandlers printExceptions {
      page = getPage("https://cheese.com")
      println(page.text)
    }
But you could also write an "on error resume next" handler for Exception. Exceptions thrown with this handler would be equivalent to toss. (in real life you'd probably write a different effect, rather than re-using the Exception/throw effect):
    define_handler onErrorResumeNext {
      throw(msg, resume_func): { 
        println(msg)
        // YOLO, call it anyways
        withHandlers onErrorResumeNext {
          resume_func()
        }
      }
    }
    withHandlers onErrorResumeNext {
      page = getPage("https://cheese.com")
      println(page.text) // Probably uninitialized - a better  :P
    }
Breaking away from the Exception example, two cool examples are:
    // Stream - potentially infinite and/or asynchronous iterables
    // basically just like python generators
    define_effect Stream {
      emit(item)
    }
    define_handler toList(accumulator) {
      emit(item, resume_func): {
        if item == nil {
          return accumulator
        } else {
          accumulator.append(item)
          withHandlers toList { resume_func() }
        }
      }
    }
    define_handler take(how_many) {
      emit(item, resume_func) {
        if how_many == 0 {
          emit nil
        } else {
          withHandlers take(how_many-1) { resume_func() }
        }
      }
    }
    define_func fib_stream() {
      a, b = 0, 1
      loop {
        emit a
        a, b = b, a+b
      }
    }
    // Usage
    first_five = withHandlers toList {
      withHandlers take(5) {
        fib()
      }
    }
    println("The first five fibonacci numbers are", first_five)
And this one I'm just going to lift from the Unison documentation [1]:
    Each.toList do
      a = Each.range 0 5
      -- beginning of resume_func f_A
      b = each [1, 2, 3]
      -- beginning of resume_func f_B
      guard (a < b)
      -- beginning of resume_func f_C
      (a, b)
    -- yields
    [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
Note that this has the same semantics as:
    results = []
    for (a = 0; a < 5 ; a++) {  // for each a, call f_A(a)
      foreach b in [1, 2, 3] {  // for each b, call f_B(b)
        if not a
And it is possible to do this, because these resumable functions can be resumed an arbitrary number of times - it's up to the handler. They also can pass parameters back to the resume_func.

Exceptions/hurl = exactly 0 resumptions toss = exactly 1 resumption effects = 0..many resumptions

[1]: https://share.unison-lang.org/@unison/base/code/releases/3.5...

































联系我们 contact @ memedata.com