420 likes | 611 Views
Imperative Programming Chapter 6. Local State. Real world software like: Banks Course grading system Are state systems. i.e. they change along time : for example: current balance and current grade. What is Imperative Programming?.
E N D
Local State Real world software like: • Banks • Course grading system Are state systems. i.e. they change along time: for example: current balance and current grade.
What is Imperative Programming? “In computer science, imperative programming is a programming paradigm that describes computation in terms of statements that change a program state (Wikipedia)” Not better the FP! Just different
The Imperative Programming Model • Imperative programming is characterized by: • Understanding variables as storage places (addresses) • Mutation operations • Operational semantics
Mutation in Racket • Variable assignment: set! • Box type • Mutable data types (we will not use Racket’s built in, but create our own)
Variable Assignment Modification of value of a defined variable > (define my-list (list 1 2 3)) > my-list ’(1 2 3) > (set! my-list (list 1 2 4)) > my-list ’(1 2 4) > (set! a 4) . . set!: assignment disallowed; cannot set variable before its definition
The Box Type Values are references (or pointers) for values that can be changed during computation. • Constructor: box • Selector: unbox • Mutator: set-box!
Box Examples (1) (define a (box 7)) > (* 6 (unbox a)) 42 > a '#&7 > (set-box! a (+ (unbox a) 3)) > (* 6 (unbox a)) 60
Box Example (2): > (definecirc-l (list (box 1) (box 2) (box 3))) > circ-l '(#&1 #&2 #&3) > (unbox (car circ-l)) 1 > (unbox (cadr circ-l)) 2 > (unbox (caddr circ-l)) 3 > (set-box! (caddr circ-l) circ-l) > circ-l #0='(#&1 #&2 #�#) > (unbox (caddr circ-l)) #0='(#&1 #&2 #�#) > (eq? circ-l (unbox (caddr circ-l))) #t
The Box type The Box type: Type constructor: BOX(T ) procedure type name Value constructor: box [T → BOX(T )] Selector: unbox [BOX(T ) → T] Mutator: set-box! [BOX(T ) → Void] Identifier: box? [T → Boolean] Value equality: equal? [T1 ∗ T2 → Boolean] Identity equality: eq? [T1 ∗ T2 → Boolean]
Side Effects • Side effects are "non-functional"There existence does not agree with the FP paradigm • set! and set-box! are side effects procedure • We have already seen two kinds of side effect procedures: define and the display procedure. • Side effects procedures are applied for the sake of their side effects, and not for their returned value, void • In imperative programming, it is meaningful to use sequences of expressions
State based modeling • Defining objects with a modifiable private fields (local variables), and a set of accessors (selectors and mutators) that have controlled access to the object's private fields. • All objects may have access to shared (static) variables.
Bank Account Withdrawal Example ;; Signature: withdraw(amount) ;; Purpose: Withdraw ’amount’ from a private ’balance’. ;; Type: [Number -> Number] ;; Post-condition: result = if balance@pre >= amount ;; then balance@pre - amount ;; else balance (define withdraw (let ((balance 100)) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) (begin (display "Insufficient funds") (newline) balance)))))
> withdraw #<procedure:withdraw> > (withdraw 25) 75 > (withdraw 25) 50 > (withdraw 60) Insufficient funds 50 > (withdraw 15) 35 (define withdraw (let ((balance 100)) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) (begin (display "Insufficient funds") (newline) balance)))))
Full Bank Account Example ;; Signature: make-account(balance) ;; Type: [Number -> [Symbol -> [Number -> Number]]] (define make-account (λ (balance) (let ((balance balance)) (letrec ((get-balance (λ (amount) balance)) (set-balance! (λ (amount) (set! balance amount))) (withdraw (λ (amount) (if (>= balance amount) (begin (set-balance! (- balance amount)) balance) (begin (display "Insufficient funds") (newline) balance)))) (deposit (λ (amount) (set-balance! (+ balance amount)) balance)) (dispatch (λ (m) (cond ((eq? m ’balance) get-balance) ((eq? m ’withdraw) withdraw) ((eq? m ’deposit) deposit) (else (error ’make-account "Unknown request: ~s" m)))))) dispatch))))
> (define acc (make-account 100)) > acc #<procedure:dispatch> > ((acc ’withdraw) 50) 50 > ((acc ’withdraw) 60) Insufficient funds 50 > ((acc ’withdraw) 30) 20 > ((acc ’withdraw) 60) Insufficient funds 20 > (define acc2 (make-account 100)) > ((acc2 ’withdraw) 30) 70 > ((acc ’withdraw) 30) Insufficient funds 20
Mutable ADTs : Pair ; Type: [T1*T2 -> [Symbol -> Procedure]] ; The dispatch procedure returns procedures with different arities ; (Currying could help here). (define mpair (λ (x y) (letrec ((get-x (λ () x)) (get-y (λ () y)) (set-x! (λ (v) (set! x v))) (set-y! (λ (v) (set! y v))) (dispatch (lambda (m) (cond ((eq? m 'car) get-x) ((eq? m 'cdr) get-y) ((eq? m 'set-car!) set-x!) ((eq? m 'set-cdr!) set-y!) (else (error 'mpair "Undefined operation ~s" m)))))) dispatch)))
Mutable ADTs : Pair (define mutable-car (λ (z) ((z ’car)))) (define mutable-cdr (λ (z) ((z ’cdr)))) (define set-car! (lambda (z new-value) ((z ’set-car!) new-value))) (define set-cdr! (lambda (z new-value) ((z ’set-cdr!) new-value)))
Mutable ADTs : Pair > (define p1 (mpair 1 2)) > (mutable-car p1) 1 > (mutable-cdr p1) 2 > (set-car! p1 3) > (mutable-car p1) 3 > (set-cdr! p1 4) > (mutable-cdr p1) 4 > (mutable-cdr (set-cdr! p1 5)) . . procedure application: expected procedure, given: #<void>; arguments were: ’cdr
Lazy Pair (define lazy-mpair (λ (x y) (let ((x (box x)) (y (box y))) (λ (sel) (sel x y))))) (define lazy-mcar (λ (z) (z (λ (x y) (unbox x))))) (define lazy-mcdr (λ (z) (z (λ (x y) (unbox y))))) (define lazy-set-car! (λ (z new-value) (z (λ (x y) (set-box! x new-value))))) (define lazy-set-cdr! (λ (z new-value) (z (λ (x y) (set-box! y new-value)))))
Lazy Pair > (define p2 (lazy-mpair 1 2)) > (lazy-mcar p2) 1 > (lazy-mcdr p2) 2 > (lazy-set-car! p2 3) > (lazy-mcar p2) 3
Value Equality vs. Identity Equality Since we have states, we can check state equality or identity. • equal? – State (value) equality • eq? – Object equality
Value Equality vs. Identity Equality > (define x (list (box 'a) (box 'b))) > (define y (list (car x) (box 'c) (box 'd))) > (define z (list (box 'a) (box 'c) (box 'd)) ) > (define w y) > (eq? y w) ; identity #t > (eq? y z) #f > (equal? y z) ; value equality #t > (equal? (car z) (car x)) #t > (eq? (car z) (car x)) #f
Evaluation of letrec Recall the problem we had: (define fact (lambda (n) (let ((iter (lambda (c p) (if (= c 0) p (iter (- c 1) (* c p)))))) (iter n 1)))) Recursion call will not work
Evaluation of letrec Here’s a solution using imperative programming: (define fact (lambda (n) (let ((iter‘unassigned)) (set!iter (lambda (c p) (if (= c 0) p (iter (- c 1) (* c p))))) (iter n 1)))) Now iter is in the scope
Evaluation of letrec • Now we unveil the secret of letrec… • Its semantics cannot be explained in FP (let ((f1 ’unassigned) ... (fn ’unassigned)) (set! f1 lambda-exp1) ... (set! fn lambda-expn) e1 ... em))
Extending the Environment Model • The substitution model cannot support mutation • Semantics ofset-binding!(x,val,<f1,...,fn>) :
Extending the Environment Model • Add set! Special operator: env−eval[(set! x e),env] = set−binding!(x,env−eval[e,env],env) • Add Box type
Extending the Interpreter & Analyzer:Data Structures (define set-binding-in-env! (lambda (envvarval) (letrec ((defined-in-env (lambda (varenv) (if (empty-env? env) env (let ((b (get-value-of-variable (first-frame env) var))) (if (eq? b '_not-found) (defined-in-envvar (enclosing-envenv)) (first-boxed-frame env))))))) (let ((boxed-frame (defined-in-envvarenv))) (if (empty? boxed-frame) (error 'set! "variable not found") (let ((frame (unbox boxed-frame))) (set-box! boxed-frame (change-frame (make-binding varval) frame)))))))) Recursive call Creates a new frame by adding a new binding
Extending the Interpreter & Analyzer: Data Structures (define change-frame (lambda (binding frame) (let ((bvar (binding-variable binding)) (bval (binding-value binding))) (change-sub frame bvarbval)))) (define change-sub (lambda (sub var value) (let ((vars (get-variables sub)) (values (get-values sub))) (if (member varvars) (make-sub (cons varvars) (cons value values)) (error 'change-sub "substitution is not defined on variable")))))
Extending the Interpreter & Analyzer: Data Structures (define make-the-global-environment (lambda () (let* ((primitive-procedures (list (list 'car car) (list 'cdrcdr) ... (list 'box box) (list 'unboxunbox) (list 'box? box?) (list 'set-box! set-box!)...
Extending the Interpreter & Analyzer:ASP (define letrec->let (lambda (exp) (letrec ((make-body (lambda (varsvals) (if (null? vars) (letrec-body exp) (make-sequence (make-assignment (car vars) (car vals)) (make-body (cdrvars) (cdrvals)))))) (make-bindings (lambda (vars) (map (lambda (var) (list var ’unassigned)) vars)))) (let* ((vars (letrec-variables exp)) (vals (letrec-initial-values exp)) ) (make-let (make-bindings vars) (make-body varsvals)))))) (define make-sequence (lambda (exp1 exp2) (cons exp1 exp2))) (define make-assignment (lambda (variable value) (attach-tag (list variable value) 'set!))) Recursive call
Extending the Interpreter: env-eval (define eval-special-form (lambda (exp env) (cond ... ((assignment? exp) (eval-assignment exp env))))) (define eval-assignment (lambda (exp env) (set-binding-in-env! env (assignment-variable exp) (env-eval (assignment-value exp) env)) 'ok))
Extending the Analyzer (define analyze-assignment (lambda (exp) (let ((var (assignment-variable exp)) (val (analyze (assignment-value exp)))) (lambda (env) (set-binding-in-env! envvar (valenv)) ’ok))))
Type Checking • (set! x e) is well typed if e is well typed • Typing rule for set! : For every: type assignment TA, expression e, and type expression S: If Tenv|- e:S, Tenv|- x:S, Then Tenv |- (set! x e):Void
Type Checking • Axiom for box For every type environment Tenv and type expression S: Tenv|- box:[S -> BOX(S)] Tenv|- unbox:[BOX(S) -> S] Tenv|- set-box!:[BOX(S)*S -> Void] Tenv|- box?:[S -> Boolean] Tenv|- equal?:[BOX(S)*BOX(S) -> Boolean] Tenv|- eq?:[BOX(S)*BOX(S) -> Boolean]
Course summary 1/3 General concepts: • semantics, types • syntax, CFG(BNF), • operational semantics, static analysis, renaming, substitution, interpretation(evaluation) • polymorphic types, type safety
Course summary 2/3 functional programming model • function definition and application • special forms • derived expressions, ASP • apply-procedure, environments, analysis • high-order procedures • variable binding, lexical scoping (dynamic scoping) • static type inference • pattern matching
Course summary 3/3 logic programming model • axioms and queries • proof trees, unification, pruning imperative programming • mutable data types • state based execution of assignments • local state encapsulation Techniques: CPS, ADTs, Currying , Sequences, lazy lists