diff --git a/info/exercises/ex-05-sol.pdf b/info/exercises/ex-05-sol.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d263d673ab1b9625b22bbc50032c975948955f3a Binary files /dev/null and b/info/exercises/ex-05-sol.pdf differ diff --git a/info/exercises/ex-05.pdf b/info/exercises/ex-05.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fd0ed338333c688e71acb42d61d99444d5217879 Binary files /dev/null and b/info/exercises/ex-05.pdf differ diff --git a/info/exercises/src/ex-05/ex/check.tex b/info/exercises/src/ex-05/ex/check.tex new file mode 100644 index 0000000000000000000000000000000000000000..d8cb5b11e66f3cefe9acaba07a22b50f754d045c --- /dev/null +++ b/info/exercises/src/ex-05/ex/check.tex @@ -0,0 +1,61 @@ + +% do type checking for some simple terms + +\pagebreak +\begin{exercise}{} + For each of the following term-type pairs \((t, \tau)\), check whether the + term can be ascribed with the given type, i.e., whether there exists a + derivation of \(\Gamma \vdash t: \tau\) for some typing context \(\Gamma\) in + the given system. If not, briefly argue why. + + \begin{enumerate} + \item \(\lstinline|x|, \abool\) + \item \(\lstinline|x| + 1, \aint\) + \item \(\lstinline|(x && y) == (x <= 0)|, \abool\) + \item \(\lstinline|f| ~\ato~ \lstinline|x| ~\ato~ \lstinline|y| ~\ato~ \lstinline|f((x, y))|\): + + \lstinline|((List[Int], Bool) => Int) => List[Int] => Bool => Int| + \item \lstinline|Cons(x, x)| : \lstinline|List[List[Int]]| + \end{enumerate} + + \begin{solution} + \begin{enumerate} + \item \lstinline|x, Bool|. Derivation, assume \lstinline|x| is a boolean: + \begin{equation*} + \AxiomC{\((\lstinline|x|, \abool) \in \{(\lstinline|x|, \abool)\}\)} + \UnaryInfC{\(\{(\lstinline|x|, \abool)\} \vdash \lstinline|x: Bool|\)} + \DisplayProof + \end{equation*} + Note that this would work with any type, as there are no constraints. + \item \(\lstinline|x| + 1, \aint\). Derivation, assume \lstinline|x| is an + integer: + \begin{equation*} + \AxiomC{\((\lstinline|x|, \aint) \in \{(\lstinline|x|, \aint)\}\)} + \UnaryInfC{\(\{(\lstinline|x|, \aint)\} \vdash \lstinline|x: Int|\)} + \AxiomC{\(1 \in \naturals\)} + \UnaryInfC{\(\{(\lstinline|x|, \aint)\} \vdash \lstinline|1: Int|\)} + \BinaryInfC{\(\{(\lstinline|x|, \aint)\} \vdash \lstinline|x + 1: Int|\)} + \DisplayProof + \end{equation*} + Due to addition constraining the type of \lstinline|x|, other possible + types would not work. + \item \(\lstinline|(x && y) == (x <= 0)|, \abool\). Not well-typed. + From the left-hand side, we would enforce that \lstinline|x: Bool|, but on + the right, we find \lstinline|x: Int|. Due to this conflict, there is no + valid derivation for this term. + \item \lstinline|f => x => y => f((x, y))|: this is the currying function. + Note that it will conform to \lstinline|((a, b) => c) => a => b => c| for any choice + of \lstinline|a|, \lstinline|b|, and \lstinline|c|. (check) + \item \lstinline|Cons(x, x)| : \lstinline|List[List[Int]]|. Not + well-typed. The \(cons\) rule tells us that the second argument must have + the same type as the result, so \lstinline|x: List[List[Int]]|, but the first + argument enforces the type to be \lstinline|List[Int]| (again, due to result + type). As \(\aint \neq \alist{\aint}\), this is not well-typed. + + Note that the singular assignment of \lstinline|x| to \lstinline|Nil()| + can make a well typed term here, but the typing must hold for \emph{all} + possible values of \lstinline|x|. + \end{enumerate} + \end{solution} + +\end{exercise} diff --git a/info/exercises/src/ex-05/ex/map.tex b/info/exercises/src/ex-05/ex/map.tex new file mode 100644 index 0000000000000000000000000000000000000000..7c487fc7b7c117eea6831e637ab486a33e6c6aa2 --- /dev/null +++ b/info/exercises/src/ex-05/ex/map.tex @@ -0,0 +1,117 @@ + +\begin{exercise}{} + Consider the following term \(t\): + % + \begin{center} + \(t = \) \lstinline{l => map(l, x => fst(x)(snd(x)) + snd(x))} + % t = \lstinline|l| ~\ato~ \lstinline|map|(\lstinline|l|, \lstinline|x| ~\ato~ \afst{\lstinline|x|}(\asnd{\lstinline|x|}) + \asnd{\lstinline|x|}) + \end{center} + + where \lstinline|map| is a function with type \(\forall \tau, \pi.\; + \alist{\tau} ~\ato~ (\tau ~\ato~ \pi) ~\ato~ \alist{\pi}\). + + \begin{enumerate} + \item Label and assign type variables to each subterm of \(t\). + \item Generate the constraints on the type variables, assuming \(t\) is + well-typed, to infer the type of \(t\). + \item Solve the constraints via unification to deduce the type of \(t\). + \end{enumerate} + + \begin{solution} + \begin{enumerate} + \item We can label the subterms in the following way: + \begin{gather} + t: \tau = \lstinline|l => map(l, x => fst(x)(snd(x)) + snd(x))| \\ + t_1: \tau_1 = \lstinline|map(l, x => fst(x)(snd(x)) + snd(x))| \\ + t_2: \tau_2 = \lstinline|x => fst(x)(snd(x)) + snd(x)| \\ + t_3: \tau_3 = \lstinline|fst(x)(snd(x)) + snd(x)| \\ + t_4: \tau_4 = \lstinline|fst(x)(snd(x))| \\ + t_5: \tau_5 = \lstinline|snd(x)| \\ + t_6: \tau_6 = \lstinline|fst(x)| \\ + \lstinline|l|: \tau_7 = \lstinline|l| \\ + \lstinline|x|: \tau_8 = \lstinline|x| \\ + \lstinline|map|: \tau_9 = \lstinline|map| + \end{gather} + We can choose to separately label \lstinline|x|, \lstinline|l|, and + \lstinline|map|, but it does not make any difference to the result. + + \item + Inserting the type of \lstinline|map| (thus removing \(\tau_9\)), and + adding constraints by looking at the top-level of each subterm, we can get + the set of initial constraints, labelled by the subterm equation above + they come from: + \begin{gather*} + \tau = \tau_7 ~\ato~ \tau_1 \tag{1} \\ + \tau_1 = \alist{\tau_3} \tag{2, 4} \\ + \tau_7 = \alist{\tau_8} \tag{2, 9} \\ + \tau_2 = \tau_8 ~\ato~ \tau_3 \tag{3} \\ + \tau_3 = \aint \tag{4} \\ + \tau_4 = \aint \tag{4} \\ + \tau_5 = \aint \tag{4} \\ + \tau_6 = \tau_5 ~\ato~ \tau_4 \tag{5} \\ + \tau_8 = (\tau_5', \tau_5) \tag{6} \\ + \tau_8 = (\tau_6, \tau_6') \tag{7} + \end{gather*} + for fresh type variables \(\tau_5'\) and \(\tau_6'\) arising from the rule + for pairs. + + \item + The constraints can be solved step-by-step (major steps shown): + \begin{enumerate} + \item Eliminating known types (\(\tau_3, \tau_4, \tau_5\)): + \begin{gather*} + \tau = \tau_7 ~\ato~ \tau_1 \\ + \tau_1 = \alist{\aint} \\ + \tau_7 = \alist{\tau_8} \\ + \tau_2 = \tau_8 ~\ato~ \aint \\ + \tau_6 = \aint ~\ato~ \aint \\ + \tau_8 = (\tau_5', \aint) \\ + \tau_8 = (\tau_6, \tau_6') + \end{gather*} + \item Eliminating \(\tau_1, \tau_6\): + \begin{gather*} + \tau = \tau_7 ~\ato~ \alist{\aint} \\ + \tau_7 = \alist{\tau_8} \\ + \tau_2 = \tau_8 ~\ato~ \aint \\ + \tau_8 = (\tau_5', \aint) \\ + \tau_8 = (\aint ~\ato~ \aint, \tau_6') + \end{gather*} + \item Eliminating \(\tau_8\) using either of its equations: + \begin{gather*} + \tau = \tau_7 ~\ato~ \alist{\aint} \\ + \tau_7 = \alist{(\tau_5', \aint)} \\ + \tau_2 = (\tau_5', \aint) ~\ato~ \aint \\ + (\tau_5', \aint) = (\aint ~\ato~ \aint, \tau_6') + \end{gather*} + \item Performing unification of the pair type: + \begin{gather*} + \tau = \tau_7 ~\ato~ \alist{\aint} \\ + \tau_7 = \alist{(\tau_5', \aint)} \\ + \tau_2 = (\tau_5', \aint) ~\ato~ \aint \\ + \tau_5' = \aint ~\ato~ \aint \\ + \aint = \tau_6' + \end{gather*} + \item Eliminating \(\tau_5'\) and \(\tau_6'\): + \begin{gather*} + \tau = \tau_7 ~\ato~ \alist{\aint} \\ + \tau_7 = \alist{(\aint ~\ato~ \aint, \aint)} \\ + \tau_2 = (\aint ~\ato~ \aint, \aint) ~\ato~ \aint + \end{gather*} + \item Eliminating \(\tau_2, \tau_7\): + \begin{gather*} + \tau = \alist{(\aint ~\ato~ \aint, \aint)} ~\ato~ \alist{\aint} + \end{gather*} + \item Finally, all type variables are assigned, as we eliminate \(\tau\): + \begin{gather*} + \emptyset \text{ (no constraints left)} + \end{gather*} + \end{enumerate} + + The type of \(t\) as discovered by the unification process is: + \begin{gather*} + \tau = \alist{(\aint ~\ato~ \aint, \aint)} ~\ato~ \alist{\aint} + \end{gather*} + \end{enumerate} + \end{solution} + +\end{exercise} diff --git a/info/exercises/src/ex-05/ex/program.tex b/info/exercises/src/ex-05/ex/program.tex new file mode 100644 index 0000000000000000000000000000000000000000..137669d10e938a4c7dec1f329cc90fe36953acc2 --- /dev/null +++ b/info/exercises/src/ex-05/ex/program.tex @@ -0,0 +1,73 @@ + +\begin{exercise}{} + + A \emph{program} is a top-level expression \(t\) accompanied by a set of + user-provided function definitions. The program is well-typed if each of the + function bodies conform to the type of the function, and the top-level + expression is well-typed in the context of the function definitions. + + For each of the following function definitions, check whether the function + body is well-typed: + % + \begin{enumerate} + \item \lstinline|def f(x: Int, y: Int): Bool = x <= y| + \item \lstinline|def rec(x: Int): Int = rec(x)| + \item \lstinline|def fib(n: Int): Int = if n <= 1 then 1 else (fib(n - 1) + fib(n - 2))| + \end{enumerate} + + \begin{solution} + \begin{enumerate} + \item Well-typed, apply rule \(Leq\). + \item Well-typed. We need to check if the body conforms to the output + type, if we know the function and its parameters have their ascribed type. + So, under the context \lstinline|rec: Int => Int, x: Int|, we need to + prove that \lstinline|rec(x): Int|. This follows from the \(app\) rule. + + So, if we allow recursion and do not check for termination, we can prove + unexpected things using the non-terminating programs. + \item Well-typed. We need to produce a derivation of the following: + \begin{equation*} + \lstinline|fib: Int => Int|, \lstinline|n: Int| \vdash \lstinline|if n <= 1 then 1 else (fib(n - 1) + fib(n - 2)): Int| + \end{equation*} + i.e., given that \lstinline|fib| inductively has type \lstinline|Int => Int| and + the parameter \lstinline|n| has type \lstinline|Int|, we need to prove that the + body of the function has the ascribed type \lstinline|Int|. + + The derivation can be constructed by following the structure of the term + on the right-hand side, the body. We set \(\Gamma = + % + \lstinline|fib: Int => Int|, \lstinline|n: Int|\) for brevity. The \lstinline|n-2| + branch is skipped due to space and being the same as the \lstinline|n-1| branch. + \begin{equation*} + \hspace*{-4cm} + \AxiomC{\((\lstinline|n|, \aint) \in \Gamma\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|n: Int|\)} + \AxiomC{\(1 \in \naturals\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|1: Int|\)} + \BinaryInfC{\(\Gamma \vdash \lstinline|n <= 1: Bool|\)} + % + \AxiomC{\(1 \in \naturals\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|1: Int|\)} + % + \AxiomC{\((\lstinline|fib, Int => Int|) \in \Gamma\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|fib: Int => Int|\)} + \AxiomC{\((\lstinline|n|, \aint) \in \Gamma\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|n: Int|\)} + \AxiomC{\(1 \in \naturals\)} + \UnaryInfC{\(\Gamma \vdash \lstinline|1: Int|\)} + \BinaryInfC{\(\Gamma \vdash \lstinline|(n - 1): Int|\)} + \BinaryInfC{\(\Gamma \vdash \lstinline|fib(n - 1): Int|\)} + % + \AxiomC{\ldots} + \UnaryInfC{\(\Gamma \vdash \lstinline|fib(n - 2): Int|\)} + % + \BinaryInfC{\(\Gamma \vdash \lstinline|fib(n - 1) + fib(n - 2): Int|\)} + % + \TrinaryInfC{\(\Gamma \vdash \lstinline|if n <= 1 then 1 else (fib(n - 1) + fib(n - 2)): Int|\)} + \DisplayProof + \end{equation*} + \end{enumerate} + + \end{solution} + +\end{exercise} diff --git a/info/exercises/src/ex-05/ex/rec-inf.tex b/info/exercises/src/ex-05/ex/rec-inf.tex new file mode 100644 index 0000000000000000000000000000000000000000..c5364bbf63032dbcf9f172761def4e9fe958fe00 --- /dev/null +++ b/info/exercises/src/ex-05/ex/rec-inf.tex @@ -0,0 +1,66 @@ + + +\pagebreak +\begin{exercise}{} + + Consider the following definition for a recursive function \(g\): + + \begin{equation*} + \lstinline|def g(n, x) = if n <= 2 then (x, x) else (x, g(n - 1, x))| + \end{equation*} + + \begin{enumerate} + \item Evaluate \(g(3, 1)\) and \(g(4, 2)\) using the definition of \(g\). + Suggest a type for the function \(g\) based on your observations. + \item Label and assign type variables to the definition parameters, body, + and its subterms. + \item Generate the constraints on the type variables, assuming the + definition of \(g\) is well-typed. + \item Attempt to solve the generated constraints via unification. Argue how + the result correlates to your observations from evaluating \(g\). + \end{enumerate} + + \begin{solution} + \begin{enumerate} + \item \lstinline|g(3, 1)| evaluates to \lstinline|(1, (1, 1))| and + \lstinline|g(4, 2)| evaluates to \lstinline|(2, (2, (2, 2)))|. Notably, + these two come from disjoint types. This suggests that the function \(g\) + is not well-typed. + \item We can label the parameters, subterms, and assign a type to the + function: + \setcounter{equation}{0} + \begin{gather} + \lstinline|g|: \tau \\ + \lstinline|n|: \tau_n \\ + \lstinline|x|: \tau_x \\ + body: \tau_1 = \lstinline|if n <= 2 then (x, x) else (x, g(n - 1, x))| \\ + t_1: \tau_2 = \lstinline|n <= 2| \\ + t_2: \tau_3 = \lstinline|(x, x)| \\ + t_3: \tau_4 = \lstinline|(x, g(n - 1, x))| \\ + t_4: \tau_5 = \lstinline|g(n - 1, x)| \\ + t_5: \tau_6 = \lstinline|n - 1| + \end{gather} + \item We can generate the constraints by looking at the top-level of each + subterm equation: + \begin{gather*} + \tau = \tau_n ~\ato~ \tau_x ~\ato~ \tau_1 \tag{1, def} \\ + \tau_1 = \tau_3 \tag{4} \\ + \tau_1 = \tau_4 \tag{4} \\ + \tau_2 = \abool \tag{4} \\ + \tau_n = \aint \tag{5} \\ + \tau_3 = (\tau_x, \tau_x) \tag{6} \\ + \tau_4 = (\tau_x, \tau_5) \tag{6} \\ + \tau_5 = \tau_1 \tag{7, def} \\ + \tau_6 = \aint \tag{9} + \end{gather*} + \item The constraints can be solved (eliminating \(\tau_4, \tau_5\)) to + reach a set of constraints containing the recursive constraint \(\tau_1 = + (\tau_x, \tau_1)\). There is no type \(\tau_1\) (the output type of + \lstinline|g|!) satisfying this. + + This matches our previous observation where \lstinline|g| produced two + different sized tuples as its output. + \end{enumerate} + \end{solution} + +\end{exercise} \ No newline at end of file diff --git a/info/exercises/src/ex-05/ex/system.tex b/info/exercises/src/ex-05/ex/system.tex new file mode 100644 index 0000000000000000000000000000000000000000..ebe61c6cf2c431bff7f5e9f0d64ea9f12ea1fad0 --- /dev/null +++ b/info/exercises/src/ex-05/ex/system.tex @@ -0,0 +1,112 @@ + +% we want lists, pairs +% both parametric +% we want some functions, as well as function definitions +% we want to be able to define and typecheck recursive functions + +Consider a type system for a simple functional language, consisting of integers, +booleans, parametric pairs, and lists. The rest of the exercises will revolve +around this system. + +% language components +\newcommand{\aint}{\lstinline|int|} +\newcommand{\abool}{\lstinline|bool|} +\newcommand{\aif}{\lstinline|if|} +\newcommand{\athen}{\lstinline|then|} +\newcommand{\aelse}{\lstinline|else|} +\newcommand{\apair}[2]{(#1, #2)} +\newcommand{\afst}[1]{\lstinline|fst|(#1)} +\newcommand{\asnd}[1]{\lstinline|snd|(#1)} +\newcommand{\alist}[1]{\lstinline|List[|#1\lstinline|]|} +\newcommand{\anil}{\lstinline|Nil()|} +\newcommand{\acons}[2]{\lstinline|Cons(|#1, #2\lstinline|)|} +\newcommand{\ato}{\lstinline|=>|} + +%%% Actual type system +\begin{gather*} + % variables + \AxiomC{\((x, \tau) \in \Gamma\)} + \RightLabel{(var)} + \UnaryInfC{\(\Gamma \vdash x : \tau\)} + \DisplayProof \\ + % integers + \AxiomC{\(n\) is an integer value} + \RightLabel{(int)} + \UnaryInfC{\(\Gamma \vdash \num(n) : \aint\)} + \DisplayProof \\ + % + - + \AxiomC{\(e_1 : \aint\)} + \AxiomC{\(e_2 : \aint\)} + \RightLabel{(+)} + \BinaryInfC{\(\Gamma \vdash e_1 + e_2 : \aint\)} + \DisplayProof \qquad + \AxiomC{\(e_1 : \aint\)} + \AxiomC{\(e_2 : \aint\)} + \RightLabel{(-)} + \BinaryInfC{\(\Gamma \vdash e_1 - e_2 : \aint\)} + \DisplayProof \\ + % booleans + \AxiomC{\(b\) is a boolean value} + \RightLabel{(bool)} + \UnaryInfC{\(\Gamma \vdash \abool(b) : \abool\)} + \DisplayProof \\ + % props + \AxiomC{\(\Gamma \vdash e_1 : \abool\)} + \AxiomC{\(\Gamma \vdash e_2 : \abool\)} + \RightLabel{(and)} + \BinaryInfC{\(\Gamma \vdash e_1 \land e_2 : \abool\)} + \DisplayProof \qquad + \AxiomC{\(\Gamma \vdash e_1 : \abool\)} + \AxiomC{\(\Gamma \vdash e_2 : \abool\)} + \RightLabel{(or)} + \BinaryInfC{\(\Gamma \vdash e_1 \lor e_2 : \abool\)} + \DisplayProof \\ + \AxiomC{\(\Gamma \vdash e_1 : \abool\)} + \RightLabel{(not)} + \UnaryInfC{\(\Gamma \vdash \neg e_1 : \abool\)} + \DisplayProof \\ + % ite + \AxiomC{\(\Gamma \vdash e_1 : \abool\)} + \AxiomC{\(\Gamma \vdash e_2 : \tau\)} + \AxiomC{\(\Gamma \vdash e_3 : \tau\)} + \RightLabel{(ite)} + \TrinaryInfC{\(\Gamma \vdash \aif \, e_1 \, \athen \, e_2 \, \aelse \, e_3 : \tau\)} + \DisplayProof \\ + % pairs + \AxiomC{\(\Gamma \vdash e_1 : \tau_1\)} + \AxiomC{\(\Gamma \vdash e_2 : \tau_2\)} + \RightLabel{(pair)} + \BinaryInfC{\(\Gamma \vdash \apair{e_1}{e_2} : \apair{\tau_1}{\tau_2}\)} + \DisplayProof \\ + % fst + \AxiomC{\(\Gamma \vdash e : \apair{\tau_1}{\tau_2}\)} + \RightLabel{(fst)} + \UnaryInfC{\(\Gamma \vdash \afst{e} : \tau_1\)} + \DisplayProof \qquad + % snd + \AxiomC{\(\Gamma \vdash e : \apair{\tau_1}{\tau_2}\)} + \RightLabel{(snd)} + \UnaryInfC{\(\Gamma \vdash \asnd{e} : \tau_2\)} + \DisplayProof \\ + % lists + \AxiomC{\phantom{\(e \in \alist{\tau}\)}} + \RightLabel{(nil)} + \UnaryInfC{\(\Gamma \vdash \anil : \alist{\tau}\)} + \DisplayProof \qquad + \AxiomC{\(\Gamma \vdash e_1 : \tau\)} + \AxiomC{\(\Gamma \vdash e_2 : \alist{\tau}\)} + \RightLabel{(cons)} + \BinaryInfC{\(\Gamma \vdash \acons{e_1}{e_2} : \alist{\tau}\)} + \DisplayProof \\ + % functions + \AxiomC{\(\Gamma, x : \tau_1 \vdash e : \tau_2\)} + \RightLabel{(fun)} + \UnaryInfC{\(\Gamma \vdash \lambda x : \tau_1 ~\ato~ e : \tau_1 ~\ato~ \tau_2\)} + \DisplayProof \qquad + % application + \AxiomC{\(\Gamma \vdash e_1 : \tau_1 ~\ato~ \tau_2\)} + \AxiomC{\(\Gamma \vdash e_2 : \tau_1\)} + \RightLabel{(app)} + \BinaryInfC{\(\Gamma \vdash e_1 \, e_2 : \tau_2\)} + \DisplayProof \\ +\end{gather*} diff --git a/info/exercises/src/ex-05/main.tex b/info/exercises/src/ex-05/main.tex new file mode 100644 index 0000000000000000000000000000000000000000..ae5544de28a216f881f648f6db083b36ad452c08 --- /dev/null +++ b/info/exercises/src/ex-05/main.tex @@ -0,0 +1,35 @@ +\documentclass[a4paper]{article} + +\input{../macro} + +\ifdefined\ANSWERS + \if\ANSWERS1 + \printanswers + \fi +\fi + +\DeclareMathOperator{\half}{half} +\newcommand{\num}{\ensuremath{\mathbf{num}}} + +\title{CS 320 \\ Computer Language Processing\\Exercise Set 5} +\author{} +\date{April 02, 2025} + +\begin{document} +\maketitle + +% type system +% do some type checking +% some unification +% some inference +\input{ex/system} + +\input{ex/check} + +\input{ex/program} + +\input{ex/map} + +\input{ex/rec-inf} + +\end{document}