Created
June 1, 2026 17:41
-
-
Save LiberalArtist/afecd2821892d9d9e2d2b9b616abecb5 to your computer and use it in GitHub Desktop.
Simulating generic `do` notation with a continuation monad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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