#lang racket ; compute a function of x, and record this in the log state ; the default value of the state is the empty list (define (my-action x [log-state '()]) (letrec ( [ result (+ 1 x) ] [ new-log-state (append log-state (list (format "Computed ~a" result))) ] ) (list result new-log-state) )) ; chain a sequence of actions, passing state to next one ; and remembering the result (letrec ( [ c1 (my-action 42 '()) ] [ c2 (my-action 7 (second c1)) ] [ c3 (my-action (+ (first c1) (first c2)) (second c2)) ] ) c3 ) ; converting this same let chain into lambdas puts the actions into ; reverse order! ((lambda (c1) ((lambda (c2) ((lambda (c3) c3) (my-action (+ (first c1) (first c2)) (second c2)) ; compute c3 ) ) (my-action 7 (second c1)) ; compute c2 ) ) (my-action 42 '()) ; compute c1 ) ; if we pull out the function result v and the state s ; the computation is clearer (letrec ( [ c1 (my-action 42 '()) ] [ v1 (first c1) ] [ s1 (second c1) ] [ c2 (my-action 7 s1) ] [ v2 (first c2) ] [ s2 (second c2) ] [ c3 (my-action (+ v1 v2) s2) ] [ v3 (first c3) ] [ s3 (second c3) ] ) (list v3 s3) ; put the result back into the caller's context ) ; it would be nice to have a chaining operator that pulled out ; the result into a new variable, and automatically passed the ; updated state to the next action. The chaining operator is ; applied to the initial state, and next-action must be a ; function that is applied to the next-state, i.e. a chain operation ; (chain action arg result-var next-action) ; is a function from the current state to a final result ; where ; action is a function (op arg state-in) -> (result, state-out) ; that takes an argument and current state, applies op on them, ; and returns a list of the operation result and next state. ; result-var is a variable identifier that is bound to the result ; next-action is a chain function that can reference any variable ; from any enclosing chain operation (define-syntax-rule (chain action arg result-var next-chain) (lambda (state-in) (letrec ( [ c (action arg state-in) ] ; perform the action on the current state [ result-var (first c) ] ; capture the result in a variable [ state-out (second c) ] ; capture the new state ) (next-chain state-out) ; feed the new state into the next chain ; which can make reference to result-var ))) ((chain my-action 42 v1 (chain my-action 7 v2 (chain my-action (+ v1 v2) v3 ; previous v1 v2 are available (lambda (s) (list v3 s)) ) ) ) '()) ; if we do not want state to escape into the outside world ; then we can define a done statement that prevents us from ; obtaining it. (define-syntax-rule (done exp) (lambda (state-in) exp) ) ((chain my-action 42 v1 (chain my-action 7 v2 (chain my-action (+ v1 v2) v3 (done v3) ) ) ) '()) ; and finally, using the default input state of the action ; we can start off the chain (define-syntax-rule (chain-start op arg result-var next-action) (letrec ( [ c (op arg) ] ; exploit default initial state of op [ result-var (first c) ] [ state-out (second c) ] ) (next-action state-out) )) (chain-start my-action 42 v1 (chain my-action 7 v2 (chain my-action (+ v1 v2) v3 (done v3) )))