The following is a simple implementation of single-output monadic do-notation as Racket syntax. The programmer can choose the monad that goes into the do-notation: because we avoid generics, we can use the Identity monad, for instance.

#lang racket
 
(require racket/match)
 
(struct monad
  (return bind))
 
(define List
  (monad
   (λ (x) (list x))
   (λ (xs f) (append-map f xs))))
 
(define-syntax do
  (syntax-rules (<- pure)
    [(_ m mexp)  mexp]
    [(_ m var <- mexp rest ...) ((monad-bind m) mexp (lambda (var) (do m rest ...)))]
    [(_ m pure value)           ((monad-return m) value)]
    ))
 
(do List
    i <- (do List
       x <- '(3 4)
       v <- '(2 7 4)
       pure (+ x v))
    j <- '(2 3)
    pure (* i j))
 
 
;; The main limitation here is that this Do-notation only
;; allows unary outputs. Having more would require using lists
;; in an intelligent way.
(match '(3 4) [(list u v) (+ u v)])

Tags: Racket.