by Marc Nieper-Wißkirchen
This SRFI is currently in final status.  Here is an explanation of each status that a SRFI can hold.  To provide input on this SRFI, please send email to srfi-247@nospamsrfi.schemers.org.  To subscribe to the list, follow these instructions.  You can access previous messages via the mailing list archive.
define-syntactic-monad.)This SRFI extends Scheme with a simple mechanism to implicitly add formal arguments to procedure definitions and to implicitly add arguments to procedure calls. Contrary to parameters (also known as fluids or dynamically bound variables), which can be used for the same purpose, no runtime overhead is generated.
Thanks to its proper tail calls, Scheme is an excellent programming language for describing state machines. In a finite state machine, each state is modelled by a procedure and each state transition by a tail call to one of these procedures. In more general state machines, states are parameterized by values of a list of state variables. Each procedure accepts the current values of the state variables as arguments and each tail call is done with possibly updated values of the state variables.
Consider the following example of an interpreter for a simple programming language dealing with three stacks:
(define run
  (lambda (prog a b c)
    (define a->b
      (lambda (prog a b c)
        (run prog (cdr a) (cons (car a) b) c)))
    (define a->c
      (lambda (prog a b c)
        (run prog (cdr a) b (cons (car a) c))))
    (define b->a
      (lambda (prog a b c)
        (run prog (cons (car b) a) (cdr b) c)))
    (define b->c
      (lambda (prog a b c)
        (run prog a (cdr b) (cons (car b) c))))
    (define c->a
      (lambda (prog a b c)
        (run prog (cons (car c) a) b (cdr c))))
    (define c->b
      (lambda (prog a b c)
        (run prog a (cons (car c) b) (cdr c))))
    (if (null? prog)
        (values a b c)
        (let ([ins (car prog)] [prog (cdr prog)])
          (case ins
            [(a->b) (a->b prog a b c)]
            [(a->c) (a->c prog a b c)]
            [(b->a) (b->a prog a b c)]
            [(b->c) (b->c prog a b c)]
            [(c->a) (c->a prog a b c)]
            [(c->b) (c->b prog a b c)])))))
    In this example, the state variables
      are prog, a, b,
      and c, which are threaded through the
      code.  While the semantics of the code are very clear,
      syntactically it exhibits two problems: Firstly, it doesn't
      scale when we extend the interpreter with more registers, that
      is state variables because we have to change every procedure
      definition and every procedure call.  Secondly, at each
      transition step only a subset of the state variables are
      updated, yet we have to list all state variables at each procedure call.
Using the mechanism provided by this SRFI, we can rewrite the
      interpreter with the following code, which does not exhibit the
      two problems.  At runtime, the code behaves exactly as the
      previous code.  By convention, the names for syntactic monads
      begin with the character $.  (Locally defined
      syntactic monads are often just named $.)
(define-syntactic-monad $ prog a b c)
(define run
  ($ lambda ()
    ($ define (a->b)
      ($ run ([a (cdr a)] [b (cons (car a) b)])))
    ($ define (a->c)
      ($ run ([a (cdr a)] [c (cons (car a) c)])))
    ($ define (b->a)
      ($ run ([a (cons (car b) a)] [b (cdr b)])))
    ($ define (b->c)
      ($ run ([b (cdr b)] [c (cons (car b) c)])))
    ($ define (c->a)
      ($ run ([a (cons (car c) a)] [c (cdr c)])))
    ($ define (c->b)
      ($ run ([b (cons (car c) b)] [c (cdr c)])))
    (if (null? prog)
        (values a b c)
        (let ([ins (car prog)] [prog (cdr prog)])
          (case ins
            [(a->b) ($ a->b)]
            [(a->c) ($ a->c)]
            [(b->a) ($ b->a)]
            [(b->c) ($ b->c)]
            [(c->a) ($ c->a)]
            [(c->b) ($ c->b)])))))
    Iterative and recursive loops are a particular case of state
    machines, which can also benefit from defining a syntactic monad.
    As examples, we give an implementation of the partition procedure found in SRFI 1 and R6RS and of the factor procedure found in Kent Dybvig's The Scheme Programming Language.
(define partition
  (lambda (pred ls)
    (define-syntactic-monad $ in out)
    (let f ([ls ls])
      (if (null? ls)
          ($ values ([in '()] [out '()]))
          ($ let*-values ([() (f (cdr ls))])
            (let ([x (car ls)])
              (if (pred x)
                  ($ values ([in (cons x in)]))
                  ($ values ([out (cons x out)])))))))))
(partition symbol? '(one 2 3 four five 6)) ; => (one four five) (2 3 6)
    (define factor
  (lambda (n)
    (define-syntactic-monad $ n i)
    ($ let f ([i 2])
      (cond
        [(>= i n) (list n)]
        [(integer? (/ n i))
         (cons i ($ f [(n (/ n i))]))]
        [else ($ f [(i (+ i 1))])]))))
(factor 3628800) ; => (2 2 2 2 2 2 2 2 3 3 3 3 5 5 7)
    The following definition is exported by the (srfi
        :247) and (srfi :247 syntactic-monads)
        libraries.
(define-syntactic-monad syntactic
          monad name
          formal …)The define-syntactic-monad form defines a
          syntactic monad.
A define-syntactic-monad form is a definition and
          can appear anywhere any
          other definition can
          appear.
          Syntactic monad name and
          the formals must all be
          identifiers.
        
Syntactic monad name is
          bound as a syntactic keyword.
          The formals, taken as
          symbols, become the state variable names of the
          syntactic monad defined.  At use sites
          of syntactic monad name,
          each state variable name defines a corresponding state
            variable, which is an implicit identifier with the state
          variable name as its symbolic name and which contains the same
          contextual information as the
          keyword syntactic monad
              name in the macro use.  An identifier is
          the same as a state variable if binding the
          identifier would shadow free references to the state variable.
          The list of state variables (in the order of the state variable
          names) at use sites of syntactic monad
              name is denoted by state
              variable ….
        
Example:
(define-syntactic-monad $ a b)
The keyword syntactic monad
              name defined by the define-syntactic-monad definition can be used in
          the following ways (every other use is a syntax violation):
(syntactic monad
                  name lambda formals
                body) is equivalent
              to (lambda
                (state
                  variable …
                . formals) body).
Example:
(($ lambda (c) (list a b c)) 1 2 3) ; => (1 2 3)
(syntactic monad name define
                (name
                . formals) body)
              is equivalent to (define name
                (syntactic monad
                  name lambda formals body).
Example:
($ define (f c d) (list a b c d)) (f 1 2 3 4) ; => (1 2 3 4)
(syntactic monad name
                case-lambda
                [formals body] …)
              is equivalent to (case-lambda [(state
                  variable …
                . formals) body] …)
Example:
(($ case-lambda [() (list a b)]) 1 2) ; => (1 2)
(syntactic monad name let*-values
                ([formals init] …) body)
              is equivalent to (let*-values ([(state
                  variable …
                . formals) init] …) body)
Example:
($ let*-values ([(c) (values 1 2 3)]
                [() (values a (+ b c))])
  (list a b)) ; => (1 5)
            (syntactic monad
                  name procedure expression
                (binding
                …) argument
                  expression …)
              is equivalent to a procedure call of the
              form (procedure expression
                state variable
                  expression … argument
                  expression
                …).  Here,
              each binding is of the
              form [variable expression]
              where each variable must
              be the same as one of the state variables.  It is also a syntax
              violation if one of the variables
              appears more than once amongst
              the variables.
              The state variable
                  expressions are a list of expressions
              corresponding to the state variables.  For
              each variable, which is
              a state variable, the
              corresponding state variable
                  expression is
              the expression
              corresponding to
              the variable.  For the
              state variables that do not appear amongst
              the variables, the
              corresponding state variable
                  expression is a variable reference to the same
              state variable.
Example:
(let ([a 1]) ($ list ([b 2]) 4)) ; => (1 2 4)
(syntactic monad
                  name procedure expression)
              is equivalent to
              (syntactic monad
                  name procedure expression
                ()).
Example:
(let ([a 1] [b 6]) ($ list)) ; => (1 6)
(syntactic monad name
                let loop variable
                ([variable init] …) body)
              is equivalent to
              (let loop variable ([state variable state variable init]
                … [other variable other variable init] …)
                body).  Here, for
              each state variable
              appearing among
              the variables, the
              corresponding state variable
                  init is the
              corresponding init. For each state
                  variable not appearing among
              the variables, the
              corresponding state variable
                  init is a variable reference to the same state
              variable.  The other
                  variables are
              the variables that are
              not state variables in that order.
              The other inits are the
              corresponding inits.
Example:
(let ([a 1])
  ($ let loop ([c 3] [b 2])
    (if (= a 2)
        (list a b c)
        (loop 2 (+ b 6) (+ c 7))))) ; => (2 8 10)
            Note: In (define-syntactic-monad syntactic-monad-name formal …),
          only the names of
          the formals are
          significant.
        
The sample implementation is written in portable
      R6RS scheme.  It can easily be ported to other Scheme
      systems supporting the syntax-case system.
This SRFI was inspired from and would not exist without the
      prior appearance of a form
      of define-syntactic-monad in the source code of
      Chez Scheme.
© 2023 Marc Nieper-Wißkirchen.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.