Skip to content
/ cio Public

A structural effects-and-handlers library for Racket

License

Notifications You must be signed in to change notification settings

omentic/cio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cio: a structural effects-and-handlers library

cio is an (untyped) structural effects-and-handlers library for Racket.

This was primarily a learning project for me to understand how effects-and-handlers and call/cc and the like slot together. If you want to use it, go ahead! The implementation is fairly minimal. Be warned it is not well-tested, and not guaranteed to behave with other continuation-based libraries (though it should).

design

(define (write msg)
  (suspend `(write ,msg)))

(define computation
  (λ () (begin (write "hello") (write "world!"))))

(try (computation)
  [`(write ,msg)
    (println msg) (resume)])

(try (computation)
  [`(write ,msg)
    (println (format "I see you tried to print '~a'. Not so fast!" msg)) (resume)])

(define (read)
  (suspend `(read)))

(define computation-ii
  (λ () (begin (write "hello--") (write (string-append "hello " (read))))))

(try (computation-ii)
  [`(write ,msg)
    (println msg) (resume)]
  [`(read)
    (println "(what's your name)")
    (resume (read-line))])

The design of cio is primarily modelled after Effekt: though lacking the types, of course. cio provides four primitives: try, suspend, resume, and resume/suspend.

try steps a computation to a value. If in that process, an effect is raised by suspend, and an appropriate handler (case) is provided for it within the try body, the effect is handled there and the computation is (possibly) resumed.

There is a distinction between deep and shallow try handlers. cio supports both - deep handlers are used by default, but shallow handlers can be used by inserting the #:shallow parameter immediately following try. Shallow handlers only trigger once, while deep handlers will handle future effects after any resumption. Deep handlers are considerably more useful and intuitive, and combined with mutable state can easily model shallow handlers - thus they are the default.

suspend takes a value, treated as an effect, and pops it up the call stack to the nearest try handler. It should be noted that suspend takes a value. cio is structural, and effect handlers do no more than match upon values. If the user wishes to distinguish between different kinds of effects (which they probably should), the value in suspend must be wrapped in a tag corresponding to a tag in a handler.

resume resumes a computation, optionally with a value. resume/suspend resumes a computation with an effect. They are only usable within the handlers of a try block. When an effect is raised from the computation of a try block, and caught by a handler, a continuation to the rest of the computation is bound to a hidden continuation variable, which resume operates upon. This means that it can be difficult to operate on an external try block within another nested try block - however, resume is expanded before packing it up in a function, meaning this can be worked around with mutable state, Effekt-style.

Resumption is an extremely powerful feature. Suspension alone only allows the implementation of exceptions. Suspension and resumption without a value allows the implementation of generators. Suspension and resumption with a value allows the implementation of async/await. Suspension and resumption with another effect allows the implementation of bidirectional control flow. This makes for a very powerful - possibly too powerful - abstraction over all non-local control flow. Whether this can be done in a controlled and well-typed fashion is an active area of research.

bibliography

A lot of wonderful papers on effects-and-handlers have been published, most of them in the last couple of years. If you are interested in learning more, I recommend reading the following papers / listening to the following talks, in order:

  1. Deliminated Continuations, Demystified (talk only)
  2. Effects as Capabilities (talk)
  3. Handling Bidirectional Control Flow (talk)
  4. Effects, Capabilities, and Boxes (talk)
  5. Continuing WebAssembly with Effect Handlers (talk)
  6. Soundly Handling Linearity (talk)
  7. Lexical Effect Handlers, Directly

About

A structural effects-and-handlers library for Racket

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages