CSCI 4430 Programming Languages
This is an individual assignment. Just as with all other
homework, submitted work should be your own. Course staff runs plagiarism
detectors and will treat excessive similarities between submissions as evidence
of cheating. Submit in Submitty for autograding. Your Haskell file must be named Interpreter.hs. Submitty compiles your Interpreter module using
The
Glorious Glasgow Haskell Compilation System, version 8.6.5 and links it with various tests that call your functions.
Download and install the Glasgow
Haskell Compiler: https://www.haskell.org/ghc.
You can run GHC in interactive
mode by running ghci from the command line. Try all examples from
lecture before starting on the homework. You can type the functions from
lecture into a file called, e.g., fun.hs:
apply_n f n x = if n == 0 then x else apply_n f (n-1) (f x)
plus a b = apply_n ((+) 1) b a
mult a b = apply_n ((+) a) b 0
expon a b = apply_n ((*) a) b 1
Run ghci then load the file by typing
:l fun.hs
and call these functions, e.g., plus 2 3. You
can reload the file after making a change
:r
Add ADTs and functions to your file. E.g., the Yabi interpreter from HW4 is a good exercise to start with and get more familiar with Haskell syntax and type error messages.
Now, we will describe the actual homework. In lecture, we wrote several interpreters
for the Lambda Calculus. They terminated when they reached an answer in Weak
Head Normal Form. In this homework you will write a normal order
interpreter that yields an answer in Normal Form (i.e., the expression
cannot be further reduced).
Your first task is to write an interpreter in the style we gave in lecture
(Lecture 19). Please include your answer in comments in your Haskell file under
heading Problem 1.
Next, you will code an interpreter in Haskell. Note however, that you will
not be coding the recursive interpretation from Problem 1 but a step-by-step
interpretation. The key function is the oneStep
function. It takes a lambda expression and either (i)
carries out one step of evaluation yielding the next expression, or (ii)
yields Nothing, meaning that the expression is already in the target normal
form.
A lambda expression is of the following form, as we discussed in class:
data Expr =
Var Name --- a variable
| App Expr Expr --- function application
| Lambda Name Expr --- lambda abstraction
Deriving
(Eq,Show) --- the Expr data type derives from built-in Eq and Show classes,
--- thus, we can compare and print expressions
type Name = String --- a variable name
You need to implement the following
functions:
Free variables. Write the function to compute the list of free
variables in an expression.
freeVars :: Expr -> [Name]
It takes an expression and returns the list of free variables in that
expression. For example, freeVars (App (Var "x") (Var "x")) returns ["x"].
You will use freeVars to generate an (infinite) list of variables that are not free in
an expression; variables from that list can be used as fresh variables
when needed for substitution. For example, given expression Lambda "1_"
(App (Var "x") (App (Var "1_") (Var "2_"))), the infinite list of potential fresh variables is ["1_","3_","4_","5_",..]. Remember, you will make use of the
infinite list of integers [1..] for
this purpose.
Substitution. Next, write the substitution function
subst :: (Name,Expr)
-> Expr -> Expr
All functions in Haskell are curried: i.e., they take just one
argument. The above function takes a variable x and an
expression e, and returns
a function that takes an expression E and returns E[e/x]. Important:
subst needs to implement the algorithm we gave in class
(Lecture 15). Specifically, when performing a substitution of x with e on a lambda expression λy.E1, where y≠x, subst replaces parameter y with the next fresh variable from the infinite
list of potential fresh variables, even if y is not
free in e and it would have been safe to leave it as is. (This is
necessary for autograding on Submitty.
An alternative would have been to normalize the expression using DeBruijn indices.) Remember, the infinite list of potential
fresh variables is ["1_","2_","3_"..] excluding variables that are free
in
x, E1 or e.
A single step. Now write a function to do a single step of reduction:
normNF_OneStep :: Expr -> Maybe Expr
where the built-in Maybe type is defined as follows:
data Maybe a =
Nothing
| Just a
normNF_OneStep takes an expression e. If there is a redex
available in e, it picks the
correct normal order redex and reduces e. If a reduction was carried out,
resulting in a new expression expr', normNF_OneStep returns Just expr', otherwise, i.e., the input expression is
already in normal form, normNF_OneStep returns Nothing.
Repetition. Finally, write a function
normNF_n :: Int -> Expr ->
Expr
Given an integer n and an expression e, normNF_n does n reductions (or as many as possible) and returns
the resulting expression.
Minimal starter code is provided here.
Notes on grading: This homework is worth a total of 50 points: 40
points will be awarded for functional correctness by the autograder
and 10 points will be awarded for quality and completeness of Problem 1 and
comments.
None yet. Check the Announcements on Submitty regularly.