diff --git a/semester4/fmfp/code/00_syntax.hs b/semester4/fmfp/code/00_syntax.hs index fefd062..f9695d8 100644 --- a/semester4/fmfp/code/00_syntax.hs +++ b/semester4/fmfp/code/00_syntax.hs @@ -1,11 +1,17 @@ --- Declaring a function. Naming using lowerCamelCase --- Arguments separated by whitespace -myFunc :: Int -> Int -> Int +-- Declaring a function. Naming using lowerCamelCase. Arguments separated by whitespace +-- We can omit the parenthesis in the type (they show what compiler does) +-- The below function uses guards (boolean checks) +myFunc :: Int -> (Int -> Int) myFunc x y | x > 0 = x + y | x < 0 = -x + y + | otherwise = 0 --- On compile the above function is transformed like this: --- TODO: Transform the template into correct version -myFuncXCompiled :: Int -> Int -myFuncXCompiled x = x +-- If the checks are pattern-based, use pattern matching. _ is wildcard (any value) +-- It is used when we don't need the variable. You can combine pattern-matching and guards +myOtherFunc :: Int -> Int -> Int +myOtherFunc 0 _ = 0 +myOtherFunc x 0 = x +myOtherFunc x y + | x > 0 = x + y + | x < 0 = -x + y diff --git a/semester4/fmfp/code/10_parser.hs b/semester4/fmfp/code/10_parser.hs new file mode 100644 index 0000000..68bfa2e --- /dev/null +++ b/semester4/fmfp/code/10_parser.hs @@ -0,0 +1,75 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} + +{-# HLINT ignore "Use lambda-case" #-} +{-# HLINT ignore "Use newtype instead of data" #-} + +import Prelude hiding (return, (>>), (>>=)) + +data Parser a = Prs (String -> [(a, String)]) + +-- Main parser function +parse :: Parser a -> String -> [(a, String)] +parse (Prs p) = p + +------------------- +-- Basic parsers -- +------------------- +-- Trivial failure ([] signifies parse failed) +failure :: Parser a +failure = Prs (const []) + +-- Trivial success without progress +return :: a -> Parser a +return x = Prs (\inp -> [(x, inp)]) + +-- Trivial success with progress +item :: Parser Char +item = + Prs + ( \inp -> case inp of + "" -> [] + (x : xs) -> [(x, xs)] + ) + +---------- +-- Glue -- +---------- +-- Apply both parsers +(|||) :: Parser a -> Parser a -> Parser a +p ||| q = Prs (\s -> parse p s ++ parse q s) + +-- If first parser fails, apply second parser +(+++) :: Parser a -> Parser a -> Parser a +p +++ q = + Prs + ( \s -> case parse p s of + [] -> parse q s + res -> res + ) + +-- Sequencing (first parser p, then parser q) +(>>=) :: Parser a -> (a -> Parser b) -> Parser b +p >>= g = Prs (\s -> [(u, s'') | (t, s') <- parse p s, (u, s'') <- parse (g t) s']) + +-- Simple version of the above +(>>) :: Parser a -> Parser b -> Parser b +p >> q = p >>= const q + +-- Parse single character with property p +sat :: (Char -> Bool) -> Parser Char +sat p = item >>= \x -> if p x then return x else failure + +char :: Char -> Parser Char +char x = sat (== x) + +string :: String -> Parser String +string "" = return "" +string (x : xs) = char x >> string xs >> return (x : xs) + +-- 0 or more repetitions of p +many :: Parser a -> Parser [a] +many p = many1 p ||| return [] + +-- 1 or more repetitions of p +many1 :: Parser a -> Parser [a] +many1 p = p >>= \t -> many p >>= \ts -> return (t : ts) diff --git a/semester4/fmfp/formal-methods-functional-programming-summary.pdf b/semester4/fmfp/formal-methods-functional-programming-summary.pdf index 6864a5b..3500d76 100644 Binary files a/semester4/fmfp/formal-methods-functional-programming-summary.pdf and b/semester4/fmfp/formal-methods-functional-programming-summary.pdf differ diff --git a/semester4/fmfp/formal-methods-functional-programming-summary.tex b/semester4/fmfp/formal-methods-functional-programming-summary.tex index cddaefd..ed58314 100644 --- a/semester4/fmfp/formal-methods-functional-programming-summary.tex +++ b/semester4/fmfp/formal-methods-functional-programming-summary.tex @@ -77,7 +77,15 @@ \input{parts/02_typing/01_mini-haskell/02_further-rules.tex} \input{parts/02_typing/01_mini-haskell/03_type-inference.tex} \input{parts/02_typing/02_algebraic-data-types/00_correctness.tex} -% \input{parts/02_typing/01_mini-haskell/} +\input{parts/02_typing/02_algebraic-data-types/01_induction-nat-num.tex} +\input{parts/02_typing/02_algebraic-data-types/02_lists.tex} +\input{parts/02_typing/02_algebraic-data-types/03_trees.tex} +\input{parts/02_typing/02_algebraic-data-types/04_structural-induction.tex} +\input{parts/02_typing/03_interpreter/00_intro.tex} +\input{parts/02_typing/03_interpreter/01_read.tex} +\input{parts/02_typing/03_interpreter/02_eval.tex} +% \input{parts/02_typing/03_interpreter/} + diff --git a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/00_correctness.tex b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/00_correctness.tex index 00d9b11..368ce80 100644 --- a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/00_correctness.tex +++ b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/00_correctness.tex @@ -13,76 +13,3 @@ Or the same as a natural deduction rule: \infer2[$m$ not free in $\Gamma, P$]{\Gamma \vdash \forall n \in \N. P} \end{prooftree} \] - - -\subsubsection{Induction over the natural numbers} -In Haskell, we can also define all the natural numbers using -\mint{haskell}+data Nat = Zero | Succ Nat deriving (Eq, Ord, Show)+ - -Thus the natural numbers are (isomorphic to) the set -\[ - \texttt{Nat} = \{ \texttt{Zero}, \texttt{Succ Zero}, \texttt{Succ (Succ Zero)}, \ldots \} -\] - -The data type provides two crucial rules for constructing members of \texttt{Nat}: -\begin{itemize} - \item $\texttt{Zero} \in \texttt{Nat}$ - \item If $x \in \texttt{Nat}$, then $\texttt{Succ} x \in \texttt{Nat}$ -\end{itemize} - -The induction stated as a natural deduction rule: -\[ - \begin{prooftree} - \hypo{\Gamma \vdash P[n \mapsto \texttt{Zero}]} - \hypo{\Gamma, P[n \mapsto m] \vdash P[n \mapsto \texttt{Succ}\ m]} - \infer2[$m$ not free in $\Gamma, P$]{\Gamma \vdash \forall n \in \texttt{Nat}. P} - \end{prooftree} -\] - - -\subsubsection{Lists} -A possible data type for lists in Haskell is: -\mint{haskell}+data L t = Nil | Cons t (L t)+ - -A natural deduction rule for induction over lists is: -\[ - \begin{prooftree} - \hypo{\Gamma \vdash P[xs \mapsto \texttt{Nil}]} - \hypo{\Gamma, P[xs \mapsto ys] \vdash P[xs \mapsto \texttt{Cons}\ y\ ys]} - \infer2[$y$, $ys$ not free in $\Gamma, P$]{\Gamma \vdash \forall xs \in \texttt{L}\ t. P} - \end{prooftree} -\] - - -\subsubsection{Trees} -A possible data type for trees in Haskell is: -\mint{haskell}+data Tree t = Leaf | Node t (Tree t) (Tree t)+ - -A natural deduction rule for induction over trees is: -\[ - \begin{prooftree} - \hypo{\Gamma \vdash P[x \mapsto \texttt{Leaf}]} - \hypo{\Gamma, P[x \mapsto l] \vdash P[xs \mapsto \texttt{Node}\ a\ l\ r]} - \infer2[$a$, $l$, $r$ not free in $\Gamma, P$]{\Gamma \vdash \forall x \in \texttt{Tree}\ t. P} - \end{prooftree} -\] - - -\subsubsection{Structural Induction} -Induction is based on the structure of terms -\mint{haskell}+data T t = Leaf t | Node1 (T t) | Node2 t (T t) (T t)+ - -\shade{blue}{Base Case} $T_0 = \{ \texttt{Leaf}\ a \divider a \in t \}$ - -\shade{green}{Step Case} $T_i = T_{i - 1} \cup \{ \texttt{Node1}\ s \divider s \in T_{i - 1} \} \cup \{ \texttt{Node2}\ a\ l\ r \divider a \in t \text{ and } l, r \in T_{i - 1} \}$ - -A natural deduction rule structural induction is: -\[ - \begin{prooftree} - \hypo{\Gamma \vdash P[x \mapsto \texttt{Leaf}\ a]} - \hypo{\Gamma, P[x \mapsto s] \vdash P[xs \mapsto \texttt{Node1}\ s]} - \hypo{\Gamma, P[x \mapsto l], P[x \mapsto r] \vdash P[ \mapsto \texttt{Node2}\ a\ l\ r]} - \infer3[$(*)$]{\Gamma \vdash \forall x \in \texttt{T}\ t. P} - \end{prooftree} -\] -(*) $a$, $l$, $r$, $s$ not free in $\Gamma, P$ diff --git a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/01_induction-nat-num.tex b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/01_induction-nat-num.tex new file mode 100644 index 0000000..9e6f7ed --- /dev/null +++ b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/01_induction-nat-num.tex @@ -0,0 +1,23 @@ +\subsubsection{Induction over the natural numbers} +In Haskell, we can also define all the natural numbers using +\mint{haskell}+data Nat = Zero | Succ Nat deriving (Eq, Ord, Show)+ + +Thus the natural numbers are (isomorphic to) the set +\[ + \texttt{Nat} = \{ \texttt{Zero}, \texttt{Succ Zero}, \texttt{Succ (Succ Zero)}, \ldots \} +\] + +The data type provides two crucial rules for constructing members of \texttt{Nat}: +\begin{itemize} + \item $\texttt{Zero} \in \texttt{Nat}$ + \item If $x \in \texttt{Nat}$, then $\texttt{Succ} x \in \texttt{Nat}$ +\end{itemize} + +The induction stated as a natural deduction rule: +\[ + \begin{prooftree} + \hypo{\Gamma \vdash P[n \mapsto \texttt{Zero}]} + \hypo{\Gamma, P[n \mapsto m] \vdash P[n \mapsto \texttt{Succ}\ m]} + \infer2[$m$ not free in $\Gamma, P$]{\Gamma \vdash \forall n \in \texttt{Nat}. P} + \end{prooftree} +\] diff --git a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/02_lists.tex b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/02_lists.tex new file mode 100644 index 0000000..d056fd7 --- /dev/null +++ b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/02_lists.tex @@ -0,0 +1,12 @@ +\subsubsection{Lists} +A possible data type for lists in Haskell is: +\mint{haskell}+data L t = Nil | Cons t (L t)+ + +A natural deduction rule for induction over lists is: +\[ + \begin{prooftree} + \hypo{\Gamma \vdash P[xs \mapsto \texttt{Nil}]} + \hypo{\Gamma, P[xs \mapsto ys] \vdash P[xs \mapsto \texttt{Cons}\ y\ ys]} + \infer2[$y$, $ys$ not free in $\Gamma, P$]{\Gamma \vdash \forall xs \in \texttt{L}\ t. P} + \end{prooftree} +\] diff --git a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/03_trees.tex b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/03_trees.tex new file mode 100644 index 0000000..52d72d6 --- /dev/null +++ b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/03_trees.tex @@ -0,0 +1,12 @@ +\subsubsection{Trees} +A possible data type for trees in Haskell is: +\mint{haskell}+data Tree t = Leaf | Node t (Tree t) (Tree t)+ + +A natural deduction rule for induction over trees is: +\[ + \begin{prooftree} + \hypo{\Gamma \vdash P[x \mapsto \texttt{Leaf}]} + \hypo{\Gamma, P[x \mapsto l] \vdash P[xs \mapsto \texttt{Node}\ a\ l\ r]} + \infer2[$a$, $l$, $r$ not free in $\Gamma, P$]{\Gamma \vdash \forall x \in \texttt{Tree}\ t. P} + \end{prooftree} +\] diff --git a/semester4/fmfp/parts/02_typing/02_algebraic-data-types/04_structural-induction.tex b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/04_structural-induction.tex new file mode 100644 index 0000000..cc1bd6f --- /dev/null +++ b/semester4/fmfp/parts/02_typing/02_algebraic-data-types/04_structural-induction.tex @@ -0,0 +1,18 @@ +\subsubsection{Structural Induction} +Induction is based on the structure of terms +\mint{haskell}+data T t = Leaf t | Node1 (T t) | Node2 t (T t) (T t)+ + +\shade{blue}{Base Case} $T_0 = \{ \texttt{Leaf}\ a \divider a \in t \}$ + +\shade{green}{Step Case} $T_i = T_{i - 1} \cup \{ \texttt{Node1}\ s \divider s \in T_{i - 1} \} \cup \{ \texttt{Node2}\ a\ l\ r \divider a \in t \text{ and } l, r \in T_{i - 1} \}$ + +A natural deduction rule structural induction is: +\[ + \begin{prooftree} + \hypo{\Gamma \vdash P[x \mapsto \texttt{Leaf}\ a]} + \hypo{\Gamma, P[x \mapsto s] \vdash P[xs \mapsto \texttt{Node1}\ s]} + \hypo{\Gamma, P[x \mapsto l], P[x \mapsto r] \vdash P[ \mapsto \texttt{Node2}\ a\ l\ r]} + \infer3[$(*)$]{\Gamma \vdash \forall x \in \texttt{T}\ t. P} + \end{prooftree} +\] +(*) $a$, $l$, $r$, $s$ not free in $\Gamma, P$ diff --git a/semester4/fmfp/parts/02_typing/03_interpreter/00_intro.tex b/semester4/fmfp/parts/02_typing/03_interpreter/00_intro.tex new file mode 100644 index 0000000..506a1dd --- /dev/null +++ b/semester4/fmfp/parts/02_typing/03_interpreter/00_intro.tex @@ -0,0 +1,7 @@ +\newpage +\subsection{Interpreters} +Interpreters are prevalent in programming languages, database systems, text processors, HDLs, search engines, etc. + +They are in concept very simple, as they perform three steps read, evaluate, print. + +The implementation of one however is not trivial by any stretch of the imagination. diff --git a/semester4/fmfp/parts/02_typing/03_interpreter/01_read.tex b/semester4/fmfp/parts/02_typing/03_interpreter/01_read.tex new file mode 100644 index 0000000..a72e372 --- /dev/null +++ b/semester4/fmfp/parts/02_typing/03_interpreter/01_read.tex @@ -0,0 +1,52 @@ +\subsubsection{Read step} +During this step, text is turned from text into a more easily handlable format + + +\paragraph{Lexical Analysis} +During lexical analysis, the input is turned into tokens. +For example: The source code is \texttt{position := initial + rate + 60} + +The translation is: +\begin{multicols}{2} + \begin{enumerate} + \item Identifier \texttt{position} + \item Assignment symbol \texttt{:=} + \item Identifier \texttt{initial} + \item Addition symbol \texttt{+} + \item Identifier \texttt{rate} + \item Addition symbol \texttt{+} + \item Number \texttt{60} + \end{enumerate} +\end{multicols} + +It also removes whitespaces and comments + + +\paragraph{Parsing} +The tokens are then turned into an abstract syntax tree. + +The syntax is specified by a grammar such as: +\begin{align*} + Expr & ::= Identifier \divider Number \divider Expr\; \texttt{`+`}\; Expr \\ + Assign & ::= Identifier\; \texttt{`:=`}\; Expr +\end{align*} + +This can also be represented as a haskell type: +\begin{code}{haskell} + data Expr = Identifier Ident | Number Num | Plus Expr Expr + data Assign = Assignment Ident Expr + type Ident = String + type Num = Int +\end{code} + +Since some to be parsed statements are more complex to parse, we may do combinatory parsing. + +This is much more powerful as it can handle ambiguous grammars typically found in real programming languages. + +We can use for example these parser combinators: + +\newpage +\inputcode{haskell}{code/10_parser.hs} + +These are just some of the functions defined. +You can find a full mini-haskell and lambda-calculus parser on CodeExpert (at the time of writing this that was the case at least) diff --git a/semester4/fmfp/parts/02_typing/03_interpreter/02_eval.tex b/semester4/fmfp/parts/02_typing/03_interpreter/02_eval.tex new file mode 100644 index 0000000..2047ec6 --- /dev/null +++ b/semester4/fmfp/parts/02_typing/03_interpreter/02_eval.tex @@ -0,0 +1,3 @@ +\newpage +\subsection{Evaluation} +Evaluation is then done using tree traversal as we have already seen in the haskell section.