call/cc in Lua - Possible?

天大地大妈咪最大 提交于 2019-11-28 18:44:56

There are two prerequisites to manually implement call/cc per the Wikipedia quote:

  1. the language must support closures
  2. you must write your program in continuation passing style (CPS)

I suspect you will not like #2.

To write your program in continuation passing style:

  1. Every function must take a continuation argument
  2. Functions must return by calling their continuation

So, using k as the name of the continuation argument, a function would look like:

function multiplyadd(k, x, y, z) return k(x * y + z) end

The toplevel might use print as its continuation, so invoking multiplyadd at top level would look like:

multiplyadd(print, 2, 4, 1)

With that scaffolding we could define call/cc as

function callcc(k,f) return f(k,k) end

Note that the above multiplyadd actually cheats since * and + are not in CPS. It is very tedious to add all the operators in CPS form, replace all the Lua library functions with CPS equivalents, and translate/generate all your code to CPS; see details here.

I guess you forgot the part about writing your program in continuation passing style. Once you do that, call/cc is trivial (in Lua or in any other language), as the continuation will be an explicit parameter to all functions (call/cc included).

PS: besides closures, you also need proper tail calls to program in continuation passing style.

Answering the question about plans for call/cc in Lua: There are no plans for call/cc in Lua. Capturing a continuation is either too expensive or require some code analsis well beyond what the Lua compiler can do. There is also the problem that Lua continuations may include parts in C.

With coroutines, however, we can already implement call/cc1 in Lua (one-shot continuations). That is good enough for many uses of continuations.

The key phrase is

It is possible to implement programs in continuation-passing style

(Emphasis mine.) You do this by taking regular "direct-style" programs and converting them to continuation-passing style (CPS) by a program transformation called the CPS transform. The key is that the CPS transform of call/cc is a simple function.

This is not practical for programmers. The CPS transform has two uses:

  • As a theoretical idea for studying language features, especially control operators
  • As a pass in a compiler that uses CPS as an intermediate language

You don't want to go anywhere near doing CPS transforms on Lua code, especially not by hand.

Here's my cps-convert in scheme, just pass it every function you want to convert.

(define (cps-convert function . functions)
  # Since "help" is called at 2 different places...
  (define (help) (error "syntax: (cps-convert f1 f2 ...)"))
  # Single function converter
  (define (convert func)
    # "name" contains the function's name prefixed with "cps-"
    (let ([name (string->symbol
                          (string-append "cps-" (symbol->string func)))])
      # Dirty hack to define "cps-*" in the global environment
     `(eval '(begin
                   # Necessary to prevent the function from being evaluated
                   (define ,name #f)
                                # Magic
                   (set! ,name (lambda (k . args) (k (func args)))))
                 # Global environment
                 (interaction-environment))))
  # Prerequisite... Call help if condition not met
  (if (symbol? function)
      # function is a symbol
      (cond
        # If there is only one function to convert
        [(null? functions) (convert function)]
        # Else ensure every other "functions" are symbols and convert each
        [(every symbol? functions) (apply convert function functions)]
        # Oops! Condition not met!
        [else (help)])
      # Else clause from previous "if"
      (help)))
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!