In functional programming we pass the state as arguments to functions. It can be tedious to track all funciton arguments and not only this when we want to add an extra state parameter to a huge codebas you end up in an never ending work of updating function arguments and call sites. But in scheme we can do better. Consider this little tool that you can make use of in guile.
The first tool in the toolbox is
(define-syntax with
(lambda (x)
(syntax-case x ()
((_ ((a v) ...) code ...)
(with-syntax (((s ...) (generate-temporaries #'(a ...))))
#'(let ((s v) ...)
(syntax-parameterize
((a (lambda (x)
(syntax-case x ()
((_ arg (... ...))
#'(s arg (... ...)))
(_ #'s))))
...)
code ...)))))))
This will essentially bind the syntax parameter a
to value v
e.g. after using a
the value of v
is used
Then the meat can be defined as
(define-syntax make
(lambda (x)
(syntax-case x ()
((_ define-name call-name apply-name arg ...)
(with-syntax (((arg2 ...) (generate-temporaries #'(arg ...))))
#'(begin
(define-syntax-parameter arg
(lambda (x)
(error (format #f "parameter ~a not defined" 'arg))))
...
(define-syntax-rule (define-name (f . a) code (... ...))
(define (f arg2 ... . a)
(with ((arg arg2) ...)
code (... ...))))
(define-syntax-rule (call-name f a (... ...))
(f arg ... a (... ...)))
(define-syntax-rule (apply-name f a (... ...))
(apply f arg ... a (... ...)))))))))
An example of the use is
(make s-define s-call s-apply s)
we make a define
a call
and apply
with extra standard argument s
e.g. make a standard argument s
to each application or definition. We bind values to this state variable s
with with
hence we can do:
(s-define (f x)
(pk `(s is ,s))
(+ s x))
(pk 'a (with ((s 1)) (s-call f 2)))
(s-define (g y) (+ (s-call f 1) (s-call f 2)))
(pk 'b (with ((s 2)) (s-call g 10)))
And get the output:
;; ((s is 1))
;;; (a 3)
;;; ((s is 2))
;;; ((s is 2))
;;; (b 7)
Have fun!!