Homework 3
Working with semantics
This homework is written in literate Haskell; you can download the raw source to fill in yourself. You’re welcome to submit literate Haskell yourself, or to start fresh in a new file, literate or not.
Please submit homeworks via the DCI submission page.
For the rest of this course, you’re free to use any functions from the Prelude or standard libraries that come with the Haskell platform, e.g., Data.Map
and Data.Set
. That said—don’t use anything you don’t understand! If you can’t explain your solution to a homework problem, I don’t care whether or not it works—you’ll get a zero.
module Hw03 where
import Prelude
Okay—let’s start actually studying programming languages.
Problem 1: compiling expressions to a stack machine
The eval
function from the last homework is an interpreter: it takes in an abstract syntax tree (AST) and runs it immediately to produce a result. The translate
function you defined last week, from ArithExp'
to ArithExp
, is a miniature compiler. Let’s write a marginally less minimal compiler: we’ll translate arithmetic expressions to run on a tiny stack machine.
Our stack machine’s assembly language will have four instructions.
data Instruction =
IPush Int
| IPlus
| ITimes
| INeg
deriving Show
type Stack = [Int]
(a) 10 points
Implement a function that runs a single instruction. Here is the reduction relation:
Command | Input stack | Output stack | |
---|---|---|---|
IPush n | s | –> | n:s |
IPlus | n1:n2:s | –> | n1 + n2:s |
ITimes | n1:n2:s | –> | n1 * n2:s |
INeg | n:s | –> | (0-n):s |
Before you write this code, try to explain to yourself why the return type is Maybe Stack
.
instruction :: Instruction -> Stack -> Maybe Stack
instruction = undefined
Were you right?
(b) (10 points)
Explain what can go wrong when instruction
runs. What kind of errors can happen, and when? Why can instruction
run into errors but not the eval
function from last week?
(c) (5 points)
The instruction
function lets you run an individual step of our stack machine. Finish the function program
that runs multiple steps of the stack machine, where a program is just a list of instructions interpreted from front to back. (This is just like taking the reflexive, transitive closure of the instruction
function.)
For example, program [IPush 21,IPush 21,IPlus] []
should evaluate to Just [42]
.
type Program = [Instruction]
program :: Program -> Stack -> Maybe Stack
program p s = undefined
(d) (10 points)
Write a function depth
which can predict the depth of the output stack given a program and the depth of its input stack. Return Nothing
if the program will raise an error.
depth :: Program -> Int -> Maybe Int
depth = undefined
(e) (10 points)
Write a compiler from our original ArithExp
language (redefined here) to Program
s. You can use the function runAndCompile
below to test your code.
data ArithExp =
Num Int
| Plus ArithExp ArithExp
| Times ArithExp ArithExp
| Neg ArithExp
deriving (Eq,Show)
compile :: ArithExp -> Program
compile = undefined
runAndCompile :: ArithExp -> Int
runAndCompile e =
case program (compile e) [] of
Just [x] -> x
_ -> error "Your compiler is buggy!"
Problem 2: lambda calculus reduction
Please do problem 4.3 from Mitchell, page 83.
Problem 3: symbolic reduction
Please do problem 4.4 from Mitchell, page 83.
Problem 4: Church numerals
In class we defined Church numerals and booleans, and showed how to define more complex functions in the pure lambda calculus. Please show how to define the following functions in the pure lambda calculus. (You may use the functions defined in class in defining these new functions.)
Do these questions on paper, not in Haskell.
(a) (5 points)
Define a function minus
such that minus m n
reduces to m - n
if m
> n
and 0 otherwise. Do not use recursion, but instead define it directly. You may assume that you are given the function pred
defined in class that computes the predecessor of a number (where the predecessor of 0 is 0, predecessor of 1 is 0, 2 is 1, etc.). That is, your answer may include the term pred
without providing a definition of it.
minus m n =
fill in here
(b) (5 points)
Define a function lessThan
such that lessThan m n
reduces to true
iff m
< n
.
lessThan m n =
fill in here
(c) (5 points)
Define the recursive Fibonacci function fib
such that fib 0
reduces to 1, fib 1
reduces to 1, and fib n
reduces to fib (n-1) + fib (n-2)
for n
> 1. This is tricky as you are not allowed to name the function and use the name in the definition. You must use the Y combinator and emulate what we did in class defining factorial
.
You can just write Y
for the combinator.
fill in here
(d) (5 points)
Use your definition of fib from above to calculate fib 2. Do it step by step, showing all of your work. You may assume that fib 1
and fib 0
both reduce to 1, that pred 2
reduces to 1, and that plus 1 1
reduces to 2 without showing all of the reduction steps. All other steps in the reduction should be shown. You can write Church numerals as actual numbers, i.e., 0 to mean \s z. z
and 1 to \s z. s z
.
Note that I just want the steps—you don’t need to write derivations for every step taken!
fib 2 =
(your definition of fib) 2 -->
... fill in the steps ...