# Modeling Languages --- ## Languages are a solved problem - They were solved in the 2010s with the emergence of JavaScript and Rust and various frameworks * They were solved in the 2000s with the emergence of Python and perl and C# * They were solved in the 1990s with the emergence of C++ and Java. * They were solved in the 1980s with the emergence of C. We have C. We wrote Unix and Linux with C. We don't need anything else. * They were solved in the 1970s with the emergence of Pascal and Simula. Some really smart people told me that's all I would need to know an I learned them well. I never wrote a line of deliverable code in either language. * They were solved in the 1960s with the emergence of Lisp, COBOL and FORTRAN. We still use all three * They were solved in the 1950s with ENIAC and MANIAC and JONNIAC * They were solved in the 1930s with the Universal A-Machine and Lambda calculus. We still use both. Hopefully you get the picture --- ## Every application of any reasonable size has a built in customization language: - emacs - elisp - autocad - lisp - word - visual basic - excel - scripting language - unix - shells and shell scripts - browsers - HTML - all the things that read XML - gaming engines - protocols - yacc, lex, and friends - any program that reads a config file Studying languages makes us better programmers. Gives us insights into the code we write. Language is how we represent information and knowledge. ## How we think of languages When we learn or use languages as programmers, we tend to think about them in several ways: - syntax - behavior associated with syntax (semantics) - libraries - programming idioms Syntax is a solved problem and largely social. Syntax tells us very little about a language: ```text a [25] - Java access of array element 25 (vector-ref a 25) - Scheme access of vector element 25 a [25] - C access of array element 25 with no boundary checks a [25] - Haskell call of a on a list of length 1 ``` Syntax is a fickle friend. Libraries help us program, but actually can make language study harder. Idioms, like syntax are a study of sociological issues. Semantics - what programs mean - is what we're interested in. Precisely defining and implementing what a program means. ## Describing Meaning Describe the meaning of each syntactic element of a language. This is critical for developing compilers and interpreters. We have to know what a language is *supposed* to do before we can determine if our tools are implemented correctly. - Define the new language concrete syntax - Define the meaning of each syntactic element using a known language - Evaluation semantics tells us what it does (execution) - Static semantics tells us what we can predict (type checking) Three ways of doing this (EECS 762) - denotational - map each language structure to a mathematical function - operational - define how legal strings in a language are evaluated - axiomatic - define pre- and post-conditions on execution of language constructs We're going to do something very close to operational semantics - Shriram Krishnamurthi calls this *interpreter semantics* - where we define a golden interpreter for our language. This is how many languages include Verilog and OCaml are defined ## Compilers and Interpreters Two primary styles for language processing: - *Compilers* translate language structures into an executable form and throw the rest away - The *source* language is the language being translated - The *target* language is the language being targeted - *Interpreters* define a function that executes language syntax directly. - The *embedded* language is the language being interpreted - The *host* language is the known language defining the interpreter We will be building interpreters. Our *host* language will be Haskell while our *embedded* language will evolve over the course of the semester. Most real languages are neither purely interpreted or compiled. ## Defining Syntax _Programs are data structures._ ```haskell AE ::= num | AE + AE | AE - AE | (AE) ``` It's a set AE How big? - Infinite Recursive! Inductive Examples ```haskell 4 1 + 3 (2 + 2) + (5 - 7) 1 + 3 - (5 + (8 - 4)) ``` - *concrete syntax* - what programmers write - *abstract syntax* - interpreter operates over ```haskell data AE where Num :: Int -> AE Plus :: AE -> AE -> AE Minus :: AE -> AE -> AE deriving (Show,Eq) ``` `AE` - type name `Num`, `Plus`, ... - Constructors construct elements of the type. ALL elements of the type. `AE -> AE ...` - Signature We'll write an interpreter over AE This is not the standard syntax for a Haskell algebraic type, but instead uses the GADT form. It is equivalent to: ```haskell data AE = Num Int | Plus AE AE | Minus AE AE deriving (Show,Eq) ``` - *parser* - concrete syntax -> abstract syntax "1+3" == (Plus 1 3) ```haskell expr :: Parser AE expr = buildExpressionParser operators term operators = [ [ inFix "+" Plus AssocLeft , inFix "-" Minus AssocLeft ] ] numExpr :: Parser AE numExpr = do i <- integer lexer return (Num (fromInteger i)) term = parens lexer expr <|> numExpr -- Parser invocation parseAE = parseString expr ``` _Note_: This is not the abstract syntax we will actually use Examples ```haskell (parse "3") == (Num 3) (parse "3 + 4") == (Plus (Num 3) (Num 4)) (parse "((3 - 4) + 7)" == (Plus (Minus (Num 3) (Num 4)) (Num 7)) ``` Parsers are solved problems and this is the last we will speak of them in detail. We're going to skip the parser because the abstract syntax will be as easy to read as the concrete syntax. What a bonus. # Interpreters --- ## Monadic Interpreters _We will learn about languages by building interpreters for them in Haskell_ The general notion of an interpreter maps a _language_ to a _value_. Mathematically: $E: L\rightarrow V$ $E$ is our interpreter $L$ is our language and $V$ is our value. Values are good results. Cannot be evaluated further. Let's start with the simplest language ever: ```haskell AE ::= num ``` ```haskell data AE where Nat :: Int -> AE (deriving Eq,Show) ``` `Nat` - constructor ```haskell eval :: AE -> Int ``` A parser will translate numbers into `AE`: ! - Bang ? - Hook `*` - Splat #! - Shebang ```haskell parse "1" == (Nat 1) parse "2" == (Nat 2) parse "a" == ! parse "1+2" == ! ``` An interpreter will translate `AE` into values: ```haskell eval::AE -> Int eval (Nat x) = x ``` all together now: ```haskell interp x = eval (parse x) interp "1" == 1 interp "3" == 3 ``` or ```haskell interp = eval . parse ``` This is goofy. In and out and that's it. Now let's add addition to our language. Just another term: ```haskell data AE where Nat :: Int -> AE Plus :: AE -> AE -> AE (deriving Eq,Show) ``` This is not much harder: ```haskell eval::AE -> Int eval (Nat x) = x eval (Plus x y) = (eval x) + (eval y) ``` `x` and `y` in `Plus` bound to input arguments. ```haskell eval (Plus (Nat 1) (Nat 3)) == (eval (Nat 1)) + (eval (Nat 3)) == 1 + 3 == 4 ``` Do programs in AE terminate? Yes and that's okay. Do programs in AE ever crash? No But you can't do anything powerful. Let's add another operator, `Minus` ```haskell data AE where Nat :: Int -> AE Plus :: AE -> AE -> AE Minus :: AE -> AE -> AE ``` and extend `eval` with a new case: ```haskell eval::AE -> Int eval (Nat x) = x eval (Plus l r) = (eval l) + (eval r) eval (Minus l r) = (eval l) - (eval r) --- Not good - could be negative ``` What does `Minus` force us to deal with? - Errors Simple error handling using `error`: ```haskell eval (Minus l r) = let x = (eval l) - (eval r) in if x<0 then error "!" else x ``` What if I don't want to crash? Return an error value: ```haskell eval (Minus l r) = let x = (eval l) - (eval 2) in if x < 0 then -1 else x ``` Whatever we choose, it must be of type `int`. Why is that a problem? Magic Value like `-1`, but easily introduces errors. ## Maybe Two constructors: - `Just x` - where `x` is the result of a computation - `Nothing` - is not the result of a successful computation `Maybe` is parameterized over type: ```haskell data Maybe A = Just :: A -> Maybe A Nothing :: Maybe A ``` What does `Maybe` do to `A`? Using the `Maybe` in a traditional way: ```haskell eval::AE -> (Maybe Int) eval (Nat x) = Just x eval (Plus l r) = case (eval l) Nothing -> Nothing (Just l') -> case (eval r) (Just r') -> (Just l'+r') Nothing -> Nothing eval (Minus l r) = case (eval l) Nothing -> Nothing (Just l') -> case (eval r) Nothing -> Nothing (Just r') -> if (l'<r') then Nothing else Just l'-r' ``` ((2-3) + 4) How does `Maybe` help here? ## Maybe the Monad Using `Maybe` as a Monad ```haskell eval::AE -> Maybe Int eval (Nat x) = Just x eval (Plus l r) = do { x <- eval l; y <- eval r; Just (x+y) } eval (Minus l r) = do { x <- eval l; y <- eval r; if x<y then Nothing else Just x-y} ``` `x <- e` is called *bind* and we're binding the result of evaluating `e` to `x`. Bind only works when `e` is a monadic data structure. The bind arrow does this with `Maybe`: 1. Evaluates the right side 2. If the right side is `Just a`, assign `a` to `x` and go to the next line 3. If the right side is `Nothing`, fall through and return `Nothing` `return a` == `Just a` ```haskell eval::AE -> Maybe Int eval (Nat x) = return x eval (Plus l r) = do { x <- eval l; y <- eval r; return (x+y) } eval (Minus l r) = do { x <- eval l; y <- eval r; if x<y then Nothing else return x-y} ``` This is pretty cool. The Monad and the `do` notation capture the shunting of control around a case when `Nothing` appears. We don't have to worry about it anymore. It gets cooler: ```haskell eval :: AE -> Maybe AE ``` What changed? ```haskell eval (Nat x) = return (Nat x) eval (Plus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; return (Nat x+y)} eval (Minus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; if x<y then Nothing else return (Nat x-y)} ``` Pattern matching _inside the bind_. This will come in very handy later, but file it away for now. ```haskell (Boolean Bool) ``` If patterns do not match, then `Nothing` is returned. ---- ## Language Properties - Completeness - every wff that we put into `eval` will get evaluated - Determinicity - every wff we put into `eval` will produce only one value - Normalizing - every wff we put into `eval` will terminate in a value - Value - a good computation result wff - Well Formed Formula ("woof") ## Inference Rules and Axioms - Axioms - Things we know. Givens. - Inference Rules - Things we can deduce from what we know in 1 step - Derivations - Sequences of inference rule applications. An inference rule is a set of _antecedents_ and a _consequent_. If the antecedents are true, the consequent follows immediately: $\begin{prooftree}\AXC{\(A\)}\AXC{\(B\)}\RLS{Inference Rule}\BIC{\(C\)}\end{prooftree}$ Means *if we know $A$ and we know $B$ then we know $C$* An _axiom_ is an inference rule with no antecedents. $\begin{prooftree}\AXC{}\RLS{Axiom}\UIC{\(A\)}\end{prooftree}$ Means *if we know nothing then we know $A$*. So $A$ is always try with no need for proof. We can define all kinds of things with inference rules: $\begin{prooftree}\AXC{\(t_1\in L\)}\AXC{\(t_2\in L\)}\RLS{Syntax}\BIC{\(t_1\)+``-''+\(t_2\in L\)} \end{prooftree}$ t ::= Nat | t1-t2 $\begin{prooftree}\AXC{\(A\Rightarrow B\)}\AXC{\(A\)}\RLS{Logic}\BIC{\(B\)}\end{prooftree}$ $\begin{prooftree}\AXC{\(A\wedge B\)}\RLS{Logic}\UIC{\(B\)}\end{prooftree}$ And we can build trees that define proofs. $\begin{prooftree} \AXC{\(A\Rightarrow B\)} \AXC{\(A\wedge A\)}\UIC{\(A\)}\RLS{Proofs} \BIC{\(B\)} \end{prooftree}$ Hilbert defined this system to define all of mathematics. It did not go well. However, Hilbert Systems and inference rules are pretty cool creatures will great utility We will use them to define languages mathematically where they are the dominant definition mechanism. First let's define some notational conventions: - $v$ is a variable representing _values_ - $t$ is a variable representing _terms_ - $\underline{+}$ is an operation in our concrete syntax while $+$ is an operation in Haskell $t_1\Downarrow t_2$ is an evaluation relation and is read "$t_1$ evaluates to $t_2$ in one step" The Haskell function we've defined called `eval` corresponds with $\Downarrow$ - `eval` is a function, not a relation - $t_1\Downarrow t_2$ == `eval t1 = t2` Let's walk through some inference rules for our first little language AE: Values evaluate to themselves. Note the underline. $\begin{prooftree}\AXC{}\RLS{NumE}\UIC{\(\underline{v} \Downarrow v\)}\end{prooftree}$ Addition in AE is addition in Haskell. $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\RLS{PlusE}\BIC{\(t_1 \underline{+} t_2 \Downarrow v_1+v_2\)}\end{prooftree}$ If $t_1$ evaluates to $v_1$ and $t_2$ evaluates to $v_2$, then $t_1+t_2$ evaluates to $v_1+v_2$. $3\underline{+}5\Downarrow 3+5$ $3\Downarrow 3$ $5\Downarrow 5$ $8$ Subtraction in AE is subtraction in Haskell $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\RLS{MinusE}\BIC{\(t_1 \underline{-} t_2 \Downarrow v_1-v_2\)}\end{prooftree}$ If $t_1$ evaluates to $v_1$ and $t_2$ evaluates to $v_2$, then $t_1-t_2$ evaluates to $v_1-v_2$. But should it be? What does this say? $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\AXC{\(v_1\geq v_2\)}\RLS{MinusE+}\TIC{\(t_1 \underline{-} t_2 \Downarrow v_1-v_2\)}\end{prooftree}$ What happens if we add this rule to what we already have? $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\AXC{\(v_1 < v_2\)}\RLS{MinusEZero}\TIC{\(t_1 \underline{-} t_2 \Downarrow 0\)}\end{prooftree}$ Another alternative. $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\AXC{\(v_1 < v_2\)}\RLS{MinusEBottom}\TIC{\(t_1 \underline{-} t_2 \Downarrow \bot\)}\end{prooftree}$ This definitional style is called _Big Step Semantics_ or _Natural Semantics_ But there's more: We can define evaluation with our rules: $\begin{prooftree}\AXC{\(\underline{5}\Downarrow 5\)}\AXC{\(\underline{2}\Downarrow 2\)}\RLS{PlusE}\BIC{\(\underline{5+2} \Downarrow 5+2 \)}\AXC{\(\underline{3} \Downarrow 3\)}\RLS{PlusE}\BIC{\(\underline{5 + 2+ 3} \Downarrow 10\)}\end{prooftree}$ So is this a proof or an evaluation? ## Our First Language A complete definition that allows: - Parsing - Evaluation - Reasoning ### Concrete Syntax ```other AE ::= num | AE + AE | AE - AE | (AE) ``` ### Inference Rules $\begin{prooftree}\AXC{}\RLS{NumE}\UIC{\(\underline{v} \Downarrow v\)}\end{prooftree}$ $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\RLS{PlusE}\BIC{\(t_1 \underline{+} t_2 \Downarrow v_1+v_2\)}\end{prooftree}$ $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\AXC{\(v_1\geq v_2\)}\RLS{MinusE+}\TIC{\(t_1 \underline{-} t_2 \Downarrow v_1-v_2\)}\end{prooftree}$ $\begin{prooftree}\AXC{\(t_1 \Downarrow v_1\)}\AXC{\(t_2 \Downarrow v_2\)}\AXC{\(v_1 < v_2\)}\RLS{MinusEBottom}\TIC{\(t_1 \underline{-} t_2 \Downarrow \bot\)}\end{prooftree}$ ### Abstract Syntax ```haskell data AE = Nat Int | Plus AE AE | Minus AE AE deriving (Show,Eq) ``` ### Interpreter ```haskell eval (Nat x) = return (Nat x) eval (Plus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; return (Nat x+y)} eval (Minus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; if x<y then Nothing else return (Nat x-y)} ``` ## Adding Booleans to AE Definition of ABE is AE with Booleans added: ```other ABE ::= Nat | ABE + ABE | ABE - ABE | (ABE) | true | false | if ABE then ABE else ABE | ABE <= ABE | ABE && ABE | isZero ABE v ::= Nat | true | false Nat ::= 0 | succ Nat ``` And the abstract syntax. ```haskell data ABE where Num :: Int -> ABE Plus :: ABE -> ABE -> ABE Minus :: ABE -> ABE -> ABE Boolean :: Bool -> ABE And :: ABE -> ABE -> ABE Leq :: ABE -> ABE -> ABE IsZero :: ABE -> ABE If :: ABE -> ABE -> ABE -> ABE deriving (Show,Eq) ``` What changed? This is Project 0. # Adding Identifiers --- ## bind and identifiers Things we need to do: 1. Concrete Syntax (`t::=t...`) 2. Inference Rules (antecedents and consequents) 3. Abstract Syntax (`data ABE ...`) 4. Interpreter (`eval t`) `bind` Creates a _binding_ between an _identifier_ and _value_. Normally called `let`. Some examples to ponder when defining a variable using `bind`. ```other bind x=5 in x+x == 5+5 == 10 ``` ```other bind x=5 in bind y=6 in x+y == bind y=6 in 5+y == 5+6 == 11 ``` ```other bind x=5 in bind x=6 in x+x == bind x=6 in x+x == 6+6 == 12 ``` ```other bind x=5 in bind x=6+x in x+x == bind x=6+5 in x+x == bind x=11 in x+x == 11+11 == 22 ``` ```other bind x=5 in x + bind y=6 in x+y == 5 + bind y=6 in 5+y == 5 + 5 + 6 == 5 + 11 == 16 ``` ```other bind x=5 in x + bind x=6 in x+x == 5 + bind x=6 in x+x == 5 + 6 + 6 == 5 + 12 == 17 ``` ```other bind x=5 in x + y == 5 + y == BOOM ``` ```other bind x=x+1 in x == BOOM ``` ## Concrete Syntax ``` BAE ::= num | BAE + BAE | BAE - BAE | (BAE) | bind ID = BAE in BAE | ID ID ::= string ``` ### Useful Definitions - instance - occurrence of an identifier ```other bind ->x = ->x+5 in ->y-4 ``` - binding instance - where an identifier is declared and given a value ```other bind ->x = x+5 in y-4 ``` - bound value - value given to an identifier in a binding instance ```other bind x = ->(x+5) in x-4 ``` - scope - the region where an identifier is defined and can be used ```other bind x = x+5 in [x-4] ``` - bound instance - where an identifier is used _in scope_ ```other bind x = x+5 in [->x-4] ``` - free instance - where an identifier is used _outside scope_ ```other bind x = ->x+5 in x-4 ``` ```other bind x=5 in bind y=6 in x+y+z ``` What is the scope for a variable defined with `bind`? Everything after `in` ## Inference Rules for `bind` and Identifiers First definition will use substitution. ### Substitution operator - $[x \rightarrow v]t$ - Replace all _free_ instances of $x$ in $t$ with $v$ - $[x\rightarrow 5]3 == 3$ - $[x\rightarrow 5]x == 5$ - $[x\rightarrow 5]5+5 ==$ - $[x\rightarrow 7]
bind x=7 in bind y=5 in` $x+y$ $==
bind x=7 in bind y=5 in` $x+y$ - $[x\rightarrow 7]
bind y=5 in` $x+y$ $==$ `bind y=5 in` $7+y$ - $[x\rightarrow 5]
x + bind x=10 in x` $==$ `5 + bind x=10 in x` Substitution is a common mathematical operator that we will assume exists. ### Inference Rules - `bind` $\begin{prooftree} \AXC{\(a\Downarrow v_a\)}\AXC{\([i\rightarrow v_a]s\Downarrow v_s\)}\RLS{BindE} \BIC{\(\mathsf{bind}\ i=a\ \mathsf{in}\ s\Downarrow v_s\)} \end{prooftree}$ `bind x=3+2 in x+x` #### Explanation - $a$ evaluates to $v_a$ - substitute $v_a$ for $i$ in the body of `bind`, evaluate to $v_s$ - Result is $v_s$ $\begin{prooftree} \AXC{\(a\Downarrow v_a\)}\AXC{\([i\rightarrow v_a]s\Downarrow v_s\)}\RLS{BindE} \BIC{\(\mathsf{bind}\ i=a\ \mathsf{in}\ s\Downarrow v_s\)} \end{prooftree}$ ### Inference Rules - Identifiers $\begin{prooftree} \AXC{}\RLS{IDE} \UIC{\(x\Downarrow\bot\)} \end{prooftree}$ #### Explanation - Evaluating an identifier means the identifier was not replaced - No `bind` defined the identifier or it would no longer be there ``` bind x = 3 in y == ??? ``` ## Abstract Syntax Add constructors for new constructs in concrete syntax ```haskell data AE where Nat :: Int -> AE ID :: String -> AE Plus :: AE -> AE -> AE Minus :: AE -> AE -> AE Bind :: String -> AE -> AE -> AE deriving (Show,Eq) ``` ## Evaluation ```haskell eval (Nat x) = return (Nat x) eval (Id s) = Nothing eval (Plus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; return (Nat x+y)} eval (Minus l r) = do { (Nat x) <- eval l; (Nat y) <- eval r; if x<y then Nothing else return (Nat x-y)} eval (Bind i a b) = do { a' <- eval a; Just (eval (subst i a' b))} ``` We will need to define substitution for this to work: ```haskell subst :: String -> BAE -> BAE -> BAE subst x v (Nat x) = (Nat x) subst x v (Id x') = if x==x' then v else (Id x') subst x v (Plus l r) = (Plus (subst x v l) (subst x v r)) subst x v (Minus l r) = (Minus (subst x v l) (subst x v r)) subst x v (Bind x' v' t') = if x==x' then (Bind x' v' t') else (Bind x' v' (subst x v t')) ``` ```haskell eval (Bind “x” (Num 5) (Bind “y” (Num 6) (Plus (Id “x”) (Id “y”)))) == (Bind "y" (Num 6) (Plus (Num 5) (Id "y"))) == (Plus (Nun 5) (Num 6)) == (Num 11) ``` ```haskell eval (Bind “x” (Num 5) (Plus (Id “x”) (Id “x”)))) == (Plus (Num 5) (Num 5)) == (Num 10) ``` ```haskell eval (Bind “x” (Num 5) (Bind “x” (Num 6) (Plus (Id “x”) (Id “x”)))) == (Bind "x" (Num 6) (Plus (Id "x") (Id "x"))) == (Plus (Num 6) (Id "x")) == (Num 12) ``` ```haskell eval (Bind “x” (Num 5) (Bind “y” (Num 5) (Bind “x” (Num 6) (Bind “y” (Num 4) (Bind “z” (Num 7) (Id “z”)))))) == ``` Anything wrong here? This is a _reference interpreter_ that defines what we want BAE to be, but not necessarily its implementation. - Natural language - Inference rules - Reference interpreter # Deferring Substitution How would you build this interpreter "for real"? Instead of immediately substituting, let's remember bindings of identifiers to values. `[("x",5),("y",7)]` Old Way: ```other eval bind x = 5 in bind y = 6 in x + y == bind y=6 in 5+y == 5+6 == 11 ``` New Way: ```other eval bind x = 5 in [("x",5)] -- "x" is 5 in this scope bind y = 6 in [("x",5),("y",6)] 5 + 6 [("x",5),("y",6)] == 11 ``` - Environment - list of identifiers and values currently in scope ```haskell eval (Bind “x” (Num 5) [("x",5)] (Bind “y” (Num 6) [("x",5),("y",6)] (Plus (Id "x") (Id "y")))) [("x",5),("y",6)] ``` ```haskell eval (Bind “x” (Num 5) [("x",5)] -- Environment (Bind “x” (Num 6) [("x",6),("x",5)] -- Shadowing "x" (Plus 6 6)) [("x",6),("x",5)] ``` ```haskell eval (Bind “x” (Num 5) [("x",5)] (Bind “x” (Num 6) [("x",6),("x",5)] (Plus 6 (Id "y")))) [("x",6),("x",5)] -- Lookup of y fails == BOOM ``` ```haskell eval (Bind “x” (Num 5) [("x",5)] (Plus 5 [("x",5)] (Bind “x” (Num 6) [("x",6)("x",5)] (Plus 6+6))) [("x",6)("x",5)] == 17 ``` ```haskell eval (Bind “x” (Num 5) [("x",5)] (Plus (Bind “x” (Num 6) [("x",6),("x",5),] (Plus 6 6)) [("x",6),("x",5)] 5) [("x",5)] == 17 ``` Now more inference rules? $\begin{prooftree} \AXC{}\AXC{} \BIC{\(\mathsf{bind}\; x=v\;\mathsf{in}\ t\Downarrow v\)} \end{prooftree}$ Why or why not? ## eval with env Haskell built-ins cool functions: ```haskell Type Env = [(String,BAE)] lookup :: A -> [(A,B)] -> Maybe B x:xs -- Adds x to front of xs -- Pattern matches with non-empty list (that's kinda cool) ``` Let's define `eval`: `return` == `Just` ```haskell eval :: Env -> BAE -> Maybe BAE eval _ (Num n) = Just (Num n) eval e (Plus l r) = do { (Num l') <- (eval e l) ; (Nun r') <- (eval e r) ; return (l'+r') } eval e (Minus l r) = do { (Num l') <- (eval e l) ; (Nun r') <- (eval e r) ; if r'<=l' thern return (l'-r') else Nothing } eval e (Bind x a s) = do { a' <- (eval e a) ; (eval (x,a'):e s) } eval e (Id x) = (lookup x e) ``` Good things about `eval` using deferred substitution: - No more `subst` - No more walking the code We will say our original eval function performs _direct substitution_ and will use the name `evals` We will say our new eval function performs _deferred substitution_ and will us the name `eval` How do we know our new `eval` is correct? $\forall t:BAE\cdot \mathsf{eval}\; []\; t = \mathsf{evals}\; t$ What does this say? Can we do it? # Predicting Behavior Start with a tiny language with representative terms: ```haskell data BAE where | Nat :: Int -> BAE EOVal -> BAE | Plus :: BAE -> BAE -> BAE | Bind :: String -> BAE -> BAE -> BAE ``` ## Abstract Interpretation Can we predict when a returned value is odd or even? ```haskell data EOVal where | Even | Odd ``` ```haskell predict :: [("String",EOVal)] -> BAE -> Maybe EOVal predict _ (Nat n) = return (if even n then Even else Odd) -- Just Even -> n is even return n predict c (Plus l r) = do { l' <- predict c l ; r' <- predict c r ; return (if (l'==r') then Even else Odd) } predict c (Bind x a s) = do { a' <- (eval c a) ; (predict (x,a'):c s) } predict c (Bind x a s) = -- c is the environment predict c (Id x) = (lookup x c) ``` The `predict` function implements _abstract interpretation_ that makes predictions without running code. Let's add some new structures to our language and look at a different kind of prediction: ```haskell data BAE where | Nat :: Int -> BAE | Boolean :: Bool -> BAE | Plus :: BAE -> BAE -> BAE | And :: BAE -> BAE -> BAE | If :: BAE -> BAE -> BAE -> BAE | Bind :: String -> BAE -> BAE -> BAE | Id :: String -> BAE deriving (Show,Eq) ``` Instead of `Odd` and `Even` let's use `TNum` and `TBool` for numbers and booleans: ```haskell data TBAE where | TNum | TBool ``` Can we calculate the number-ness or boolean-ness of a term? ```haskell predict :: [(String,TVal)] -> BAE -> Maybe TVal predict _ (Nat _) = return TNum predict _ (Boolean _) = return TBool predict g (Plus l r) = do { TNum <- predict g l ; TNum <- predict g r ; return TNum } predict g (And l r) = do { TBool <- predict g l ; TBool <- predict g r ; return TBool } predict g (If c t e) = do { TBool <- predict g c ; t' <- predict g t ; e' <- predict g e ; if t'==e' then return t' else Nothing } predict g (Bind x a s) = do { a' <- (predict g a) ; (predict (x,a'):g s) } predict g (Id x) = (lookup x g) ``` What is `predict` in this case? ## Type Checking As you might have guessed we just wrote a type checker for our little language. Lets formalize this idea using inference rules. First, a few definitions: - $x : T$ - typing relation saying `x` is of type `T`. Just like $x\Downarrow t$ $ \begin{prooftree} \AXC{\(\)}\RLS{NumT} \UIC{\(N:TNum\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(l:TNum\)}\AXC{\(r:TNum\)}\RLS{PlusT} \BIC{\(l+r : TNum\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(TBool\)}\AXC{\(TBool\)}\RLS{AndT} \BIC{\(l \wedge r : TBool\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(c:TBool\)}\AXC{\(t:T\)}\AXC{\(e:T\)}\RLS{IfT} \TIC{\(if\ c\ then\ t\ else\ e : T\)} \end{prooftree} $ - _context_ - a list of identifiers and _types_ in scope represented by $\Gamma$ - $\Gamma\vdash x:T$ - Gamma derives $x$ type $T$ given context $\Gamma$. _Context_ is the same as _Environment_ except it contains types. $ \begin{prooftree} \AXC{\((x:T)\in \Gamma\)}\RLS{Name} \UIC{\(\Gamma\vdash x : T\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(\Gamma\vdash a:T_a\)}\AXC{\((x:T_a):\Gamma\vdash t:T\)}\RLS{BindT} \BIC{\(bind\ x=a\ in\ t : T\)} \end{prooftree} $ ## Optimization We know several identities over our calclulations: - $0+x==x$ - $True \wedge x == x$ - `if` $True$ `then` $t$ `else` $e == t$ Can we optimize such expressions out of an program? ```haskell optimize :: BAE -> BAE optimize (Nat n) = (Nat n) optimize (Boolean b) = (Boolean b) optimize (Plus l r) = if (optimize l)==(Nat 0) then (optimize r) else (Plus (optimize l) (optimize r)) optimize (And l r) = optimize (If c t e) = if c==(Boolean True) then (optimize t) else (If (optimize c) (optimize t) (optimize e)) optimize (Bind x a s) = (Bind x (optimize a) (optimize s)) optimize (Id x) = (Id x) ``` ## General Purpose Evaluators Let's change our data structure just a bit: ```haskell data BAE A where | Nat :: A -> BAE A | Plus :: BAE A -> BAE A -> BAE A | Bind :: String -> BAE A -> BAE A -> BAE A | Id :: String -> BAE A ``` ``` predict :: ??? predict _ (Nat n) = predict c (Plus l r) = predict c (Bind x a s) = ``` # Lambda The Ultimate! Now things get serious... ```other inc x = x + 1 ``` ```other inc 3 == 3 + 1 == 4 ``` ```other inc ((5+1)-3) == ((5+1)-3)+1 == 4 or == inc 3 == 3+1 == 4 ``` Some nifty definitions: `inc x = x + 1` - `x` - formal parameter - `x + 1` - body ```other inc 3 ``` - `3` - actual parameter - `inc 3` - application of `inc` to `3` How do we evaluate function application? - $inc\ 3 == [x\rightarrow 3]x+1$ ## Kinds of Functions in Languages - First order functions - Cannot take other functions as arguments. Have special representation in the language that is not accessible ``` int foo(int x){x++}; ``` - Higher order functions - Can take functions as arguments. Can return functions. ```other map foo [1,2,3] = [2,3,4] ``` - First class functions - Functions are values like any other value in the language ```haskell foo = \x -> x+1 foo x = x +1 ``` - We will look at languages with first-class functions - First-class functions are the current trajectory of modern language - ... it's been a long time coming. ## Concrete Syntax - `lambda x in x + x` - Defines a function value over _formal parameter_ `x` - Called a `lambda` or an `abstraction`. - Lambdas are values - From a logical perspective, `lambda` introduces a variable - `(l a)` - Applies a function `l` to _actual paramater_ or _argument_ `a` - Called an _application_ or simply an _app_ - From a logical perspective, `app` eliminates a variable. ```other ((lambda x in x+x) 3) == 3+3 == 6 ``` Have you seen this kind of evaluation before? ``` ((lambda x in x+x) 3) == bind x=3 in x+x ``` ```other FBAE ::= V|FBAE+FBAE|FBAE-FBAE |bind id = FBAE in FBAE |lambda id in FBAE |(FBAE FBAE) |id V ::= Nat|lambda id in FBAE ``` ``` (lambda x in x) == (lambda x in x) ``` ``` ((lambda x in x) 5) == [x->5]x == 5 ``` ``` ((lambda x in x) (lambda x in x)) == [x->(lambda x in x)]x == (lambda x in x) ``` ``` ((((lambda x in (lambda y in x+y)) 3) 2) == ([x->3](lambda y in x+y) 2) == ([y->2](3+y)) == 3+2 == 5 x->3 (lambda y in x+y) y x+y (lambda x y z ...)== (lambda x in (lambda y in (lambda z in ...))) ``` ``` (((lambda x in (lambda y in x+y)) 1) == [x->1](lambda y in x+y) == (lambda y in 1+y) Nat->Nat->Nat Nat->Nat ``` ``` ((lambda x in (x 3)) (lambda x in x)) == [x->(lambda x in x)](x 3) == ((lambda x in x) 3) == [x->3]x == 3 ``` ``` bind inc=(lambda x in x+1) in (inc 3) == [inc->(lambda x in x+1)](inc 3) == ((lambda x in x+1) 3) ... == 3+1 == 4 ``` ``` bind inc=(lambda x in x+1) in bind dec=(lambda x in x-1) in bind sqr=(lambda x in x*x) in (inc (sqr (sqr 3))) ... ``` Alternate concrete syntax: $\lambda x.s$ More common in mathematical presentations ## Abstract Syntax ```haskell data FBAE where | Num :: Int -> FBAE | Plus :: FBAE -> FBAE -> FBAE | Minus :: FBAE -> FBAE -> FBAE | Bind :: String -> FBAE -> FBAE -> FBAE | Lambda :: String -> FBAE -> FBAE | App :: -> FBAE -> FBAE -> FBAE | Id :: String -> FBAE deriving (Show,Eq) ``` ## Inference Rules $ \begin{prooftree} \AXC{\(f\Downarrow (\mathsf{lambda}\ i\ \mathsf{in}\ s)\)} \AXC{\(a\Downarrow v_a\)} \AXC{\([i\rightarrow v_a]s\Downarrow v_s\)}\RLS{Beta} \TIC{\((f\ a)\Downarrow v_s\)} \end{prooftree} $ $ \begin{prooftree} \AXC{}\RLS{LambdaE} \UIC{\(\mathsf{lambda}\ i\ \mathsf{in}\ s\Downarrow \mathsf{lambda}\ i\ \mathsf{in}\ s\)} \end{prooftree} $ Remember bind? $\begin{prooftree} \AXC{\(a\Downarrow v_a\)}\AXC{\([i\rightarrow v_a]s\Downarrow v_s\)}\RLS{BindE} \BIC{\(\mathsf{bind}\ i=a\ \mathsf{in}\ s\Downarrow v_s\)} \end{prooftree}$ What's the difference here? We can define `bind` using `lambda` ``` bind i=a in s == ((lambda i in s) a) ``` - This is called a _derived form_ ## Church's Lambda Calculus - Can represent _any_ computable function - Equivalent to Turing Machines - Basis for functional programming - Turing Machines give rise to _imperative programming_ - Lambda Calculus gives rise to _functional programming_ ### Concrete Syntax ```other LC ::= |id |lambda id in LC |(LC LC) V ::= lambda id in LC ``` $ \begin{prooftree} \AXC{\(f\Downarrow \mathsf{lambda}\ i\ \mathsf{in}\ s\)} \AXC{\(a\Downarrow v_a\)} \AXC{\([i\rightarrow v_a]s\Downarrow v_s\)}\RLS{CBV Beta} \TIC{\((f\ a)\Downarrow v_s\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(f\Downarrow \mathsf{lambda}\ i\ \mathsf{in}\ s\)} \AXC{\([i\rightarrow a]s\Downarrow v_s\)}\RLS{CBN Beta} \BIC{\((f\ a)\Downarrow v_s\)} \end{prooftree} $ That's it! ### Fun with Lambda ``` (lambda x in x) is a value ``` ``` (lambda y in y)(lambda x in x) == (lambda x in x) ``` ``` (lambda x in x x) 3 == [x->3](x x) == (3 3) ``` ``` (lambda x in x x)(lambda y in y) == [x->(lambda y in y)](x x) == ((lambda y in y) (lambda y in y)) == [x->(lambda y in y)]y == (lambda y in y) ``` ``` (lambda x in x x)(lambda y in y y) == [x->(lambda y in y y)](x x) == ((lambda x in x x) (lambda x in x x)) == [x->(lambda x in x x)](x x) == ((lambda x in x x) (lambda x in x x)) ``` Omega combinator ## Curried Functions ``` f = (lambda x in (lambda y in x + y)) f 3 4 == ((f 3) 4) == ``` ## Evaluation First lets add to our direct substitution interpreter. Call-by-value ```haskell eval Lambda i s = return (Lambda i s) eval (Id _) = Nothing eval App f a = do {(Lambda i s) <- eval f ; a' <- eval a ; eval (subst a' i s)} eval Bind i a s = do {a' <- eval a ; eval (subst a' i s)} ``` ==Midterm Stops Here== [[2023-10-03-Midterm-Discussion|Midterm Discussion]] ``` ((Lambda i s) a) == bind i a s ``` ``` bind f = (lambda x in x) in (f 2) == ``` ``` bind n = 1 in bind f = (lambda x in x + n) in bind n = 2 in f 1 == bind f = (lambda x in x + 1) in bind n = 2 in f 1 == bind n = 2 in (lambda x in x + 1) 1 == (lambda x in x+1) 1 == [x->1]x+1 == 1+1 == 2 ``` `(f a) -> (App f a)` ``` f(x)=x+1 == bind f = (lambda x in x+1) f(x,y)=x+y == bind f = (lambda x in (lambda y in x+y)) == f(x)=(lambda y in x+y) ``` Same problem as before - inefficient and kind of clumsy Try again with environments: ``` Env = [(string,FBAE)] eval e (Lambda i s) = return (Lambda i s) eval e (App f a) = do { (Lambda i s) <- eval e f; a' <- eval e a; eval ((i,a'):e) s } eval e (Id i) = (lookup i e) ``` ``` bind f = (lambda x in x) in [(f,(lambda x in x))] (f f) '' == ((lambda x in x)(lambda x in x)) [(x,(lambda x in x)),(f,(lambda x in x))] == (lambda x in x) ``` ``` bind n = 1 in [(n,1)] bind f = (lambda x in x + n) in [(f,(lambda x in x + n)),(n,1)] bind n = 2 in [(n,2),(f,(lambda x in x + n)),(n,1)] f 1 == (lambda x in x + n) 1 [(x,1),(n,2),(f,(lambda x in x + n)),(n,1)] == x + n [(x,1),(n,2),(f,(lambda x in x + n)),(n,1)] == 1 + 2 == 3 ``` Oops... ## Static and Dynamic Scoping - Static Scoping - Identifier scope in a lambda is the scope where a lambda is _defined_. - Dynamic Scoping - Identifier scope in a lambda is the scope where a lambda is _used_. ``` bind n = 1 in. [] bind f = (lambda x in x + n) in [] bind n = 2 in [] f 1 ``` How do we fix this problem? *Closures* implement static scoping by including an environment in the function value. Naively: ``` data FBAE where ... Closure :: String -> FBAE -> Env -> FBAE ... ``` ``` (lambda x in x+1 [(n,3))] ``` We will keep a copy of the definition environment in the closure - `Env` in the argument list. Closures are now function values. Introducing a Value type returned by the interpreter ``` data FBAEVal where NumV :: Int -> FBAEVal ClosureV :: String -> FBAE -> Env -> FBAEVal eval :: Env -> FBAE -> (Maybe FBAEVal) ``` How do we: 1. Use the return value 2. Use the closure for static scoping ``` eval e (Num n) = return (NumV n) eval e (Plus l r) = do { (NumV l') <- (eval e l) ; (NumV r') <- (eval e r) ; return (NumV (l'+r'))} eval e (Lambda i s) = return (ClosureV i s e) eval e (App f a) = do {(ClosureV i s e') <- (eval e f) ; a' <- (eval e a) ; eval ((i,a'):e') s }} ``` Where are closures in the evaluator? Let's try the evaluation one more time. ``` bind n = 1 in [(n,1)] bind f = (lambda x in x + n) in [(f,(ClosureV x x+n [(n,1)])),(n,1)] bind n = 2 in [(n,2),(f,(ClosureV x x+n [(n,1)])),(n,1)] f 1 == (ClosureV x x+n [(n,1)]) 1 == x+n [(x,1),(n,1)] == 1+1 == 2 ``` - Immediate substitution $\Rightarrow$ static scoping - Deferred substitution $\Rightarrow$ dynamic scoping - Deferred substitution + closures $\Rightarrow$ static scoping ## Derived Forms Defining new language constructs in terms of existing language constructs `unless` as an example ``` unless ... 1. unless c e or e unless c 2. Unless BBAE -> BBAE -> BBAE 3. unless c e == if c then false else e 4. ``` $ \begin{prooftree} \AXC{\(c\Downarrow False\)}\AXC{\(e\Downarrow v\)}\RLS{UnlessF} \BIC{\(\mathsf{unless}\ c\ e\Downarrow v\)} \end{prooftree} $ $ \begin{prooftree} \AXC{\(c\Downarrow True\)}\RLS{UnlessT} \UIC{$\mathsf{unless}\ c\ e\Downarrow False$} \end{prooftree} $ ```haskell 5. eval Unless c e = do { c’ <- eval c; if c’ return false else eval e } 6. ``` $ \begin{prooftree} \AXC{\(\Gamma\vdash c:TBool\)} \AXC{\(\Gamma\vdash e:TBool\)} \RLS{TUnless} \BIC{\(\mathsf{unless}\ c\ e:TBool\)} \end{prooftree} $ - _Elaboration_ - Defining new language constructs in terms of existing constructs 1. Define concrete syntax 2. Define abstract syntax 3. _Define elaboration function_ 4. Define type rules 5. Extend type inference function - Embedded Language - New language with extensions - Host Language - Target language for translation ```haskell elab :: Embedded Language AST -> Host Language AST ``` To evaluated translate embedded language to host language and execute as usual: ```haskell evale t = eval [] (elab t) ``` or ```haskell evale t = eval [] . elab ``` Let's introduce a new expression in FBAE that performs increment. Sort of like ++ in C. In fact, we could use the notation `t++` to represent this if we wanted. To keep things simple, let's use `inc t`. Here is the abstract syntax for the host language, FAE: ```haskell data FAE where | Num :: int -> FBAE | Plus :: FBAE -> FBAE -> FBAE | Lambda :: String -> FBAE -> FBAE | App :: FBAE -> FBAE -> FBAE | Id :: String -> FBAE ``` This is just FBAE without `bind` Here is the abstract syntax for our embedded language ```haskell data FAEX where | NumX :: int -> FAEX | PlusX :: FAEX -> FAEX -> FAEX | LambdaX :: String -> FAEX -> FAEX | AppX :: FAEX -> FAEX -> FAEX | IdX :: String -> FAEX | IncX :: FAEX -> FAEX ``` What changed? Concrete syntax for the new `inc` function: ```haskell inc x == x+1 ``` Now an inference rule for `inc`: $ \begin{prooftree} \AXC{\(x\Downarrow v-1\)} \UIC{\(inc\ x\Downarrow v\)} \end{prooftree} $ Does this work? Do we even need it? Now our elaborator: ```haskell elab :: FAEX -> FAE elab NumX n = Num n elab PlusX l r = Plus (elab l) (elab r) elab LambdaX i b = Lambda i (elab b) elab AppX f a = App (elab f) (elab a) elab IdX s = Id s elab IncX x = Plus (elab x) (Num 1) ``` And finally `eval` that calls `elab` before evaluating `t`: ```haskell evalX t = eval [] (elab t) ``` What about type checking? Anything wrong with this? ```haskell typeofX t = typeof [] (elab t) ??? ``` We really do need a type checker for our embedded language. ```haskell Con = [(string,FAEType)] typeofX :: Con -> FAEX -> Maybe FAETNum typeofX _ (NumX _) = TNum typeofX c (PlusX l r) = do { TNum <- typeofX c l; TNum <- typeofX c r; return TNum } ... typeofX c (IncX t) = do { TNum <- typeofX t ; return TNum } typeofX c (IdX s) = lookup s c interpX t = do { typeofX [] t; evalX [] t } ``` What about `bind`? `bind i = v in t == (app (lambda i t) v)` 1. Concrete Syntax - already defined for FBAE 2. Abstract Syntax - already defined for FBAE 3. Evaluation Rules - already defined for FBAE 4. Elaboration - some work required 5. Type Rules - already defined for FBAE 6. typeof - already for FBAE Let's define the `elab` case for `bind`: ``` elab (Bind i a t) = (App (Lambda i (elab t)) (elab a)) ``` # Recursion ## Dynamically Scoped Recursion ``` bind fact = [] lambda x in if x=0 then 1 else x * (fact x-1) in (fact 3) == (fact 3) [(fact,(lambda x in ...))] == ((lambda x in if x=0 then 1 else x * (fact x-1)) 3) == if x=0 then 1 else x * (fact x-1) [(x,3),(fact,(lambda x in ...))] == if 3=0 then 1 else 3 * ((lambda x in ...) 3-1) == 3 * ((lambda x in ...) 2) == 3 * if x=0 then 1 else x * (fact x-1) [(x,2),(x,3),(fact,(lambda x in ...))] == 3 * if 2=0 then 1 else 2 * ((lambda x in ...) 2-1) == 3 * 2 * ((lambda x in ...) 1) == 3 * 2 * if x=0 then 1 else x * (fact x-1) [(x,1),(x,2),(x,3),(fact,(lambda x in ...))] == 3 * 2 * if 1=0 then 1 else 1 * ((lambda x in ...) 0) == 3 * 2 * 1 * ((lambda x in ...) 0) == 3 * 2 * 1 * if 0=0 then 1 else 0 * (fact x-1) [(x,0),(x,1),(x,2),(x,3),(fact,(lambda x in ...))] == 3 * 2 * 1 * 1 == 6 ``` ### Execution Sequence ((lambda x in if x=0 then 1 else x * (fact x-1)) 3) [(fact,(lambda x in ...))] Recursion works like recursion should. Cool ## Statically Scoped Recursion ``` bind fact = [] lambda x in if x=0 then 1 else x * (fact x-1) in [(fact,closure x ... [])] (fact 3) == (fact 3) [(fact,closure x ... [])] == ((closure x ... []) 3) [(fact,closure x ... [])] == if 3=0 then 1 else 3 * (fact 3-1) [(x,3),(fact,closure x ... [])] == 3 * (fact 2) [(x,3),(fact,closure x ... [(fact,closure x ... [])])] == 3 * ((closure x ... []) 2) [] --- CAN'T HAPPEN fact is not in the environment == ``` ### Execution Sequence ((closure x (if x=0 then 1 else x*(fact x-1))) 3) [(fact,closure x ... [])] == if x=0 then 1 else x*(fact x-1) [(x,3)] == 3 * fact 2 [(x,3)] ## Omega (redux) ``` bind o = lambda x in x x o o == (lambda x in x x)(lambda x in x x) == (x x) [(x,(lambda x in x x))] == (lambda x in x x)(lambda x in x x) ``` Does this work with closures? ``` bind o = lambda x in x x o o == o o [(o,(closure x in x x []))] == (closure x in x x [])(closure x in x x []) [(o,(closure x in x x []))] == (x x) [(x,(closure x in x x []))] == (closure x in x x [])(closure x in x x []) [(o,(closure x in x x []))] ``` ## The Y Combinator ``` bind Y = (lambda f (lambda x in (f (x x))) (lambda x in (f (x x)))) in (Y F) == ((lambda x in (F (x x)))) (lambda x in (F (x x)))) == (F (x x)) [(x,(lambda x in (F (x x)))] == (F ((closure x in (F (x x)) [] (closure x in (F (x x)) [])) == (F (F (x x))) [(x,(lambda x in (F (x x)))] == (F (F (F (x x)))) [(x,(lambda x in (F (x x)))] ``` - `f` is the function being called recursively - `F` the function we're applying recursively, not an identifier The Y Combinator calculates a _fixed point_ for F Calculating `sum` as an example. ``` bind F = (lambda g in (lambda z in if z=0 then z else z + (g (z-1)))) in bind Y = (lambda f (lambda x in (f (x x))) (lambda x in (f (x x)))) in ((Y F) 5) ``` Ummm. Where is `sum` defined here? ``` == (((lambda x in (F (x x)))) (lambda x in (F (x x)))) 5) Apply the first lambda to the second lambda == ((F (x x)) 5) [(x,(lambda x in (F (x x))))] Expand F == (((lambda g in (lambda z in if z=0 then ...)) (x x)) 5) [(x,(lambda x in (F (x x))))] Bind g to (x x) and evaluate the body == (((lambda z in if z=0 then z else z + (g (z-1))) 5) [(g,(x x)),(x,lambda x in (F (x x))))] Bind 5 to z == (z + (g z-1)) [(z,5),(g,(x x)),(x,(lambda x in (F (x x))))] Substitute z == (5 + ((x x) 4))) [(z,5),(g,(x x)),(x,(lambda x in (F (x x))))] Substitute x == (5 + (lambda x in (F (x x)))(lambda x in (F (x x))) 4)) repeat... == (5+(4+(3+(2+(1+(lambda x in (F (x x)))(lambda x in (F (x x))) 0)))))) [(z,1),(z,2),(z,3),(z,4),(z,5),(g,(x x)),(x,(lambda x in (F (x x))))] == (5+(4+(3+(2+(1+(((lambda z in if z=0 then 0 else z + (g (z-1))) 0)))))) == (5+(4+(3+(2+(1+0))))) == 15 ``` # Midterm Review - Interpreters - Concrete and Abstract Syntax - Parsing and Interpretation - Values and terms - Predicting types - Optimization - Extending Languages - Simple Inference Rules - Identifiers and Substitution - Binding identifiers to values - Substitution and Interpretation - Environments and Deferring Substitution - Identifier Scoping - Static and Dynamic Scoping - Functions - Taxonomy of functions - first order, higher order, first class - Interpreting functions - Closures for functions There will be very little if any Haskell on the exam. You will be asked to analyze code written in our little languages like FBAE, BAE and friends. I will provide those language definitions for you, so there is no need to memorize them. # Typing functions ``` bind inc = lambda x in x + 1 in (inc inc) ``` This is a problem, but why? ``` bind x=1 in [(x,1)] Evaluation x+1 ``` ``` bind x=1 in [(x,TNum)] Type checking x+1 == x+1 [(x,TNum)] == TNum + TNum == TNum ``` Where do 1 and `TNum` come from? Let's do the same with lambda expressions: ``` (lambda x in [(x,???)] Evaluation x+1) == x+1 [(x,???)] (lambda x in [(x,???)] Type checking x+1) ((lambda x in x+1) 1) Now what??? ``` What's the issue here? - `T->T` is a _function type_ or _signature_ Our concrete syntax for types becomes: ``` T ::= TNum | TBool | T -> T ``` `->` can be thought of as a type constructor. We will often write `D->R` where: - D - domain type - R - range type What's different about a function type? Function types represent promises: - `TBool->TNum` - Input a Boolean and get back a number - `TNum->TNum->TNum` - Input a number and get back a function from number to number - `TNun->TNum->TBool` - Input a number and get back a function from number to Boolean Curry-Howard says function types are also theorems. We'll come back to that. Unsurprisingly `lambda` as a function type: `lambda x in t : D->R` ## Finding D and R Let's start with `bind` What is the type of `bind`? ``` [] |- bind x=1 in x+1 : T ``` $\Gamma=[\ ]$ derives `bind...` is of some type `T` `T` is the type of the _body_ of bind. That's what `bind` returns. To find the type of the body, add type of `x` to the empty context: ``` == [(x,Tnum)] |- x+1 : T == TNum + TNum : T == TNum ``` Where did we get that type? The type of `bind` is the type of the _body_ with the type of the _identifier_ added to it's context Does the same thing work for lambda? Let's look at the type of `bind` as `app` `bind x = 3 in x+1 == ((lambda x in x+1) 3)` - Where do we get `D`? - Where do we get `R`? - What happens when we pull the `lambda` out of the `app`? `(lambda x in x+1):D->R` - Where do we get `D`? - Where do we get `R`? ``` [] |- lambda x in x+1 : D->R == [(x,???)] |- x+1 : == ``` - We need the type of `x` to compute the type of the function body. - Where do we get it? - `D` is the type of the input parameter that we don't have. - `R` is the type of the body _given the parameter_ type. Just like `bind`. Let's assume the type of `x`: ``` [] |- lambda x:TNat in x+1 : TNat->TNat == [(x,TNat)] |- x+1 : TNat ``` Can we find `D` and `R`? - The type of `x` is `D` because we assumed it so - The type of the body assuming `x:D` is `R` ``` [] |- lambda x:TNum in [(x,TNum)] x+1 [(x,TNum)] == TNum + TNum == TNum == TNum -> TNum ``` If you give the lambda `x:D` you will get `z:R` What is the scope of `x`? `f::Int->Int->Int` `f x::Int->Int` `f x y::Int` ``` lambda x:TNum in lambda y:TNum in x+y lambda x:TNum in [(x,TNum)] lambda y:TNum in [(y,TNum),(x,TNum)] x+y:TNum == TNum -> TNum == TNum -> TNum -> TNum ``` ``` lambda f:TBool->TNum in [(f,TBool->TNum)] lambda a:TBool in [(a,TBool),(f,TBool->TNum)] f a : TNum == TBool->TNum == (TBool -> TNum) -> TBool -> TNum ``` ## Type Inference for Lambda The Haskell code: ```haskell typeof c (Lambda x d t) = do {r <- typeof (x,d):c t; return (d:->:r)} ``` Type Rule: $ \begin{prooftree} \AXC{\((x:D):\Gamma\vdash t:R\)}\RLS{LambdaT} \UIC{\(\Gamma\vdash(\mathsf{lambda}\ x:D\ \mathsf{in}\ t):D\rightarrow R\)} \end{prooftree} $ `app` makes us keep our promise What is the type of: `((lambda x:TNum in x+1) 2 : T` We know the type of the lambda and the actual parameter: - `(lambda x:TNum in x+1) : TNum -> TNum` - `2 : TNum` What is the type of the `App`? What is the type of: `((lambda x:TNum in x+1) True : T` We know the type of the lambda and the actual parameter: - `(lambda x:TNum in x+1) : TNum -> TNum` - `True : TBool` What is the type of the `App`? Did we keep our promise? ## Type Inference for App The Haskell code: ```haskell typeof c (App f a) = do { D:->:R <- typeof c f; A <- typeof c a; if A=D then return R else Nothing} ``` Type rule: $ \begin{prooftree} \AXC{\(\Gamma\vdash f:D\rightarrow R\)}\AXC{\(\Gamma\vdash a:D\)}\RLS{TApp} \BIC{\(\Gamma\vdash (f\ a):R\)} \end{prooftree} $ Examples: ``` bind inc = lambda x:TNum in x+1 in [(inc,TNum->TNum)] inc 3 == inc : TNum->TNum, 3:TNum == TNum ``` ``` bind plus = (lambda x:TNum in [(x,TNum)] (lambda y:TNum in [(y,TNum),(x,TNum)] x+y)) in plus 3 4 : TNum [(plus,TNum->TNum->TNum)] plus 4 : (TNum->TNum) (plus 3) 4 : (TNum->TNum)->TNum TNum->(TNum->TNum) ``` ``` (bind app = lambda f:(TBool->TNum) in [(f,(TBool->TNun)] lambda a:TBool in [(a,TBool),(f,(TBool->TNun)] f a:TNum in [(app,(TBool->TNum)->TBool->TNum)] app (lambda x:TBool in if x then 1 else 0) : TBool -> TNum 3 :TNum) : TNum ``` # Simply Typed Lambda Calculus Functions and function types. Nothing else. ``` STC ::= id | lambda id in STC | STC STC T ::= T -> T ``` ``` lambda x:T in lambda y:T in x : T->T->T -- BAD No T ``` ``` lambda x:T in lambda y:T in ((x false) y) -- BAD No Booleans ``` ``` omega = (lambda x:T->T->T->T in (x x))(lambda x:T in (x x)) ``` We cannot define types for recursive functions. Actually, we cannot write functions at all. A perfectly fine language that we can write no programs in. Mathematical oddities abound... What would fix this problem? # Recursion (again) What we know so far about recursion 1. Recursion works in untyped, dynamically scoped FBAE (Lambda, Bind, AE) 2. Recursion does not work in untyped, statically scoped FBAE 3. Recursion does not work in typed FBAE ``` bind f=(lambda x in if x=0 then 1 else x*(f (x-1))) in [(f,(ClosureV “x” (if ...) []))] f 0 == (ClosureV “x” (if x=0 then 1 else x*(f (x-1))) []) 0 == if 0=0 then 1 else ..) == 1 f 1 == (ClosureV “x” if x=0 then 1 else x*(f (x-1)) []) 1 ==if 1=0 then 1 else 1*(f 1) [] ``` ### Solution ``` == (if x=0 then 1 else x*(f (x-1))) [(x,1),(f,(ClosureV ... []))] == 1*(f 0) [(x,1),(f,ClosureV ... []))] == 1*if 0=0 then 1 else == 1 ``` ## How to _fix_ this problem? ``` bind f=lambda x in if x=0 then 1 else x*(f (x-1)) ``` Interpret the lambda and get: ``` (ClosureV “x” TNum (if x=0 then 1 else x * (f (x-1))) []) ``` Apply to 0: ``` (ClosureV “x” TNum (if x=0 then 1 else x * (f (x-1))) []) 0 e == 1 ``` Now Apply to 1: ``` (ClosureV “x” TNum (if x=0 then 1 else x * (f (x-1))) []) 1 e == ``` Let's spike the closure with its own definition! ``` ((ClosureV “x” TNum (if x=0 then 1 else x*(fact (x-1)) [(fact,(ClosureV “x” TNum (if ...) []))]) 1 e == if 1=0 then 1 else 1*(fact 0) == 1*(ClosureV "x" TNum (if ...) []) == 1*1 ((ClosureV ...) 2 e) [(fact,(ClosureV “x” TNum (if ...) [(fact,(ClosureV “x” TNum (if ...) [])]))] == ``` Let's spike the closure's closure with its own definition! ``` ((ClosureV “x” TNum (if x=0 then 1 else x*(fact (x-1)) [(fact,(ClosureV “x” TNum (if ...)) [(fact,(ClosureV “x” TNum (if ...) []))]))]) 2 e == ``` What about 3? Pre-seeding the environment doesn't work. Why? What's the problem? ## The Fix Let's define a thing called `fix` that takes a function as its argument. When evaluated, `fix` will substitute its function argument into itself _before_ evaluation. What might that look like? ``` bind f = (lambda g in (lambda x in (if x=0 then 1 else x * (g (x-1)))) in ((fix f) 2) == ``` `f` takes two arguments, the first is a function `g` that defines the recursive call. Have we seen this? `fix` will replace `g` with `(fix f)`. `(fix f)` - not `f` - Then evaluate What does that do? Let's look at an inference rule: $ \begin{prooftree} \AXC{\([g\rightarrow(\mathsf{fix}\ (\mathsf{lambda}\ g\ \mathsf{in}\ t))]t\Downarrow v\)}\RLS{FixE} \UIC{\((\mathsf{fix}\ (\mathsf{lambda}\ g\ \mathsf{in}\ t))\Downarrow v\)} \end{prooftree} $ `fix` is copying itself _before_ evaluating. Remember substitution! Not quite as simple as throwing `fix` in front of a function. - We are effectively passing in the recursive call as a parameter - $g$ is the recursive function, not the data parameter - Need to extend the actual function to account for this new parameter ``` bind f = (lambda g in (lambda x in (if x=0 then 1 else x * (g (x-1)))) in (fact 2) == ((lambda x in (if x=0 then 1 else x * ((fix f) (x-1)))) 2) == (if 2=0 then 1 else 2 * ((fix f) (2-1))) == 2 * ((fix f) 1) == 2 * ((lambda x in (if x=0 then 1 else x * ((fix f) (x-1)))) 1) == 2 * (if 1=0 then 1 else 1 * ((fix f) (1-1))) == 2 * 1 * ((fix f) 0) == 2 * 1 * ((lambda x in (if x=0 then 1 else x * ((fix f) (x-1)))) 0) == 2 * 1 * (if 0=0 then 1 else 1 * ((fix f) 0-1))) == 2 * 1 * 1 ``` - `fix f` creates one recursive step at a time. - nothing is recursive? Really? Apply fix to the extended function and we get... ### Old Example ``` bind f = (lambda g in (lambda x in (if x=0 then 1 else x * (g (x-1)))) in ((fix f) 2) == [g->fix (lambda g b)]b == (lambda x in (if x=1 then 1 else x * (g (x-1)))) == (lambda x in (if x=1 then 1 else x * ((fix (lambda g b)) (x-1)) 2 == 2 * ((fix (lambda g b)) 1) == 2 * ((fix (lambda g (lambda x in if x=0 then 1 else x * (g (x-1))))) 1 == 2 * (lambda x in if x=0 then 1 else x (fix (lambda g b)) (x-1)))))) 1 == 2*1 == 2*1*1 == 2 ``` ## Extending FBAE for `fix` We have everything we need. First, the eval case: ```haskell eval e (Fix f) = do { (ClosureV g b e’) <- (eval e f); eval e’ (subst g (Fix (Lambda g b)) b) } ``` This is the same `subst` that we defined before Look carefully at what is being substituted. What is it?? ## Typing Fix `f` == the non-recursive form of `f` `(fix f)` == the recursive form of function `f` What is the type of `(fix f)`? ``` (fix f):D->R ``` What is the type of the lambda for factorial? ``` f = (lambda g:(D->R)->D->R in (lambda x:TNat in if x=0 then 1 else x*(g (x-1)))) ``` And the fix? ``` (fix f):D->R ``` $ \begin{prooftree} \AXC{\(\Gamma\vdash f:D\rightarrow R\)} \UIC{\(\Gamma\vdash (\textsf{fix}\ f):R\)} \end{prooftree} $ What is the type of the lambda for factorial? ``` f = (lambda g:TNat->TNat in (lambda x:TNat in if x=0 then 1 else x*(g (x-1)))) (fix f):TNat->TNat f:(TNat->TNat)->(TNat->TNat) ``` And now for something completely different... # Mutable State The big difference between Haskell and C ``` ... x=x+3 ; y=x++ ... ``` What does the `“;”` do? ``` bind x=(f a) in (g 3) == ((lambda _ in (g 3)) (f a)) ``` What’s happening besides substitution? ``` do { l’ <- eval l ; r’ <- eval r ; return (op l’ r’) } ``` What’s happening besides binding? Order! We tend to think of two paradigms: - functional - ordering is achieved via calling convention - imperative - ordering is achieved via sequencing mutable state What’s happening in this sequence? Where does x get a value? ``` x := 1; x := x + 1; x := x + 1; ... ``` `:=` is an assignment that updates a variable in the next state ``` x := x+1 == x’ = x+1 ``` The tick notation traditionally means next. The next `x` is the current `x` plus 1 ## Adding State to FBAE How is _state_ different than _environment_? 1. A Seq sequence operator 2. Define a store 3. Update eval to maintain state in addition to environment New concrete syntax: `t ; t` New abstract syntax: `Seq::FBAE->FBAE->FBAE` Define sequence by elaboration: `Seq l r` == `(App (Lambda x r) l)` ``` eval Seq l r = do { s <- eval q l; eval s r } ``` Interesting syntax! We'll skip typing for now. ## New operations for state ``` FBAE := new FBAE | deref FBAE | set loc FBAE ``` - `loc` - new type of value for locations - `new t` - creates a new location, puts t there, returns the location - `deref l` - retrieves a value from l and returns it - `set l t` - stores t in location l and returns l ``` deref (new 5) == 5 ``` ``` deref (set (new 5) 6) == 6 ``` ``` bind l = new 2+3 in deref l == 5 ``` ``` bind l = new 2+3 in set l ((deref l) + 1) ; (deref l) == ``` ``` l := r == (set l (deref r)) ``` ``` bind m = new 5 in bind n = m in set m 6 ; deref n == 6 ``` ``` bind m = new 5 in bind n = m in bind n = new 5 in set m 6 ; deref n == 5 ``` ``` bind inc = (lambda l in (set l ((deref l) + 1))) bind n = new 5 inc n ; deref n == 6 ``` ## Implementing State Store as a function with location as a number We could use an array or a sequence, but let's try a technique used in formal modeling. ``` type Sto = Loc -> Maybe FBAEVal type Loc = Int ``` `Sto` is the store and is a function from `Loc` to `Maybe FBAEVal`. Why a `Maybe`? - `Just x` - a good location is accessed. - `Nothing` - a bad location is accessed. What are good and bad memory locations? What does this have to do with language design? Memory contains `FBAEVal`. - How is this different from a C pointer? - How is this different from a Java reference? What does this have to do with language design? Dereferencing is just using the store as a function: ``` derefSto s l = (s l) ``` The initial store is a store with nothing in it. ``` initSto :: Sto initSto x = Nothing ``` - What is `initSto 3`? - What is `initSto` for any value? Updating the store is a bit trickier... `m0 = \l -> if l=3 then 1 else Nothing` `m1 = \l -> if l=1 then 2 else (m0 l)` `m2 = \l -> if l=2 then 0 else (m1 l)` `m3 = \l -> if l=3 then 4 else (m2 l)` ``` setSto :: Sto -> Loc -> FBAEVal -> Sto setSto s l v = \m -> if m==l then (Just v) else (s m) ``` - `\x -> t` is `lambda x in t` in FBAE - Given that, how does this work? - Why would I ever do this? The store becomes a collection of nested if statements. Not the most efficient, but it does what it’s supposed to for our purposes. Putting things together to model memory: ``` type Stor (Loc,Sto) ``` A location, store pair. - `loc` is the next memory element - `sto` is the current memory ``` derefStore (_,s) l = derefSto s l ``` Dereferencing is accessing the memory location ``` initStore = (0,initSto) ``` Initialize memory is the initial, empty memory with a next value of 0. ``` setStore (m,s) l v = (m,setSto s l v) ``` Storing a value ``` newStore (l,s) = (l+1,s) ``` Allocating memory location. - Now we know what `l` is for - Keep track of where the next memory location should be. ## Integrating Mutable Store How does store differ from environment? - Changes to store persist across scopes - Locations are values Let's add a constructor for locations ``` data FBAEVal = ... Loc :: Int -> FBAEVal ``` Seems simple enough - `eval` can return a location - Locations can be calculated? Maybe? - Locations can be stored? Maybe? Our choices are important. Even the tiny ones. How to implement mutability of `Store`?? Start with a new return value: ``` type Retval = (FBAEVal,Store) ``` `Retval` is the return value for our interpreter - A value from FBAE - The resulting store What's this for?? ``` eval :: Env -> Store -> FBAE -> Maybe Retval ``` `eval` now takes an environment _and_ a store. - `Env` - identifiers and values in scope - `Store` - contents and current location in memory. What should an initial call to `eval` look like? - environment - store - term `x:=x+1 ; x:=x+1` ``` eval e s (Seq l r) = do {(v,s') <- (eval e s l) ; (v',s'') <- (eval e s' r) ; return (v',s'')} ``` - One thing, then the other.... ``` eval e s (Set l t) = do {(v,s') <- (eval e s t) ; (v,s'') <- (setStore s' l v) ; return (v,s'')} ``` - All that work finally pays off - `Set` calls `setStore` - What is the pattern matching in the `do` clause doing? ``` eval e s (Plus l r) = do {((NumV l'),s') <- (eval e s l) ; ((NumV r');,s'') <- (eval e s' r) ; return ((NumV l'+r'),s'')} ``` This passing of state is the new idiom: ``` eval e s (Deref t) = do {((Loc l'),s') <- (eval e s t) ; (derefStore (Loc l') s'} ``` Let's unpack `New` and figure out what's going on: ``` eval e s (New t) = do { (t’,(l,s’)) <- eval e s t ; return ((Loc l),(setStore (newStore (l,s’)) l t’)) } ``` Wow. What the heck? - `(t',(l,s'))` - `t'` value to be stored in `s'` in `l` - `(Loc l+1)` - next location after `l` - `(newStore (l,s'))` - new store with `l` allocated - `(setStore ...)` - stores `t' `` # Variables and Assignment Let's do this together... We have utilities for mutable store. What things do we need? - Variable declaration - Variable dereference - Variable assignment We want to control the way that memory gets accessed. - No more `set`, `deref`, `new` ## Variable Declaration - What tools might we have? `var x:=t1` == `bind x=new t1 in t2` ``` var x:=0 var y:=1 var z:=3 ``` ## Variable Dereference - It would be nice to just say `x+1` - Is that possible if `x` is a variable 1. Get var location value from e 2. Get value value from s by dereferencing the location `(deref (lookup x e) s)` - value stored in the location stored in `x` ## Variable Assignment - What tools might we have? `x := t == (set x t) ``` Asn :: FBAE -> FBAE -> FBAE x := x+y+z ``` - Evaluate the right side - Already declared variable - Variable - Identifier whose value can be changed `(set x t)` ## Type Checking Variables - What tools might we have? - `bind x=new 3 in x:=x+1` - `var x:=3; [x:NumT] `x:=x+1` ``` (LocT tl) <- typeof c l tr <- typeof c l (Loc tv) <- lookup x c ``` `LocT :: TFBAE -> TFBEA` ## Throwing Errors - What tools might we have? `eval _ bang = Nothing` ## While Loop - What tools might we have? `while c do t == if c then (t ; while c do t) else skip` `while c do t == lambda f in lambda ` ``` while x<5 do x:=x+1 == w = lambda _ in if x<5 then x:=x+1 ; w _ else skip == lambda w in lambda _ in if x<5 then x:=x+1 ; w _ else skip == fix (lambda w in (lambda _ in if x<5 then x:=x+1 ; w _ else skip)) ``` ## Goto - Should we bother? ## Lists ## Objects & Object Oriented ``` let F=(lambda f in (lambda x in if x=0 then 1 else x*(f x-1))) in let fact=(fix F) (fact 3) ``` ``` let fact = (lambda x in if x=0 then 1 else x*(fact (x-1))) in (fact 3) ``` ``` typeofM c (Plus t1 t2) = do {NumT <- typeofM c t1; NumT <- typeofM c t2; return NumT } ``` `D -> R` ``` typeofM c (Lambda x D b) = do {R <- typeofM (x,D):c b; return (D :->: R)} ``` ``` typeofM c (Bind x t1 t2) = do {tx <- (typeofM c t1); (typeofM (x,tx):c t2)} ``` ``` bind n=3 in [(n,3)] bind f=(lambda x in x+n) in [(f,lambda...),(n,3)] bind n=1 in [(n,1),(f,lambda...),(n,3)] (f n) == x+n [(x,1),(n,1),(f,lambda...)(n,3)] == 2 Dynamic Scoping ``` ``` bind n=3 in [(n,3)] bind f=(lambda x in x+n) in [(f,(ClosureV x x+n [(n,3)]),(n,3)] bind n=1 in [(n,1),(f,(ClosureV x x+n [(n,3)]),(n,3)] (f n) == x+n [(x,1),(n,3)] == 4 Static Scoping ``` ``` bind x=v in b == ((lambda x in b) v) ``` ``` bind z=3+4 in z+z match with left side of elaboration rule x=z v=3+4 b=z+z substitute into right side ((lambda z in z+z) 3+4) ```