; Richard Uhtenwoldt’s examples
; Monads in Scheme
; monad basis:
(define (bind p k)
(cond ((not (procedure? p)) (error "first arg to bind must be a procedure"))
((not (procedure? k)) (error "second arg to bind must be a procedure"))
(#t
(lambda ()
(let* ((p2 (p))
(q (k p2)) )
(if (not (procedure? q))
(error "second arg to bind must be a procedure that returns a procedure")
(q) ))))))
(define (return v) (lambda () v))
; particular monadic primitives
(define (display2 a) (lambda() (display a)))
(define read2 (lambda () (read)))
; Test code employing the monadic primitives: copy input to output
;((bind read2 (lambda (x) (display2 x))))
; Test code employing the monadic primitives: copy input to output
;((bind read2 (lambda (x) (display2 x)))) xx
digression on above code.
(define (then p q) (bind p (lambda (ignored) q)))
(define-syntax do-notation
(syntax-rules (<-)
((do-notation clause) clause)
((do-notation (<- var exp) clauses ...)
(bind exp (lambda (var) (do-notation clauses ...))))
((do-notation exp clauses ...)
(then exp (do-notation clauses ...)))))
;((do-notation (<- x read2) (display2 x)))
(define new-cell2
(lambda () (vector #f)))
(define (cell-ref2 cell)
(lambda () (vector-ref cell 0)))
(define (cell-set2 cell new-value)
(lambda () (vector-set! cell 0 new-value)))
;((bind new-cell2 (lambda (cell) (then (cell-set2 cell 5) (bind (cell-ref2 cell) display2)))))
;; should print 5
(define (make-spaceship2 fuel-capacity)
(do-notation
(<- position new-cell2)
(cell-set2 position 0)
(<- fuel new-cell2)
(cell-set2 fuel 0) ;deliver the new spaceship unfueled for safety!
(<- some-constant (return 1))
(return (lambda (method)
(case method
((add-fuel) (lambda (fuel-to-add)
(bind (cell-ref2 fuel) (lambda (f)
(cell-set2 fuel (min (+ f fuel-to-add) fuel-capacity))))))
((move) (lambda (displacement-desired)
(bind (cell-ref2 fuel) (lambda (f)
(let* ( (cost-in-fuel-of-displacement-desired
(* some-constant (abs displacement-desired)))
)
(if (>= f cost-in-fuel-of-displacement-desired)
(then
(cell-set2 fuel (- f cost-in-fuel-of-displacement-desired))
(bind (cell-ref2 position) (lambda (p)
(cell-set2 position (+ p displacement-desired)))))
(display2 "Not enough fuel. Move command ignored.\n"))
)))))
((dock) (lambda ()
;; returns nil or t according as docking fails or succeeds.
;; docking succeeds if and only if the spaceship is where it was
;; when it was delivered.
(bind (cell-ref2 position) (lambda (p) (return (equal? 0 p)))) ))
)))))
(define (add-fuel spaceship fuel-to-add)
((spaceship 'add-fuel) fuel-to-add) )
(define (move spaceship displacement-desired)
((spaceship 'move) displacement-desired) )
(define (dock spaceship)
((spaceship 'dock)) )
;; some tests of the above code follow
((do-notation
(<- enterprise (make-spaceship2 100))
(add-fuel enterprise 80)
(move enterprise 30)
(move enterprise -30)
(<- outcome (dock enterprise))
;; #f was chosen because it is the standard "uninteresting" value in Scheme:
(if (not outcome) (display2 "Unit test #0 failed.\n") (return #f))
(add-fuel enterprise 20)
(move enterprise 30)
(move enterprise -30)
;; the Enterprise runs out of fuel before it can get back to Starbase
(<- outcome2 (dock enterprise))
(if outcome2 (display2 "Unit test #1 failed.\n") (return #f))
(display2 "Unit tests completed.\n")
))