Skip to content

Instantly share code, notes, and snippets.

@LiberalArtist
Created June 1, 2026 17:41
Show Gist options
  • Select an option

  • Save LiberalArtist/afecd2821892d9d9e2d2b9b616abecb5 to your computer and use it in GitHub Desktop.

Select an option

Save LiberalArtist/afecd2821892d9d9e2d2b9b616abecb5 to your computer and use it in GitHub Desktop.
Simulating generic `do` notation with a continuation monad
#lang racket
;; https://blog.sigfpe.com/2008/12/mother-of-all-monads.html
(require syntax/parse/define
(only-in data/applicative pure)
(only-in data/monad chain))
;; By happy coincidence, pure and chain are the generic versions,
;; bind and return are specific to the continuation monad below.
(define ((return x) k)
(k x))
(define ((bind f m) k)
(m (λ (x)
((f x) k))))
(define (Cont f)
f)
(define (runCont m [k (λ (x) x)])
(m k))
(define-syntax-parser do
;; a `do` that only works with `Cont`, per the hypothetical
;; adapted from data/monad by Alexis King, ISC license
#:literals [<-]
#:track-literals
[(_ x:expr)
#'x]
[(_ [var:id <- mx:expr] . rest)
#'(bind (λ (var) (do . rest))
mx)])
(define-syntax (<- stx)
(raise-syntax-error #f "cannot be used outside of a do block" stx))
(define ex1
(do [a <- (return 1)]
[b <- (return 10)]
(return (+ a b))))
(module+ test
(runCont ex1))
(define ex2
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
(fred 10)))]
(return (+ a b))))
(module+ test
(runCont ex2))
(define ex3
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
"escape"))]
(return (+ a b))))
(module+ test
(runCont ex3))
(define ex4
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
(vector (fred 10) (fred 20))))]
(return (+ a b))))
(module+ test
(runCont ex4))
;; test5 uses a general-purpose do for comparison
(define ex6
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
(append (fred 10) (fred 20))))]
(return (+ a b))))
(module+ test
(runCont ex6 list))
(define ex7
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
(append* (list (fred 10) (fred 20)))))]
(return (+ a b))))
(module+ test
(runCont ex7 list))
(define ex8
(do [a <- (return 1)]
[b <- (Cont (λ (fred)
(chain fred (list 10 20))))]
(return (+ a b))))
(module+ test
(stream->list ; data/monad defines chain on lists as lazy
(runCont ex8 pure)))
(define (i x)
(Cont (λ (fred)
(chain fred x))))
(define (run m)
(runCont m pure))
(define test9
(do [a <- (i '(1 2))]
[b <- (i '(10 20))]
(return (+ a b))))
(module+ test
(stream->list
(run test9)))
;; test10 would work fine if you implemented an IO monad
;; with `print` and `getLine` operations using `gen:monad`
;; ... but none of the above helps you to implement that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment