###### tags: `MP` # Wykład III ## Fragment do wykładu I tej tercji Wcześniej przy języku dotyczącym operacji arytmetycznych mieliśmy tak, że wyjątki zwracaliśmy "krokowo". Jak mamy interpreter w postaci maszyny abstrakcyjnej to ten interpreter był ogonowy, a nie zagnieżdzony, jak to było wcześniej. Ogonowy => wywołanie dalsze jest ostatnią rzeczą, którą robimy => jak napotkamy błąd to możemy od razu powiedzieć, że wynikiem działania całego programu jest błąd (wyrzucamy stos i kończymy działanie zwracając błąd), a nie, że musimy propagować błąd jak poprzednio ## Zejście poziom niżej z reprezentacją funkcji Ostatnio: funkcje na poziomie interpretera były reprezentowane przez funkcje (Value -> Value). **Jak miałaby wyglądać struktura danych (pierwszorzędowa + funkcja interpretująca) reprezentująca wszystko, co ta funkcja robi?** Jak do tej pory wyglądało: ```plait {lambda {x} e} env (funV (lambda (v) (eval e (extend-env env x v)))) ``` Chcemy to zareprezentować trójką [x, e, env]. Wtedy musimy zmienić apply: ```plait (define (apply [v1 : Value] [v2 : Value]) : Value (type-case Value v1 [(funV x b env) (eval b (extend-env env x v2))] [else ERROR])) ``` To jest już znacznie bliższe reprezentacji niskopoziomowej. Te dwa interpretery mogłyby być równoważne, ale pokazanie tego nie jest proste (gdy damy to policzenia funkcję w pierwszym przypadku dostaniemy funkcję a w drugim strukturę z jakimiś danymi). Pokazanie równoważności by wymagało opisania kiedy struktura (domknięcie) jest równoważne. **Ważne:** jeśli nie zadbamy o to, żeby wziąć odpowiednie środowisko, bo inaczej będzie dynamiczne typowanie zmiennych, i wynik funkcji zależy nie tylko od jej argumentów ale też momentu wywołania. Działają tak niektóre starsze dialekty Lispu, ponieważ na początku popełniono ten błąd i w tych dialektach to nadal zostało. ## Jak napisać silnię? Jak mamy `define` albo `letrec` to nie ma problemu. Czy umiemy napisać silnię bez tego? (rekursja bez rekursji) Użyjemy tych fanych aplikowanych do samych siebie lambd. ```racket (define (silnia n) (let ([fact (λ (f) (λ (n) (if (= n 0) 1 (* n ((f f) (- n 1))))))]) ((fact fact) n))) ``` Oczywiście zamiast define możemy użyć let, bo już w define nie korzystamy z rekursji. ## Nieskończona pętla: ```plait (let ([w (lambda (x) (x x))]) (w w)) ``` Samo w sobie nie jest użyteczne, ale można wykorzystać to. ## Operator punktu stałego Chcemy mieć taką funkcję `fix`, żeby `(fix f)` $\to$ `(f (fix f))`. ## Dokładamy `letrec` do naszego języka: Wcześniej używaliśmy rzeczy z naszego metajęzyka do wykonywania rzeczy z języka, który interpretujemy. ```plait env {letrec x e1 e2} (letrec ([v (eval e1 (extend-env env x v))]) ;to nie zadziala, trzebaby to schowac pod lambde (eval e2 (extend-env x v))) ``` Problem: mamy język gorliwy i liczymy wszystko, co się da (nawet gdy nie jest to nam potrzebne). Działa to: ```plait {letrec f {lambda {y} ... f ...}} ``` Ale nie działa to: ```plait {letrec x {+ x 1} env} ``` Jak naprawić? ```plait env {letrec x e1 e2} (letrec ([v (eval e1 (extend-env env x (lambda () v)))]) ;bezargumentowa lamda zwracajaca wartosc, zeby ta wartosc nie probowala sie liczyc wczesniej (eval e2 (extend-env x v))) ``` W ten sposób wyraziliśmy letrec'a z naszego języka letrec'iem z meta-języka. ## Można też zrobić letrec'a używając mutowalnego stanu