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 Programs. 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 ...