Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · –...

36
Chapter 5 Advanced Techniques in Functional Programming Programming Languages and Paradigms J. Fenwick, B. Kurtz, C. Norris (to be published in 2012)

Transcript of Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · –...

Page 1: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Chapter 5 Advanced Techniques in Functional Programming

Programming Languages and Paradigms J. Fenwick, B. Kurtz, C. Norris

(to be published in 2012)

Page 2: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Introduction • In the remainder of this chapter you will learn

about – Lisp – the first functional programming language – Haskell – a pure functional language designed in the

1990s where data is immutable and functions are not allowed to have side effects

– ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family of languages

• The chapter ends with a case study where you will build an interpreter for Wren Intermediate Code – You will use a record structure to represent the state of

the computation; instructions will be passed in the current state and return a modified state after execution

Page 3: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

The List Structure in LISP • A picture of a simple list

• Lisp was originally developed on an IBM 704 – 15 bits of a machine word were called the Contents of

the Address Register, shortened to CAR – Another 15 bits were called the Contents of the

Decrement Register, shortened to CDR – The CAR-CDR pair are said to form a CONS cell – L, I, S, P above are atoms

Page 4: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Accessing a List Structure

• The CAR and CDR functions simply follow pointers – (CAR L1) returns the atom L – (CDR L1) returns the list (I S P) – The (CAR (CDR L1)) returns the atom I; this is

abbreviated as (CADR L1) – What function call would return the list (P)? – What function call would return the atom P? – The List.head and List.tail functions in F# correspond to

the CAR and CDR functions in LISP

Page 5: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Building a List Structure

• The CAR and CDR functions decompose a list • The CONS function builds a list; it forms a CONS cell

– (CONS P () ) would return the list (P) – (CONS S (CONS P () ) ) would return the list (S P) – How would you build the list (L I S P)? – The infix :: function in F# corresponds to CONS;

‘L’ :: ‘I’ :: ‘S’ :: ‘P’ would build the list shown above; in F# lists are required to contain the same type

Page 6: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Decisions in LISP • The conditional function, COND, is used to select

between many alternatives; the general form is: (COND (<predicate1> <task1>) (<predicate2> < task2>) … )

• The first true predicate triggers the corresponding task; for example the absolute value of X would be returned by (COND ((< X 0) (-X)) (T (X)) ); the T in the predicate is True, if reached its task always fires

• The tasks are often function calls, including recursive function calls for named function

• In F# COND has been replaced with if…then…else…

Page 7: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Defining Functions in LISP • The general syntax for defining a function is:

(defun <name> (<params>) <body> ) • The body is often a COND function • The basic mechanism for repetition is recursion • Example: “my-member” returns nil if the item is not

found and returns the remainder of the list starting with the item if it is found

Page 8: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Comparing Simple LISP and F# functions

let append list1 list 2 = if list1 = [] then list2 else List.head list1 :: append (List.tail list1) list2

let length list = if list = [] then 0 else 1 + length List.tail list

Page 9: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Why Tuples are needed in F# • LISP was not a typed language so lists could mix

types; F# is strongly typed and requires lists to contain the same type (like arrays in Java)

• The tuple in F# allows the mixture of types; it is similar to struct in C or records in Ada; Java uses classes for this purpose

• Here is a picture of a tuple in F# ( , , )

“Bill” 43 ‘m’

Page 10: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Intermixing Lists and Tuples - 1 • Recall the List.zip function:

List.zip [‘a’; ‘b’; ’c’] [1; 2; 3] returns [ (‘a’, 1) ; (‘b’, 2); (‘c’, 3) ] a list of tuples

• This structure can be visualized as

,

‘a’ 1

,

‘b’ 2

,

‘c’ 3

Accessing elements, even in a complex structure, is just “pointer chasing”

Page 11: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Intermixing Lists and Tuples - 2 • Assume the function nonNegative returns true if its

argument is >= 0 and false otherwise • Recall the List.partition function:

List.partition nonNegative [1; -5; -2; 3; 6; -1] returns ( [1; 3; 6] , [-5; -2; -1] ) a tuple of lists

• This structure can be visualized as ,

1 3 6 -5 -2 -1

Page 12: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Other Features of Lisp • Supports high level functions

– mapcar applies the function parameter to every item in a list and returns a list of results

– This is the same as Lisp.map in F#

• Typing – Original Lisp is dynamically typed meaning the latest

binding is the current type – Put another way, the identifier itself does not have a

type but the current value of the identifier is typed

Page 13: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Haskell • Characteristics

– Statically typed and uses type inference – immutable data and functions don’t have side effects – Similar to F# in many ways but uses different syntax

• Cons is : (in F# it is ::) • Append is ++ (in F# it is @), and so forth

– Indentation is important (similar to F#) – Supports pattern matching – Supports function currying – Supports list comprehensions – Uses lazy evaluation as the default (as contrasted to F#

where the default is eager evaluation) – The textbook contains many example functions

Page 14: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

ML • Characteristics

– Statically typed, statically scope, uses type inference – Supports built-in higher ordered functions like map – Supports function closures – Unlike Haskell and similar to F# in using eager evaluation – Many examples are included in the textbook

• A bit of history – Started with Standard ML, Caml is a descendant – OCaml is Caml with objects – F# is considered to be a descendant of OCaml

Page 15: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Implementing a WIC Interpreter in F# • Will function the same as the C interpreter and has

similar structure • Decompose you program into four modules

– A table implementation module (table.fs) for the jump table, symbol table, and table of instructions

– An instructions module (instructions.fs) with functions for each of the instructions in WIC

– A preprocessing module (preprocess.fs) will produce the initial state of computation

– A main program (interpreter.fs) initializes everything and then interprets the WIC; it is relatively short since the real work is done elsewhere

Page 16: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Importance of Position in Solution Explorer • When using F# in Visual Studio the modules will be

listed at the right side of the screen – Important: each module can only see items in itself and

the modules above it – You cannot see functions or types in modules below – If you right click on the file name in Solution Explorer

you will have options to move the file up or down

Page 17: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Functions for tables • The ST and JT will have type string * int list

– The function retrieve identifier table returns the integer value associated with identifier; throws an exception and message using failwith if the identifier is not found

– The function store identifier value table if the identifier is already in the table then the value is updated to the given value; otherwise a new tuple is formed and added to the table. Store returns the updated table

Page 18: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Functions for tables • The instruction table will have type string * string

list – The first entry in the instruction tupe is the opcode and

the second entry is the operand – If there is no operand, an empty string is stored – The function fetchOpcode location instructions returns the opcode at the specified location; location numbering starts at 0 so use List.nth

– The function fetchOpcode location instructions returns the operand at the specified location; it may be the empty string for many instructions

Page 19: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.09 • Start implementation of table.fs

– Implement the ST and JT functions store and retrieve – Test the JT functions with >let JT = [(“L1”, 2); (“L2”, 18); (“L3”, 23); (“L4”, 24)];

– Implement the instruction table functions fetchOpcode and fetchOperand

– Test the instruction table functions with > let instructions = [("get", "A"); ("get", "B"); ("put", "A"); ("put", "B"); ("halt","")];

Page 20: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Reading the WIC From a File • Here is the code

open System;

open System.IO;

open System.Collections.Generic;

open System.Windows.Forms;

let readFile =

let ofd = new OpenFileDialog() in

ofd.Title <- "Open a WIC File";

if ofd.ShowDialog() = DialogResult.OK then

Array.toList(File.ReadAllLines(ofd.FileName))

else

[]

• It should produce a result like val readFile : string list = ["get X"; "get Y"; "push X"; "push Y"; "pop X"; "pop Y";"put X"; "put Y"; "halt"]

The OpenFileDialog allows the user to browse the file system and select an input file.

Page 21: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Building the Instruction List and JT • Processing the list of instructions from the file

– An instruction such as “get A” must be added to the list of instruction tuples as (“get”, “A”)

– F# supports a string split function, <str name>.Split(‘ ‘) • This returns an array of Strings; items are indexed by using the

syntax <array name>.[0], <array name>.[1], and so forth • Arrays are OK in functional programming provided they are

immutable (that is, individual elements are not assigned new values)

– Handling a label instruction, such as “L1 label” at position 5

• Put the nop (no operation) instruction in the instruction table • Put the tuple (“L1”, 5) into the jump table

Page 22: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Representing the State of the Computation • Define the record structure type state = {

instructions: (string*string) list;

PC: int;

stack: int list;

ST: (string * int) list;

JT: (string * int) list;

}

• Creating a new machine state – Suppose the instruction only increments the PC value – The new state would be created as { state with PC = state.PC + 1 }

– All other fields would remain unchanged

If you defined named table types in table.fs, use those names here

Page 23: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.10 • Start implementation of preprocess.fs

– After reading the WIC create the instruction table and the JT

– An initialize function should return the initial state of computation for the program

• Include the instruction table, it never changes • Include the PC, it is initially set to 0 • Include the runtime stack, it is initially empty • Include the ST, it is initially empty • Include the JT, it never changes

• Test your preprocess.fs on the WIC file with the gcd program; verify the instruction table and JT are correct

Page 24: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

The main program • executeInstruction in interpreter.fs will contain the

following structure match (fetchOpcode state.PC state.instructions) with

|"get" ->

executeInstructions doGet state

|"put" ->

executeInstructions doPut state

|"push" ->

executeInstructions doPush state

Page 25: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

First Group of Instructions • The first group of instructions will be put, get, push,

pop, halt, and nop • Here is the doPut instruction let doPut state =

// retrieve the operand from the list of instructions

let operand = fetchOperand state.PC state.instructions

// fetch the value from the symbol table

let value = retrieve operand state.ST

// print the operand and value to the console

printfn "%s = %d\n" operand value

// the only item changed in the new state is the PC

{ state with PC = state.PC+1 }

• The other instructions will have a similar format • Remember push is either a literal numeric value or

a variable whose value is in the ST

Page 26: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.11 • Implement get, put, push, pop, halt, and nop • Place these functions in instructions.fs • Test your program with get A

get B

push A

push B

pop A

pop B

put A

put B

halt

This program will reverse the values of A and B

Page 27: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Arithmetic Instructions • Here is the logic for an add instruction let add state =

let num1 = state.stack.Head

let num2 = state.stack.Tail.Head

{ state with PC = state.PC + 1;

stack = num2 + num1 :: state.stack.Tail.Tail }

• The other arithmetic instructions will have a very similar format

• You can write a generic doArithmeticOp instruction let doArithmeticOp state op = …

• The call to this function will be something like |"add" ->

executeInstructions doArithmeticOp state (+)

Page 28: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.12 • Implement the four arithmetic instructions; call

failwith on a divide by zero error • Test your program with get A

get B

push A

push B

add

pop Sum

put Sum

// expand to calculate

// and print sub, mult, divide

Page 29: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Logical Instructions • Here is the not instruction let doNot state =

let num1 = state.stack.Head

let value =

if num1 <> 0 then

0

else

1

{ state with PC = state.PC + 1;

stack = value :: state.stack.Tail }

• The and and or instructions will have a format similar to the arithmetic instructions

Page 30: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.13 • Implement the three logical instructions • Test your program with get X1 or

get X2 push X1

get X3 not

get X4 push X2

push X1 or

push X2 push X4

not or

or and

push X3 pop Result

not pop Result

halt

Page 31: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Test Instructions • Here is the logic for a test less than instruction let testlt state =

let value = if state.stack.Head < 0 then 1 else 0

{state with PC = state.PC + 1; stack = value :: state.stack.Tail}

• The other test instructions will have a very similar format

• You can write a generic doTest instruction let doTest state op = …

• The call to this function will be something like |“tstlt" ->

executeInstructions doTest state (<)

Page 32: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.14 • Implement the six test instructions in a single

doTest function • Test your program with get A

get B

push A

push B

sub

sub

tstlt

pop LTresult

put LTresult

// expand to test the other

// five comparisons

halt

Page 33: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Jump Instructions • Here is the unconitional jump instruction let doJump state =

// retrieve the operand from the list of instructions

let operand = fetchOperand state.PC state.instructions

// fetch the value from the jump table

let value = retrieve operand state.JT

// assign the PC this value

{ state with pc = value }

• The conditional jump instruction will be a bit more complex; remember it comes after a test instruction

Page 34: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.15 • Test the jump instructions with get A

get B

push A

push B

sub

tstlt

jf L1

push B

pop MAX

j L2

L1 label

push A

pop MAX

L2 label

put MAX

halt

This program will find the largest of A and B

Page 35: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Lab Activity ch05.16

• Another test program get num

push 0

pop count

L1 label

push num

push 0

sub

tstgt

jf L2

push num

push 2

div

pop num

push count

push 1

add

pop count

j L1

L2 label

put count

halt

This program will print the number of binary digits in num

Page 36: Chapter 5 Advanced Techniques in Functional Programmingblk/cs3490/ch05/ch05.02.03slides.pdf · – ML – designed in the 1970s, it introduced type inferencing; F# is in the ML family

Testing the gcd program • First test the interactive version of the gcd program • Modify the program to remove the interactivity • Add the following timing functions let stopWatch = Stopwatch.StartNew()

...

stopWatch.Stop()

printfn "%f" stopWatch.Elapsed.TotalMilliseconds

• Time the gcd with M = 100000 and N = 1; continue increasing the value for M and collect timing data