aboutsummaryrefslogtreecommitdiff
path: root/state-monad.scm
blob: b261d959435dc589ab53c1165027c7f7fdc555c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
runState (return "X") 1
=> ("X", 1)

;;; return set the result value but leave the state unchanged

(read-enable 'curly-infix)

(use-modules (oop goops)
             (oop goops describe)
             (ice-9 match)
             (srfi srfi-1))

(define-generic bind)

(define-generic return)

(define-class <optional> ()
  (slot #:init-value #f
        #:init-keyword #:slot)
  (just  #:init-value #t
        #:init-keyword #:just))

(define (nothing) (make <optional> #:just #f))

(define (just obj) (make <optional>
                     #:just #t
                     #:slot obj))

(define (nothing? this)
  (not (slot-ref this 'just)))

(define (just? this)
  (not (nothing? this)))


(define-method (write (this <optional>) port)
  (if (just? this)
      (format port "[Just ~s]" (slot-ref this 'slot))
      (format port "[Nothing]")))


;; bind :: Monad m => m a -> (a -> m b) -> m b
;; return :: Monad m => a -> m a
;; map :: Functor f => (a -> b) -> f a -> f b

(define-method (>>= (this <optional>)
                    (proc <procedure>))
  (cond ((nothing? this) nothing)
        ((just? this) 
         (match this
           (($ <optional> slot) (proc slot))))))

(define-method (>>= (this <null>)
                    proc)
  '())

(define-method (>>= (this <pair>)
                    (proc <procedure>))
  (concatenate! (map proc this)))

(define-syntax do
  (syntax-rules (<- let x)
    ((_ (let var = val) rest ...)
     (let ((var val)) (do rest ...)))
    ((_ (<- ptrn val) rest ...)
     (<- ptrn val rest ...))
    ((_ a) a)
    ((_ token rest ...)
     (begin token (do rest ...)))))

(define-syntax <-
  (lambda (x)
    (syntax-case x (just)
      ((_ (just var) val rest ...)
       #'(match val
           (($ <optional> slot)
            (let ((var slot))
              rest ...)))))))

;;; Examples:

(do { (just x) <- (just 10) } x) ; => 10

(let ((j (just 10)))
  (do { (just x) <- j }
      (+ x 10))) ; => 20