#lang racket |
|
; when does a term get evaluated? |
|
(define X 2) |
(define Y 3) |
|
; if the world was purely functional it wouldn't matter if both parts |
; of an 'if' were evaluated beforehand. Although it could be quite costly |
; since you are doing unnecessary work that is just thrown away. |
|
(if (< X Y) |
(+ X X) |
(* Y Y) |
) |
|
; but if there are side effects it does matter |
(list (< X Y) |
(print "true") |
(print "false") |
) |
|
|
; so 'if' is clearly a different kind of operation than, say, 'list' |
(if (< X Y) |
(print "true") |
(print "false") |
) |
|
; note that the and and or operations are lazy, so an if |
; (if condition consequent alternate) |
; can also be written as this: |
; (or (and condition consequent) alternate) |
(or (and (< X Y) |
(+ X X) |
) |
(* Y Y) |
) |
|
(or (and (< X Y) |
(print "true") |
) |
(print "false") |
) |
|
; if we have and/or we could define our own if functon using |
; the above relationship. |
|
; But the arguments to our new function get evaluated before the call |
(define my-if |
(lambda (condition consequent alternate) |
(or (and condition consequent ) alternate ) |
) |
) |
|
; it doesn't matter here |
(my-if (< X Y) |
(+ X X) |
(* Y Y) |
) |
|
; but it does if there are side effects in an argument! |
(my-if (< X Y) |
(print "true") |
(print "false") |
) |
|
; so we have to defer the evaluation of arguments in an |
; if by wrapping them in lambda, and then evaluating only |
; the one selected by the condition. Note the ( ) around |
; the consequent and alternate closures to force evaluation |
|
; Note that the operations or/and must also be special forms |
; so somewhere there must be a built in conditional special |
; form that delays evaluation of its arguments. |
|
(define my-lazy-if |
(lambda (condition consequent-closure alternate-closure) |
(or (and condition (consequent-closure) ) (alternate-closure) ) |
) |
) |
|
; it doesn't matter here |
(my-lazy-if (< X Y) |
(lambda () (+ X X)) |
(lambda () (* Y Y)) |
) |
|
; but it does if there are side effects in an argument! |
(my-lazy-if (< X Y) |
(lambda () (print "true")) |
(lambda () (print "false")) |
) |
|
; lambda wrapping to create lazy evaluation is somewhat annoying. |
(my-lazy-if |
(< X Y) |
(lambda () |
(my-lazy-if (> X Y) (lambda () "cc true") (lambda () "cc false"))) |
(lambda () "c false") |
) |
|
; that is why it is built in to the special forms like if, and, or |
; using macros. |
|
; we can also introduce special syntax that does the closure construction |
; for us |
(define-syntax-rule (macro-if condition consequent alternate) |
((lambda (consequent-closure alternate-closure) |
(or (and condition (consequent-closure) ) (alternate-closure) ) |
) |
(lambda () consequent) |
(lambda () alternate) |
) |
) |
|
(macro-if #t (print 1) (print 0)) |
|
(define ifif (lambda (x) |
(macro-if (= x 0) (print "a") |
(macro-if (= x 1) (print "b") (print "c"))))) |
#lang racket |
(define TRUE (lambda (x y ) x)) |
(define FALSE (lambda (x y) y)) |
|
; with TRUE and FALSE defined this way, we actually can implement |
; if without cheating. Note how the logical value selects the |
; wrapping lambda to be evaluated, and then the evaluation occurs. |
|
(define-syntax-rule (lif condition consequent alternate) |
( (condition (lambda () consequent) (lambda () alternate)) ) ) |
|
; as well as the other logical operations. |
(define-syntax-rule (land a b) (a b a)) |
|
(define-syntax-rule (lor a b) (a a b)) |
|
(define-syntax-rule (lnot a) (a FALSE TRUE)) |
|
(define-syntax-rule (lxor a b) (a (b FALSE TRUE) b)) |
|
(lxor TRUE TRUE) |
(lxor TRUE TRUE) |
(define a TRUE) |
(define b FALSE) |
(lif (land a b) (print "1") (print "0")) |
|
|
; further exploting the macro capability we can introduce the |
; extended version of and and or that take 0 or more arguments and |
; short-circuit evaluation |
|
(define-syntax lland |
(syntax-rules () |
[(lland) TRUE] ; no arguments, TRUE is identity for and |
[(lland a) a] |
[(lland a b ...) (a (lland b ...) FALSE) ] |
)) |
|
(define-syntax llor |
(syntax-rules () |
[(llor) FALSE] |
[(llor a) a] |
[(llor a b ...) (a TRUE (llor b ...)) ] |
)) |
|
(lland TRUE FALSE TRUE) |
#lang racket |
|
; create some functions to track how often something |
; is used. It modifies a global state variable. |
|
(define global-eval-count 0) |
(define (reset-count) (set! global-eval-count 0)) |
(define (get-count) global-eval-count) |
(define (inc-count) |
(set! global-eval-count (+ 1 global-eval-count)) |
(displayln (format "eval count ~a" global-eval-count)) |
) |
|
; define syntax for weak delay of expr. Returns a |
; closure that when evaluated evaluates expr in the |
; original context. |
(define-syntax-rule (w-delay expr) |
(lambda () |
(inc-count) ; for tracing purposes only |
expr |
)) |
|
; define syntax for weak force of a delayed expr |
; simply evaluates the lambda |
(define-syntax-rule (w-force thunk) |
(thunk) |
) |
|
(define (test-it) |
(reset-count) |
(define v1 (w-delay (+ 40 2))) |
(displayln (w-force v1)) |
(displayln (w-force v1)) |
|
|
; f1 assumes that w-y is a delayed expression |
(define (f1 n w-y) |
(if (= n 0) 1 (+ (w-force w-y) 1))) |
|
(define x 0) |
(display "on 0: ") (displayln (f1 0 (w-delay (/ 1 x)))) |
|
(display "on 1: ") (displayln (f1 1 (w-delay (/ 1 x)))) |
) |
|
We say that
#lang racket |
; define syntax for weak delay of expr. Returns a |
; closure that when evaluated evaluates expr in the |
; original context. |
(define-syntax-rule (w-delay expr) |
(lambda () expr)) |
|
; define syntax for weak force of a delayed expr |
; simply evaluates the lambda |
(define-syntax-rule (w-force thunk) |
(thunk) ) |
|
; a double function that assumes its argument is w-delayed. |
(define (double-w w-x) |
(+ (w-force w-x) (w-force w-x))) |
|
; (w-double 42) |
; (w-double (w-delay 42)) |
|
; a double function that w-delays its result |
(define (w-double-w w-x) |
(w-delay (+ (w-force w-x) (w-force w-x)) ) ) |
|
; (define r (w-double-w (w-delay 42))) |
; (w-force r) |
|
|
; weak delay and force give you a kind of call by name behaviour, |
; as if you substituted the parameter into the code, while avoiding |
; any new lexical bindings. |
|
; note how this breaks referential transparency |
|
; a global state changing operation |
(define state 0) |
(define (next-state) |
(set! state (+ 1 state)) |
state) |
|
(define v1 (w-delay (next-state))) |
|
; every force gives a different value |
; (w-force v1)) |
; (w-force v1)) |
|
; and so does double |
; (double-w v1) |
|
; and things are really bad if you w-delay even further |
(define v2 (w-double-w v1)) |
|
; state |
; v2 |
; (w-force v2) |
; (next-state) |
; (w-force v2) |
|
#lang racket |
|
; the constructor for a counter object, returns a closure with |
; the initial value of the counter set to init. |
|
(define (create-counter init) |
(define counter init) |
|
; the lambda captures the local defintion of counter |
(lambda (op) |
(cond |
[ (eq? op 'get) counter ] |
[ (eq? op 'inc) |
(set! counter (+ 1 counter)) counter ] |
[ else (error (format "Invalid operation ~a" op) ) ] |
)) |
) |
|
; (define c (create-counter 43)) |
; (c 'get) |
; (c 'inc) |
; (c 'get) |
; (c 'dec) |
#lang racket |
|
; the constructor for a counter object, returns a closure with |
; the initial value of the counter set to init. |
|
(define (create-counter init) |
(define counter init) |
|
; the lambda captures the local defintion of counter |
(lambda args |
; if no param list, return the counter |
(if (null? args) counter |
(let ( [ op (first args) ] ) |
(cond |
[ (eq? op 'get) counter ] |
[ (eq? op 'inc) |
(set! counter (+ 1 counter)) counter ] |
[ (eq? op 'inc-by) |
(set! counter (+ (second args) counter)) counter ] |
[ else (error (format "Invalid operation ~a" op ) ) ] |
)) |
) |
)) |
|
; (define o1 (create-counter 0)) |
; |
; (o1 'inc) |
; (o1) |
; (o1 'inc-by 2) |
; (o1) |