Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
txt
I. Introduction.
The main insight of monads is that all side effects, from mutation to
I/O to non-termination, have one thing in common: order of evaluation
matters. In simple, terminating, pure lambda expressions, the order of
evaluation is completely irrelevant: no matter how you reduce it, the
final result is the same with no observable differences. But when you
have side effects, they have to happen in the right order. (Monads
aren't the only formalism for dealing with this -- CPS and A-normal
form do, too. But they're all related.)
(begin (turn-on-safety!)
(pull-trigger!))
(begin (turn-on-safety!)
(pull-trigger!))
-> (begin (turn-on-safety!) ; effect: pull-trigger!
#<void>)
-> (begin #<void> ; effect: turn-on-safety!
#<void>)
-> #<void>
[[(begin (turn-on-safety!)
(pull-trigger!))]]
= (lambda (k)
([[turn-on-safety!]] (lambda (res1)
([[pull-trigger!]] (lambda (res2)
(k res2))))))
(define (rand)
(let ([ans (modulo (* seed 16807) 2147483647)])
(begin (set! seed ans)
ans)))
The whole program would start with an initial seed like so:
(run-my-program (current-time))
http://www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt 2/5
10/10/2018 www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt
(let* ([r1 ((rand-point) seed)]
[r2 ((rand-point) (cdr r1))])
(cons (make-segment (car r1) (car r2))
(cdr r2)))))
Any procedure that can't see or change the value of the seed can be
left unchanged. We call the procedures that can have side effects
"operations," and procedures that can't "pure." For example, we could
write a distance function:
We can abstract the common pattern in the types, too: we say that an
operation that returns a value of type alpha has type T(alpha), where:
Next we try to define BEGIN like before, only instead of in CPS, it's
just threading the accumulator through:
(define (rand)
(begin (get-seed)
(let ([ans (modulo (* ??? 16807) 2147483647)])
(begin (set-seed ans)
(lambda (seed)
(cons ans ans))))))
http://www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt 3/5
10/10/2018 www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt
How does the second operation in RAND get the current seed from the
GET-SEED operation? The problem is that BEGIN disregards the result of
the first operation. Let's write a new combinator that makes the
result of the first operation available to the second:
(define (rand)
(pipe (get-seed)
(lambda (seed)
(let ([ans (modulo (* seed 16807) 2147483647)])
(begin (set-seed ans)
(lambda (seed)
(cons ans ans)))))))
(define (rand)
(pipe (get-seed)
(lambda (seed)
(let ([ans (modulo (* seed 16807) 2147483647)])
(begin (set-seed ans)
(lift ans))))))
(pipe (lift x) f) = (f x)
(pipe m lift) = m
(pipe (pipe m f) g) = (pipe m (lambda (x) (pipe (f x) g)))
[I'm tired and I don't feel like proving the monad laws hold for our
example. Actually, they probably don't because our version of pipe has
two arguments (instead of being completely curried) -- non-termination
would screw this up. Oh well.]
The monad can have any other operations as long as they don't
invalidate the laws.
http://www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt 4/5
10/10/2018 www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt
the result of running an operation produces a pair of values
containing the final value plus the accumulated seed. Since we're
really just interested in the final value, we can construct a "run"
procedure to execute a monadic operation and extract its final value:
Notice also that this is the only way to "get out" of the monad, i.e.,
to go from T(alpha) to alpha. A monadic operation is built up with
combinators to produce one long chain of operations and then "run"
with a top-level procedure. This top-level procedure is very similar
to the process of running a CPS-ed program with an initial
continuation.
IV. Summary.
V. More.
http://www.ccs.neu.edu/home/dherman/research/tutorials/monads-for-schemers.txt 5/5