;;; Commentary: ;; ;; The state monad! ;; State is best modelled monadically as a function taking a state object, and ;; returning another state object. This state object is in turn represented as i ;; list where the first value is the last value returned, and the secound value ;; is the internal state. ;; ;; All access to this internal value is done through the methods @code{get}, ;; @code{put}, and @code{modify}. ;; ;; One side effect of the @code{} object not being directly accessible is ;; that my trick for multiple dispatch return doesn't work. Which is why this ;; modules also exports @code{return-state} directly. ;; ;;; Code: (define-module (monad state) #:use-module (oop goops) #:use-module (ice-9 match) #:use-module (monad) #:export (return-state run-state get put modify) #:re-export (>>= >> return)) ;;; newtype State = st-list -> st-list ;;; state procedure <=> st-list -> st-list ;;; state list <=> (list ret st) ;;; Wrapper around a procedure with signature: ;;; (st-list -> st-list). Wrapped to allow goops ;;; multiple dispatch to do its thing. (define-class () (proc #:init-keyword #:proc #:getter proc)) ;; (st-list -> st-list) -> State (define (make-state proc) "Creates a state object from a State procedure" (make #:proc proc)) ;;; Define a procedure which is in the state monad. This means that it takes a ;;; state list as a curried argument, and it's return is wrappen in a ;;; object. ;;; It's fully possible to create stateful objects without these macros, but it's ;;; ill adviced since that would just be boilerplate. (define-syntax-rule (define-stateful ((proc args ...) st) body ...) (define (proc args ...) (make-state (lambda (st) body ...)))) (define-syntax-rule (define-stateful-method ((proc args ...) st) body ...) (define-method (proc args ...) (make-state (lambda (st) body ...)))) (define-stateful-method ((>>= (st ) (f )) st-list) (let ((new-st-list ((proc st) st-list))) (match new-st-list ((v _) ((proc (f v)) new-st-list))))) (define-stateful ((return-state v) st-list) "Sets the return value to v" (cons v (cdr st-list))) (define-method (return (a )) return-state) (define-stateful ((get) st-list) "Sets the return value of state to st." (match st-list ((_ st) (list st st)))) (define-stateful ((put v) st-list) "Sets st to v." (list '() v)) (define (modify proc) "Applies proc to the value stored in state, and stores it back" (>>= (<$> proc (get)) put)) (define-method (run-state (st ) init) "Exec state with init as starting state value and st." ((proc st) (list init init)))