--- title : Wyklad 11,12 --- Zmienna wolna i zamknięty term. Wszytkie zmienny, które nie są w let, są wolne. Czyli jeżeli nasz ewaluator napotka zmienną, to znaczy ża nasza zmienna jest wolna ```racket = (define (eval [e : Exp]) : Value (type-case Exp e [(numE n) (numV n)] [(trueE) (boolV #t)] [(falseE) (boolV #f)] [(opE o l r) ((op->proc o) (eval l) (eval r))] [(ifE b l r) (type-case Value (eval b) [(boolV v) (if v (eval l) (eval r))] [else (error 'eval "type error")])] [(varE x) (error 'eval "unbound variable")] )) ``` A dlaczego napotkanie zmiennej w ewaluatorze, będzie oznaczalo, że to jest zmienna wolna? Dlatego, że interpretacja let'a jest taka, że zmienną, którą on wiążę, zostanie zastąpione zamlniętym wyrażeniem. Patrzymy jak jest obliczany *let* ```racket= [(letE x e1 e2) (let ([v1 (eval e1)]) (eval (subst e2 x (value->exp v1))))] ``` Zauważymy tą *metacykliczność* : używam let'a, żeby zinterpretować let'a. Obliczamy wyrażenie e1. I to co teraz zrobimy z wyrażeniem e2, w którym wiemy, że *x* może mieć wolne wystąpienie. Cała rzecz, żeby miał :) Zastąpimy *x* wyrażeniem, które reprezentuje wartość e1. I jak to podstawienie się uda, to dostaniemy nowe wyrażenie, i to wyrażenie damy ewaluotorowi. 1) Dlaczego wykonujemy koercję (konwertowania) wartości do wyrażenia? *(value->exp v1)* Chcemy operować na składni. Nasz ewaluator liczy wartości wyrażeń, które są reprezentowanie typem **Value** Jak się doliczymy się do Value, to teraz potrzepujemy podstawienie. Ale my za zmienne w wyrażeniach będziemy podstawiać nowe wyrażenia, więc potrzebujemy przkształcić nasze wartości na Value. 2) Operacja podstawienia: e - wyrażenie za które będziemy podstawiać x - zmienna za którą bedziemy podstawiać a - wyrażenie, które bedziemy podstawiać ```racket= ;; precondition: a is a closed expression (define (subst [e : Exp] [x : Symbol] [a : Exp]) : Exp (type-case Exp e [(numE n) (numE n)] [(trueE) (trueE)] [(falseE) (falseE)] [(opE o l r) (opE o (subst l x a) (subst r x a))] [(ifE b l r) (ifE (subst b x a) (subst l x a) (subst r x a))] [(varE y) (if (eq? x y) a (varE y))] [(letE y e1 e2) (let ([e1-new (subst e1 x a)] [e2-new (if (eq? x y) e2 (subst e2 x a))]) (letE y e1-new e2-new))]) ``` * (varE y) Jeżeli x $\neq$ y, to znaczy, że y może występować w innym letcie, i nie dotykam tej zmiennej. * (letE y e1 e2) Ponieważ *y* (który tu wiążemy) nie jest widoczyny w *e1*, to shodzimy rekrencyjnie w e1, otrzymując jakieś nowe wyrażenie e1-new. Natomiast w e2, zaczynamy wchodzić pod nowy binder(wiązanie). I to co chcemy sprawdić czy x = y. To znaczy, żw wszystkie wystąpienia x w e2 nie są wolne, one są związane przez let'a ktorego teraz analizujemy Natomiast jesli $/neq$ to schodzimy sobie rekurencyjnie do e2 z tym podstawieniem *Kiedy może dojść do podstawienia?* Wtedy, gdy podstawiamy za zmienna, ktora ma w dannym wyrażeniu wolne wystąpienie > (subst (parse '{let x 2 {let x 1 x}}) 'x (parse '42)) > > (letE 'x (numE 2) (letE 'x (numE 1) (varE 'x))) Czyli podstawienie 42 zostało zignorowane. Tutaj *x* wolnych wystąpień nie posiada, dlatego że '{let x 2 {let x 1 **x**}}) 'x (parse '42) Za ten x moglibysmy cos wstawic. Ale ten x jest zwiazany prze wystapienie wiazace, jest chroniony przed podstawienie A tu *y* nie jest zwiazany: > (subst (parse '{let x 2 {let x 1 y}}) 'y (parse '42)) > >(letE 'x (numE 2) (letE 'x (numE 1) (numE 42))) ### Przechwycenia zmiennej > (subst (parse '{let x 1 y}) 'y (parse 'x)) (letE 'x (numE 1) (varE 'x)) Konstrukcja wiążąca zmienna przechwytuje do swojego zasięgu wiązania zmienną **x**, ktora w tym wyrazeniu, nie jest zwiazana. Operacja podstawienia przechodzi cale wyrazenie az to liści. To wyrażenie może być dowolne duże, i my musimy znależć gdzie są te zmiennem za które powinnismy podstawić. To jest operacja NIEEFEKTYWNA! Jak nie podstawienie to co? **Środowisko** ### Indeksy de'Brauna Liczymy stopień zagniezdzenia let'ów. Nie potrzebujemy juz nazw zmiennych A zmienna jest reprezentowana, jako konstruktor, ktory bierze ten adres (liczba). Czyli jak daleko jestem od mojego let'a? ## Domknięcia (reprezentacja procedur) Zamiast budować procedury, jako funkcje z meta języka, będziemy budować domknięcia. ```racket= (define-type Value (numV [n : Number]) (boolV [b : Boolean]) (funV [x : Symbol] [e : Exp] [env : Env])) ``` x - nazwa parametru formalnego e - ciało procedury env - środowisko w którym jest wywoływana funkcja Gdybyśmy tego środowiska tam nie dołożyli, to nasz interpreter implementowałby **"dynamiczne wiązanie zmiennych"**, czyli funkcja nie zapamietuje wartosci i siega po nie w momencie aplikacji. Jednak chcemy mieć **"statyczne wiązanie zmiennych"**, czyli Funkcja zapamiętuje wartości zmiennych wolnych z momentu swojej definicji, i te wartości wykorzystuje pózniej gdy jest aplikowana ##### Rekurencja Nie skończywszy danej definicji, już jej używamy. "Samoodniesienie" ```racket= (define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) ```