121
1 CS 538 Spring 2008 © CS 538 Introduction to the Theory and Design of Programming Languages Charles N. Fischer Spring 2008 http://www.cs.wisc.edu/~fischer/cs538.html 2 CS 538 Spring 2008 © Class Meets Mondays, Wednesdays & Fridays, 9:55 — 10:45 1325 Computer Sciences Instructor Charles N. Fischer 6367 Computer Sciences Telephone: 608.262.6635 E-mail: [email protected] Office Hours: 10:30 - Noon, Tuesdays & Thursdays, or by appointment 3 CS 538 Spring 2008 © Teaching Assistant Jongwon Yoon 3361 Computer Sciences Telephone: 608.354.3613 E-mail: [email protected] Office Hours: 2:00 - 3:00, Mondays, Wednesdays and Fridays, or by appointment 4 CS 538 Spring 2008 © Key Dates Feb 25: Homework #1 (tentative) March 24: Programming Assignment #1 - Scheme (tentative) April 2: Midterm Exam (tentative) April 16: Programming Assignment #2 - Standard ML (tentative) May 2: Programming Assignment #3 - Prolog (tentative) May 9: Programming Assignment #4 - Java, C#, Pizza and Python May 15: Final Exam 2:45pm-4:45pm

UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

1CS 538 Spring 2008©

CS 538

Introduction to the Theory andDesign of Programming

Languages

Charles N. Fischer

Spring 2008

http://www.cs.wisc.edu/~fischer/cs538.html

2CS 538 Spring 2008©

Class MeetsMondays, Wednesdays &Fridays,9:55 — 10:451325 Computer Sciences

InstructorCharles N. Fischer6367 Computer SciencesTelephone: 608.262.6635E-mail: [email protected] Hours:

10:30 - Noon, Tuesdays &Thursdays, or byappointment

3CS 538 Spring 2008©

Teaching AssistantJongwon Yoon3361 Computer SciencesTelephone: 608.354.3613E-mail: [email protected] Hours:

2:00 - 3:00, Mondays,Wednesdays and Fridays,or by appointment

4CS 538 Spring 2008©

Key Dates• Feb 25: Homework #1 (tentative)• March 24: Programming Assignment #1 -

Scheme (tentative)• April 2: Midterm Exam (tentative)• April 16: Programming Assignment #2 -

Standard ML (tentative)• May 2: Programming Assignment #3 -

Prolog (tentative)• May 9: Programming Assignment #4 -

Java, C#, Pizza and Python• May 15: Final Exam 2:45pm-4:45pm

Page 2: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

5CS 538 Spring 2008©

Class Text• Required text:

“Modern ProgrammingLanguages,” Adam Webber,Franklin, Beedle & Associates,2003.

• Handouts and Web-based reading willalso be used.

Reading Assignment• Webber: Chapters 1, 10, 18

(as background)

6CS 538 Spring 2008©

Class Notes• Each lecture will be made available

prior to that lecture on the class Webpage (under the “Lecture Nodes” link).

Instructional ComputersDepartmental Linux Machines(king01- king12, emperor01-emperor40) have beenassigned to CS 538. Allnecessary compiler,interpreters and tools will beloaded onto these machines.

7CS 538 Spring 2008©

You may also use your own PCor laptop. It will be yourresponsibility to load neededsoftware (instructions on whereto find needed software areincluded on the class webpage).

The Systems Lab teaches brieftutorials on Linux if you areunfamiliar with that OS.

8CS 538 Spring 2008©

Academic Misconduct Policy• You must do your own assignments —

no copying or sharing of solutions.

• You may discuss general concepts andIdeas.

• All cases of misconduct must bereported to the Dean’s office.

• Penalties may be severe.

Page 3: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

9CS 538 Spring 2008©

Program & Homework LatePolicy• An assignment may be handed in up

to 7 days late, but no later.

• Each day late will be debited 4%, up toa maximum of 28%.

• All students are given 10 “free” latedays. That is, the first 40% in latedebits will be automatically forgiven.

• Your 10 free late days my be used atany time, and in any combination.

10CS 538 Spring 2008©

Approximate Grade WeightsHomework 1 10%Program 1 - Scheme 16%Program 2 - ML 16%Program 3 - Prolog 12%Program 4 - Java, C#, Pizza and Python (optional extra credit) 10%Midterm Exam 23%Final Exam (non-cumulative) 23%

11CS 538 Spring 2008©

Programming Languages to beConsidered in Detail1. Scheme

A modern variant of Lisp.A Functional Language:Functions are “first class” datavalues.Dynamically Typed:A variable’s type may changeduring execution; no typedeclarations are needed.All memory allocation anddeallocation is automatic.Primary data structures, listsand numbers, are unlimited insize and may grow withoutbound.

12CS 538 Spring 2008©

Continuations provide a novelway to suspend and “re-execute” computations.

2. ML (“Meta Language”)Strong, compile-time typechecking.Types are determined byinference rather thandeclaration.Naturally polymorphic (onefunction declaration can beused with many differenttypes).Pattern-directed programming(you define patterns that areautomatically matched during acall).

Page 4: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

13CS 538 Spring 2008©

Typed exceptions are provided.Abstract data types, withconstructors, are included.

3. Prolog (Programming in Logic)Programs are Facts and Rules.Programmers are concernedwith definition, not execution.Execution order isautomatically determined.

14CS 538 Spring 2008©

4. PizzaExtends a popular Object-oriented language, Java, toinclude• Parametric polymorphism (similar

to C++’s templates).

• First-class functional objects.

• Algebraic data types, includingpatterns.

5. C#Microsoft’s answer to Java. Inmost ways it is very similar toJava, with some C++ conceptsreintroduced and some usefuladditions.

15CS 538 Spring 2008©

• Events and delegates are includedto handle asynchronous actions(like keyboard or mouse actions).

• Properties allow user-defined readand write actions for fields.

• Indexers allow objects other thanarrays to be indexed.

• Collection classes may be directlyenumerated:foreach (int i in array) ...

• Structs and classes co-exist andmay be inter-converted (boxed andunboxed).

• Enumerations, operatoroverloading and rectangular arraysare provided.

• Reference, out and variable-lengthparameter lists are allowed.

16CS 538 Spring 2008©

6. Java 1.5 (Tiger Java, Java 5.0)Extends current definition ofJava to include:• Parametric polymorphism

(collection types may beparameterized).

• Enhanced loop iterators.

• Automatic boxing and unboxing ofwrapper classes.

• Typesafe enumerations.

• Static imports (out.println ratherthan System.out.println).

• Variable argument methods.

• Formatted output using printf:out.printf("Ans = %3d",a+b);

Page 5: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

17CS 538 Spring 2008©

7. PythonA simple, efficient scriptinglanguage that quickly buildsnew programs out of existingapplications and libraries.It cleanly includes objects.It scales nicely into largerapplications.

18CS 538 Spring 2008©

Evolution of ProgrammingLanguages

In the beginning, ...programs were written inabsolute machine code—asequence of bits that encodemachine instructions.Example:

340200050000000c3c011001ac220000

This form of programming is• Very detailed

• Very tedious

• Very error-prone

• Very machine specific

19CS 538 Spring 2008©

Symbolic AssemblersAllow use of symbols foroperation codes and labels.Example:

li $v0,5syscallsw $v0,a

Far more readable, but still verydetailed, tedious and machine-specific.Types are machine types.Control structures areconditional branches.Subprograms are blocks ofcode called via a “subroutinebranch” instruction.All labels are global.

20CS 538 Spring 2008©

Fortran (Formula Translator)Example:

do 10 i=1,100

10 a(i)=0

Developed in the mid-50s.A major step forward:• Programming became more

“problem oriented” and less“machine oriented.”

• Notions of control structures (ifsand do loops) were introduced.

• Subprograms, calls, andparameters were made available.

• Notions of machine independencewere introduced.

• Has evolved into many newvariants, including Fortran 77,Fortran 90 and HPF (HighPerformance Fortran).

Page 6: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

21CS 538 Spring 2008©

Cobol (Common BusinessOriented Language)

Example:multiply i by 3 giving j.move j to k.write line1 after advancing1 lines.

Developed in the early 60s.The first widely-standardizedprogramming language.Once dominant in the businessworld; still important.Wordy in structure; designedfor non-scientific users.Raised the issue of who shouldprogram and how importantreadability and maintainabilityare.

22CS 538 Spring 2008©

Algol 60 (Algorithmic Language)Example:

real procedure cheb(x,n);value x,n;real x; integer n;cheb :=

if n = 0 then 1 else if n = 1 then x else 2 × x × cheb(x,n-1)-cheb(x,n-2);

Developed about 1960.A direct precursor of Pascal, C,C++ and Java.Introduced many ideas now inwide use:• Blocks with local declarations and

scopes.

• Nested declarations and controlstructures.

23CS 538 Spring 2008©

• Parameter passing

• Automatic recursion.

But,• I/O wasn’t standardized.

• IBM promoted Fortran and PL/I.

24CS 538 Spring 2008©

Lisp (List Processing Language)Example:

((lambda (x) (* x x)) 10)

Developed in the early 60s.A radical departure from earlierprogramming languages.Programs and data arerepresented in a uniform listformat.Types are a property of datavalues, not variables orparameters.A program can build and runnew functions as it executes.Data values were not fixed insize.

Page 7: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

25CS 538 Spring 2008©

Memory management wasautomatic.A formal semantics wasdeveloped to define preciselywhat a program means.

26CS 538 Spring 2008©

Simula 67 (Simulation Algol)Example:

Class Rectangle (Width, Height);Real Width, Height;Boolean Procedure IsSquare; IsSquare := Width=Height;End of Rectangle;

Developed about 1967.Introduced the notion of a class(for simulation purposes).Included objects, a garbagecollector, and notions ofextending a class.C++ was originally C withclasses (as Simula was Algolwith classes).

27CS 538 Spring 2008©

C and C++C was developed in the early70’s; C++ in the mid-80s.These languages have aconcise, expressive syntax;they generate high quality codesufficient for performance-critical applications.C, along with Unix, proved theviability of platform-independent languages andapplications.C and C++ allow programmersa great deal of freedom inbending and breaking rules.Raises the issue of whether onelanguage can span both noviceand expert programmers.

28CS 538 Spring 2008©

Interesting issue—if moststatements and expressions aremeaningful, can errors bereadily detected?

if (a=b)

a=0;

else a = 1;

Page 8: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

29CS 538 Spring 2008©

JavaDeveloped in the late 90s.Cleaner object-orientedlanguage than C++.Introduced notions of dynamicloading of class definitionsacross the Web.Much stronger emphasis onsecure execution and detectionof run-time errors.Extended notions of platformindependence to systemindependence.

30CS 538 Spring 2008©

What Drives Research intoNew Programming Languages?Why isn’t C or C++ or C+++enough?1. Curiosity

What other forms can aprogramming language take?What other notions ofprogramming are possible?

2. ProductivityProcedural languages,including C, C++ and Java, arevery detailed.Many source lines implysignificant development andmaintenance expenses.

31CS 538 Spring 2008©

3. ReliabilityToo much low-level detail inprograms greatly enhances thechance of minor errors. Minorerrors can raise significantproblems in applications.

4. SecurityComputers are entrusted withgreat responsibilities. How canwe know that a program is safeand reliable enough to trust?

5. Execution speedProcedural languages areclosely tied to the standardsequential model of instructionexecution. We may needradically different programmingmodels to fully exploit paralleland distributed computers.

32CS 538 Spring 2008©

Desirable Qualities in aProgramming Language

Theoretically, all programminglanguages are equivalent (Why?)If that is so, what properties aredesirable in a programminglanguage?

• It should be easy to use.Programs should be easy to read andunderstand.Programs should be simple to write,without subtle pitfalls.It should be orthogonal, providingonly one way to do each step orcomputation.Its notation should be natural for theapplication being programed.

Page 9: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

33CS 538 Spring 2008©

• The language should supportabstraction.

You can’t anticipate all needed datastructures and operations, so addingnew definitions easily and efficientlyshould be allowed.

• The language should supporttesting, debugging andverification.

• The language should have a gooddevelopment environment.

Integrated editors, compilers,debuggers, and version control are abig plus.

• The language should be portable,spanning many platforms andoperating systems.

34CS 538 Spring 2008©

• The language should beinexpensive to use:

Execution should be fast.Memory needs should be modest.Translation should be fast andmodular.Program creation and testing shouldbe easy and cheap.Maintenance should not be undulycumbersome.Components should be reusable.

35CS 538 Spring 2008©

Programming ParadigmsProgramming languagesnaturally fall into a number offundamental styles orparadigms.

Procedural LanguagesMost of the widely-known andwidely-used programminglanguages (C, Fortran, Pascal,Ada, etc.) are procedural.Programs execute statement bystatement, reading andmodifying a shared memory.This programming style closelymodels conventional sequentialprocessors linked to a randomaccess memory (RAM).

36CS 538 Spring 2008©

Question:Givena = a + 1;

if (a > 10) b = 10; else b = 15; a = a * b;

Why can’t 5 processors eachexecute one line to make theprogram run 5 times faster?

Page 10: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

37CS 538 Spring 2008©

Functional LanguagesLisp, Scheme and ML arefunctional in nature.Programs are expressions to beevaluated.Language design aims tominimize side-effects, includingassignment.Alternative evaluationmechanisms are possible,includingLazy (Demand Driven)

Eager (Data Driven orSpeculative)

38CS 538 Spring 2008©

Object-Oriented LanguagesC++, Java, Smalltalk, Pizza andPython are object-oriented.Data and functions areencapsulated into Objects.Objects are active, havepersistent state, and uniforminterfaces (messages ormethods).Notions of inheritance andcommon interfaces are central.All objects that provide thesame interface are treateduniformly. In Java you can printany object that provides thetoString method. Iterationthrough the elements of anyobject that implements theEnumeration interface is possible.

39CS 538 Spring 2008©

Subclassing allows to youextend or redefine part of anobject’s behavior withoutreprogramming all of theobject’s definition. Thus in Java,you can take a Hashtable class(which is fairly elaborate) andcreate a subclass in which anexisting method (like toString)is redefined, or new operationsare added.

40CS 538 Spring 2008©

Logic ProgrammingLanguages

Prolog notes that mostprogramming languagesaddress both the logic of aprogram (what is to be done)and its control flow (how youdo what you want).A logic programming language,like Prolog, lets programmersfocus on a program’s logicwithout concern for controlissue.These languages have no realcontrol structures, and littlenotion of “flow of control.”What results are programs thatare unusually succinct andfocused.

Page 11: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

41CS 538 Spring 2008©

Example:inOrder( [] ).inOrder( [ _ ] ).inOrder([a,b|c]) :- (a<b),

inOrder([b|c]).

This is a complete, executablefunction that determines if alist is in order. It is naturallypolymorphic, and is notcluttered with declarations,variables or explicit loops.

42CS 538 Spring 2008©

Review of Concepts fromProcedural ProgrammingLanguages

Declarations/Scope/Lifetime/BindingStatic/Dynamic

• Identifiers are declared, eitherexplicitly or implicitly (from context offirst use).

• Declarations bind type and kindinformation to an identifier. Kindspecifies the grouping of an identifier(variable, label, function, type name,etc.)

• Each identifier has a scope (or range)in a program—that part of theprogram in which the identifier isvisible (i.e., may be used).

43CS 538 Spring 2008©

• Data objects have a lifetime—the spanof time, during program execution,during which the object exists andmay be used.

• Lifetimes of data objects are often tiedto the scope of the identifier thatdenotes them. The objects are createdwhen its identifier’s scope is entered,and they may be deleted when theidentifier’s scope is exited. Forexample, memory for local variableswithin a function is usually allocatedwhen the function is called (activated)and released when the call terminates.In Java, a method may be loaded intomemory when the object it is amember of is first accessed.

44CS 538 Spring 2008©

Properties of an identifier (and theobject it represents) may be set at• Compile-time

These are static properties asthey do not change duringexecution. Examples includethe type of a variable, the valueof a constant, the initial valueof a variable, or the body of afunction.

• Run-time

These are dynamic properties.Examples include the value of avariable, the lifetime of a heapobject, the value of a function’sparameter, the number of timesa while loop iterates, etc.

Page 12: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

45CS 538 Spring 2008©

Example:In Fortran• The scope of an identifier is the whole

program or subprogram.

• Each identifier may be declared onlyonce.

• Variable declarations may be implicit.(Using an identifier implicitly declaresit as a variable.)

• The lifetime of data objects is thewhole program.

46CS 538 Spring 2008©

Block Structured Languages• Include Algol 60, Pascal, C and Java.

• Identifiers may have a non-globalscope. Declarations may be local to aclass, subprogram or block.

• Scopes may nest, with declarationspropagating to inner (contained)scopes.

• The lexically nearest declaration of anidentifier is bound to uses of thatidentifier.

47CS 538 Spring 2008©

Binding of an identifier to itscorresponding declaration isusually static (also calledlexical), though dynamicbinding is also possible.Static binding is done prior toexecution—at compile-time.Example (drawn from C):

int x,z;void A() {float x,y;

print(x,y,z);

}void B() { print (x,y,z)

}

floatfloat

int

int

intundeclared

48CS 538 Spring 2008©

Block Structure Concepts• Nested Visibility

No access to identifiers outsidetheir scope.

• Nearest Declaration Applies

Static name scoping.• Automatic Allocation and Deallocation

of Locals

Lifetime of data objects isbound to the scope of theIdentifiers that denote them.

Page 13: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

49CS 538 Spring 2008©

Variations in these rules ofname scoping are possible.For example, in Java, thelifetime of all class objects isfrom the time of their creation(via new) to the last visiblereference to them.Thus ... Object O;...creates an object reference butdoes not allocate any memoryspace for O.You need ... Object O = new Object(); ...to actually create memoryspace for O.

50CS 538 Spring 2008©

Dynamic ScopingAn alternative to static scopingis dynamic scoping, which wasused in early Lisp dialects (butnot in Scheme, which isstatically scoped).Under dynamic scoping,identifiers are bound to thedynamically closest declarationof the identifier. Thus if anidentifier is not locallydeclared, the call chain(sequence of callers) isexamined to find a matchingdeclaration.

51CS 538 Spring 2008©

Example: int x;

void print() {

write(x); }

main () {

bool x;

print();

}

Under static scoping the xwritten in print is the lexicallyclosest declaration of x, whichis as an int.Under dynamic scoping, sinceprint has no local declarationof x, print’s caller is examined.Since main calls print, and ithas a declaration of x as a bool,that declaration is used.

52CS 538 Spring 2008©

Dynamic scoping makes typechecking and variable accessharder and more costly thanstatic scoping. (Why?)However, dynamic scopingdoes allow a notion of an“extended scope” in whichdeclarations extend tosubprograms called within thatscope.Though dynamic scoping mayseen a bit bizarre, it is closelyrelated to virtual functionsused in C++ and Java.

Page 14: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

53CS 538 Spring 2008©

Virtual FunctionsA function declared in a class,C, may be redeclared in a classderived from C. Moreover, foruniformity of redeclaration, it isimportant that all calls,including those in methodswithin C, use the newdeclaration.Example:

class C {

void DoIt()(PrintIt();} void PrintIt() {println(“C rules!”);} } class D extends C { void PrintIt() {println(“D rules!”);} void TestIt() {DoIt();} } D dvar = new D(); dvar.TestIt();

D rules! is printed.

54CS 538 Spring 2008©

Scope vs. LifetimeIt is usually required that thelifetime of a run-time object atleast cover the scope of theidentifier. That is, wheneveryou can access an identifier, therun-time object it denotesbetter exist.But,it is possible to have a run-timeobject’s lifetime exceed thescope of its identifier. Anexample of this is static or ownvariables.

55CS 538 Spring 2008©

In C:void p() {

static int i = 0; print(i++); }

Each call to p prints a differentvalue of i (0, 1, ...) Variable iretains its value across calls.Some languages allow anexplicit binding of an identifierfor a fixed scope:

A declaration may appearwherever a statement orexpression is allowed. Limitedscopes enhance readability.

Letid = val

in statementsend;

{ type id = val; statements}

56CS 538 Spring 2008©

Structs vs. BlocksMany programming languages,including C, C++, C#, Pascaland Ada, have a notion ofgrouping data together intostructs or records.For example:struct complex { float re, im; }

There is also the notion ofgrouping statements anddeclarations into blocks:{ float re, im;

re = 0.0; im = 1.0;

}

Page 15: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

57CS 538 Spring 2008©

Blocks and structs look similar,but there are significantdifferences:Structs are data,• As originally designed, structs

contain only data (no functions ormethods).

• Structs can be dynamically created,in any number, and included inother data structures (e.g., in anarray of structs).

• All fields in a struct are visibleoutside the struct.

58CS 538 Spring 2008©

Blocks are code,• They can contain both code and

data.

• Blocks can’t be dynamically createdduring execution; they are “builtinto” a program.

• Locals in a block aren’t visibleoutside the block.

By adding functions andinitialization code to structs,we get classes—a nice blend ofstructs and blocks.For example:class complex{

float re, im;

complex (float v1, float v2){

re = v1; im = v2; }

}

59CS 538 Spring 2008©

Classes• Class objects can be created as

needed, in any number, and includedin other data structure.

• They include both data (fields) andfunctions (methods).

• They include mechanisms to initializethemselves (constructors) and tofinalize themselves (destructors).

• They allow controlled access tomembers (private and publicdeclarations).

60CS 538 Spring 2008©

Type Equivalence in ClassesIn C, C++ and Java, instances ofthe same struct or class aretype-equivalent, and mutuallyassignable.For example:class MyClass { ... }MyClass v1, v2;v1 = v2; // Assignment is OK

We expect to be able to assignvalues of the same type,including class objects.However, sometimes a classmodels a data object whosesize or shape is set uponcreation (in a constructor).

Page 16: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

61CS 538 Spring 2008©

Then we may not wantassignment to be allowed.

class Point { int dimensions; float coordinates[]; Point () { dimensions = 2; coordinates = new float[2]; } Point (int d) { dimensions = d; coordinates = new float[d]; } } Point plane = new Point(); Point solid = new Point(3); plane = solid; //OK in Java

This assignment is allowed,even though the two objectsrepresent points in differentdimensions.

62CS 538 Spring 2008©

SubtypesIn C++, C# and Java we cancreate subclasses—new classesderived from an existing class.We can use subclasses to createnew data objects that aresimilar (since they are based ona common parent), but stilltype-inequivalent.Example:class Point2 extends Point {

Point2() {super(2); } } class Point3 extends Point { Point3() {super(3); } } Point2 plane = new Point2(); Point3 solid = new Point3();

plane = solid; //Illegal in Java

63CS 538 Spring 2008©

Parametric PolymorphismWe can create distinctsubclasses based on the valuespassed to constructors. Butsometimes we want to createsubclasses based on distincttypes, and types can’t bepassed as parameters. (Typesare not values, but rather aproperty of values.)We see this problem in Java,which tries to create generalpurpose data structures bybasing them on the classObject. Since any object can beassigned to Object (all classesmust be a subclass of Object),this works—at least partially.

64CS 538 Spring 2008©

class LinkedList { Object value; LinkedList next; Object head() {return value;}LinkedList tail(){return next;}

LinkedList(Object O) { value = O; next = null;} LinkedList(Object O, LinkedList L){ value = O; next = L;}}

Using this class, we can createa linked list of any subtype ofObject.But,• We can’t guarantee that linked lists

are type homogeneous (containonly a single type).

• We must cast Object types backinto their “real” types when weextract list values.

Page 17: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

65CS 538 Spring 2008©

• We must use wrapper classes likeInteger rather than int (becauseprimitive types like int aren’tobjects, and aren’t subclass ofObject).

For example, to use LinkedListto build a linked list of ints wedo the following:LinkedList l =

new LinkedList(new Integer(123));

int i = ((Integer) l.head()).intValue();

This is pretty clumsy code.We’d prefer a mechanism thatallows us to create a “customversion” of LinkedList, basedon the type we want the list tocontain.

66CS 538 Spring 2008©

We can’t just call something likeLinkedList(int) orLinkedList(Integer) because

types can’t be passed asparameters.Parametric polymorphism isthe solution. Using thismechanism, we can use typeparameters to build a “customversion” of a class from ageneral purpose class.C++ allows this using itstemplate mechanism. Tiger Javaalso allows type parameters.In both languages, typeparameters are enclosed in“angle brackets” (e.g.,LinkedList<T> passes T, a type,to the LinkedList class).

67CS 538 Spring 2008©

Thus we haveclass LinkedList<T> { T value; LinkedList<T> next; T head() {return value;} LinkedList<T> tail() { return next;} LinkedList(T O) { value = O; next = null;} LinkedList(T O,LinkedList<T> L) {value = O; next = L;}}LinkedList<int> l = new LinkedList(123);

int i = l.head();

68CS 538 Spring 2008©

Overloading and Ad-hocPolymorphism

Classes usually allowoverloading of method names,if only to support multipleconstructors.That is, more than one methoddefinition with the same nameis allowed within a class, aslong as the method definitionsdiffer in the number and/ortypes of the parameters theytake.For example,class MyClass { int f(int i) { ... } int f(float g) { ... } int f(int i, int j) { ... }}

Page 18: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

69CS 538 Spring 2008©

Overloading is sometimescalled “ad hoc” polymorphism,because, to the programmer, itappears that one method cantake a variety of differentparameter types. This isn’t truepolymorphism because themethods have different bodies;there is no sharing of onedefinition among differentparameter types. There is noguarantee that the differentdefinitions do the same thing,even though they share acommon name.

70CS 538 Spring 2008©

Issues in OverloadingThough many languages allowoverloading, few allowoverloaded methods to differonly on their result types.(Neither C++ nor Java allow thiskind of overloading, thoughAda does). For example,class MyClass { int f() { ... } float f() { ... }}

is illegal. This is unfortunate;methods with the same nameand parameters, but differentresult types, could be used toautomatically convert resultvalues to the type demandedby the context of call.

71CS 538 Spring 2008©

Why is this form of overloadingusually disallowed?It’s because overload resolution(deciding which definition touse) becomes much harder.Considerclass MyClass { int f(int i, int j) { ... } float f(float i, float j) { ... } float f(int i, int j) { ... }}

inint a = f( f(1,2), f(3,4) );

which definitions of f do weuse in each of the three calls?Getting the correctly answercan be tricky, though solutionalgorithms do exist.

72CS 538 Spring 2008©

Operator OverloadingSome languages, like C++ andC#, allow operators to beoverloaded. You may add newdefinitions to existingoperators, and use them onyour own types. For example, class MyClass {

int i; public: int operator+(int j) { return i+j; } } MyClass c; int i = c+10; int j = c.operator+(10); int k = 10+c; // Illegal!

Page 19: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

73CS 538 Spring 2008©

The expression 10+c is illegalbecause there is no definitionof + for the types int andMyClass&. We can create one byusing C++’s friend mechanismto insert a definition intoMyClass that will have access toMyClass’s private data:

class MyClass {

int i; public: int operator+(int j) { return i+j; } friend int operator+ (int j, MyClass& v){

return j+v.i; } } MyClass c; int k = 10+c; // Now OK!

74CS 538 Spring 2008©

C++ limits operator overloadingto existing predefined operators.A few languages, like Algol 68 (asuccessor to Algol 60, developedin 1968), allow programmers todefine brand new operators.In addition to defining theoperator itself, it is also necessaryto specify the operator’sprecedence (which operator is tobe applied first) and itsassociativity (does the operatorassociate from left to right, orright to left, or not at all). Giventhis extra detail, it is possible tospecify something likeop +++ prec = 8;

int op +++(int& i, int& j) { return (i++)+(j++); }

(Why is int& used as theparameter type rather than int?)

75CS 538 Spring 2008©

Parameter BindingAlmost all programminglanguages have some notion ofbinding an actual parameter(provided at the point of call) to aformal parameter (used in thebody of a subprogram).There are many different, andinequivalent, methods ofparameter binding. Exactly whichis used depends upon theprogramming language inquestion.Parameter Binding Modes include:• Value: The formal parameter

represents a local variableinitialized to the value of thecorresponding actual parameter.

76CS 538 Spring 2008©

• Result: The formal parameterrepresents a local variable. Its finalvalue, at the point of return, iscopied into the correspondingactual parameter.

• Value/Result: A combination of thevalue and results modes. Theformal parameter is a local variableinitialized to the value of thecorresponding actual parameter.The formal’s final value, at thepoint of return, is copied into thecorresponding actual parameter.

• Reference: The formal parameter isa pointer to the correspondingactual parameter. All references tothe formal parameter indirectlyaccess the corresponding actualparameter through the pointer.

Page 20: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

77CS 538 Spring 2008©

• Name: The formal parameterrepresents a block of code(sometimes called a thunk) that isevaluated to obtain the value oraddress of the correspondingactual parameter. Each reference tothe formal parameter causes thethunk to be reevaluated.

• Readonly (sometimes called Const):Only reads of the formal parameterare allowed. Either a copy of theactual parameter’s value, or itsaddress, may be used.

78CS 538 Spring 2008©

What Parameter Modes doProgramming Languages Use?• C: Value mode except for arrays which

pass a pointer to the start of the array.

• C++: Allows reference as well as valuemodes. E.g.,

int f(int a, int& b)

• C#: Allows result (out) as well asreference and value modes. E.g.,

int g(int a, out int b)

• Java: Scalar types (int, float, char,etc.) are passed by value; objects arepassed by reference (references toobjects are passed by value).

• Fortran: Reference (even forconstants!)

• Ada: Value/result, reference, andreadonly are used.

79CS 538 Spring 2008©

Examplevoid p(value int a, reference int b, name int c) { a=1; b=2; print(c)}int i=3, j=3, k[10][10];p(i,j,k[i][j]);

What element of k is printed?• The assignment to a does not

affect i, since a is a valueparameter.

• The assignment to b does affect j,since b is a reference parameter.

• c is a name parameter, so it isevaluated whenever it is used. Inthe print statement k[i][j] isprinted. At that point i=3 and j=2,so k[3][2] is printed.

80CS 538 Spring 2008©

Why are there so ManyDifferent Parameter Modes?

Parameter modes reflectdifferent views on howparameters are to be accessed,as well as different degrees ofefficiency in accessing andusing parameters.• Call by value protects the actual

parameter value. No matter whatthe subprogram does, theparameter can’t be changed.

• Call by reference allows immediateupdates to the actual parameter.

• Call by readonly protects the actualparameter and emphasizes the“constant” nature of the formalparameter.

Page 21: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

81CS 538 Spring 2008©

• Call by value/result allows actualparameters to change, but treats acall as a single step (assignparameter values, execute thesubprogram’s body, updateparameter values).

• Call by name delays evaluation ofan actual parameter until it isactually needed (which may benever).

82CS 538 Spring 2008©

Call by NameCall by name is a special kindof parameter passing mode. Itallows some calls to completethat otherwise would fail.Considerf(i,j/0)

Normally, when j/0 isevaluated, a divide faultterminates execution. If j/0 ispassed by name, the division isdelayed until the parameter isneeded, which may be never.Call by name also allowsprogrammers to create someinteresting solutions to hardprogramming problems.

83CS 538 Spring 2008©

Consider the conditionalexpression found in C, C++,and Java:(cond ? value1 : value2)

What if we want to implementthis as a function call:condExpr(cond,value1,value2) {

if (cond) return value1; else return value2; }

With most parameter passingmodes this implementationwon’t work! (Why?)But if value1 and value2 arepassed by name, theimplementation is correct.

84CS 538 Spring 2008©

Call by Name and LazyEvaluation

Call by name has much of theflavor of lazy evaluation. Withlazy evaluation, you don’tcompute a value but rather asuspension—a function that willprovide a value when called.This can be useful when we needto control how much of acomputation is actuallyperformed.Consider an infinite list ofintegers. Mathematically it isrepresented as 1, 2, 3, ...How do we compute a datastructure that represents aninfinite list?

Page 22: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

85CS 538 Spring 2008©

The obvious computationinfList(int start) {

return list(start, infList(start+1)); }

doesn’t work. (Why?)A less obvious implementation,using suspensions, does work:infList(int start) {

return list(start, function() { return infList(start+1); });}

Now, whenever we are given aninfinite list, we get two things: thefirst integer in the list and asuspension function. When called,this function will give you the restof the infinite list (again, onemore value and anothersuspension function).

86CS 538 Spring 2008©

The whole list is there, but only asmuch as you care to access isactually computed.

87CS 538 Spring 2008©

Eager Parameter EvaluationSometimes we want parametersevaluated eagerly—as soon asthey are known.Consider a sorting routine thatbreaks an array in half, sortseach half, and then mergestogether the two sorted halves(this is a merge sort).In outline form it is:sort(inputArray) { ...merge(sort(leftHalf(inputArray)), sort(rightHalf(inputArray)));}

This definition lends itselfnicely to parallel evaluation:The two halves of an inputarray can be sorted in parallel.Each of these two halves can

88CS 538 Spring 2008©

again be split in two, allowingparallel sorting of four quarter-sized arrays, then leading to 8sorts of 1/8 sized arrays, etc.But,to make this all work, the twoparameters to merge must beevaluated eagerly, rather thanin sequence.

Page 23: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

89CS 538 Spring 2008©

Type EquivalenceProgramming languages usetypes to describe the values adata object may hold and theoperations that may beperformed.By checking the types of values,potential errors in expressions,assignments and calls may beautomatically detected. Forexample, type checking tells usthat123 + "123"

is illegal because addition is notdefined for an integer, stringcombination.Type checking is usually done atcompile-time; this is static typing.

90CS 538 Spring 2008©

Type-checking may also be doneat run-time; this is dynamictyping.A program is type-safe if it isimpossible to apply an operationto a value of the wrong type. In atype-safe language, plus is nevertold to add an integer to a string,because its definition does notallow that combination ofoperands. In type-safe programsan operator can still see an illegalvalue (e.g., a division by zero),but it can’t see operands of thewrong type.A strongly-typed programminglanguage forbids the execution oftype-unsafe programs.Weakly-typed programminglanguages allow the execution ofpotentially type-unsafe programs.

91CS 538 Spring 2008©

The question reduces to whetherthe programming language allowsprogrammers to “break” the typerules, either knowingly orunknowingly.Java is strongly typed; type errorspreclude execution. C and C++are weakly typed; you can breakthe rules if you wish. Forexample: int i; int* p;

p = (int *) i * i;

Now p may be used as an integerpointer though multiplicationneed not produce valid integerpointers.If we are going to do typechecking in a program, we mustdecide whether two types, T1 andT2 are equivalent; that is, whetherthey be used interchangeably.

92CS 538 Spring 2008©

There are two major approachesto type equivalence:Name Equivalence:Two types are equivalent if andonly if they refer to exactly thesame type declaration.For example,type PackerSalaries = int[100];

type AssemblySizes = int[100]; PackerSalaries salary; AssemblySizes size;

Issal = size;

allowed?Using name equivalence, no. Thatis, salary /≡N size since thesetwo variables have different typedeclarations (that happen to beidentical in structure).

Page 24: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

93CS 538 Spring 2008©

Formally, we define ≡N (name typeequivalence) as:(a) T ≡N T

(b) Given the declarationType T1 = T2;

T1 ≡N T2

We treat anonymous types (typesnot given a name) as anabbreviation for an implicitdeclaration of a new and uniquetype name.Thus int A[10];

is an abbreviation for Type Tnew = int[10];

Tnew A;

94CS 538 Spring 2008©

Structural EquivalenceAn alternative notion of typeequivalence is structuralequivalence (denoted ≡S).Roughly, two types arestructurally equivalent if the twotypes have the same definition,independent of where thedefinitions are located. That is,the two types have the samedefinitional structure.Formally, (a) T ≡S T

(b) Given the declaration Type T = Q;

T ≡S Q

(c) If T and Q are defined usingthe same type constructor andcorresponding parameters in the

95CS 538 Spring 2008©

two definitions are equal orstructurally equivalentthen T ≡S Q

Returning to our previousexample, type PackerSalaries = int[100];

type AssemblySizes = int[100]; PackerSalaries salary; AssemblySizes size;

salary ≡S size since both arearrays and 100=100 and int ≡Sint.

96CS 538 Spring 2008©

Which notion of Equivalence doProgramming Languages Use?

C and C++ use structuralequivalence except for structsand classes (where nameequivalence is used). For arrays,size is ignored.Java uses structural equivalencefor scalars. For arrays, it requiresname equivalence for thecomponent type, ignoring size.For classes it uses nameequivalence except that a subtypemay be used where a parent typeis expected. Thus given void subr(Object O) { ... };

the callsubr(new Integer(100));

is OK since Integer is a subclass ofObject.

Page 25: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

97CS 538 Spring 2008©

Automatic Type ConversionsC, C++ and Java also allow variouskinds of automatic typeconversions.In C, C++ and Java, a float willbe automatically created from anint: float f = 10; // No type error

Also, an integer type (char,short, int, long) will bewidened: int i = 'x';

In C and C++ (but not Java), aninteger value can also benarrowed, possibly with the lossof significant bits: char c = 1000000;

98CS 538 Spring 2008©

Reading Assignment• An Introduction to Scheme for C

Programmers (linked from class web page)

• The Scheme Language Definition (linked from class web page)

99CS 538 Spring 2008©

Lisp & SchemeLisp (List Processing Language)is one of the oldestprogramming languages still inwide use.It was developed in the late 50sand early 60s by JohnMcCarthy.Its innovations include:• Support of symbolic computations.

• A functional programming stylewithout emphasis on assignmentsand side-effects.

• A naturally recursive programmingstyle.

• Dynamic (run-time) type checking.

100CS 538 Spring 2008©

• Dynamic data structures (lists,binary trees) that grow withoutlimit.

• Automatic garbage collection tomanage memory.

• Functions are treated as “first class”values; they may be passed asarguments, returned as resultvalues, stored in data structures,and created during execution.

• A formal semantics (written in Lisp)that defines the meaning of allvalid programs.

• An Integrated ProgrammingEnvironment to create, edit andtest Lisp programs.

Page 26: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

101CS 538 Spring 2008©

SchemeScheme is a recent dialect ofLisp.It uses lexical (static) scoping.It supports true first-classfunctions.It provides program-levelaccess to control flow viacontinuation functions.

102CS 538 Spring 2008©

Atomic (Primitive) Data TypesSymbols:Essentially the same form asidentifiers. Similar to enumerationvalues in C and C++.Very flexible in structure;essentially any sequence ofprintable characters is allowed;anything that starts a validnumber (except + or -) may notstart a symbol.Valid symbols include:

abc hello-world + <=!

Integers:Any sequence of digits, optionallyprefixed with a + or -. Usuallyunlimited in length.

103CS 538 Spring 2008©

Reals:A floating point number in adecimal format (123.456) or inexponential format (1.23e45). Aleading sign and a signedexponent are allowed(-12.3, 10.0e-20).Rationals:Rational numbers of the forminteger/integer (e.g., 1/3 or 9/7)with an optional leading sign (-1/2, +7/8).Complex:Complex numbers of the formnum+num i or num-num i, wherenum is an integer or real number.Example include 1+3i, -1.5-2.5i, 0+1i).

104CS 538 Spring 2008©

String:A sequence of charactersdelimited by double quotes.Double quotes and backslashesmust be escaped using abackslash. For example"Hello World" "\"Wow!\""

Character:A single character prefixed by #\.For example, #\a, #\0, #\\, #\#.Two special characters are#\space and #\newline.

Boolean:True is represented as #t andfalse is represented as #f.

Page 27: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

105CS 538 Spring 2008©

Binary TreesBinary trees are also calledS-Expressions in Lisp and Scheme.They are of the form ( item . item )where item is any atomic value orany S-Expression. For example:

( A . B ) (1.2 . "xyz" ) ( (A . B ) . C ) ( A . (B . C ) )

S-Expressions are linearizations ofbinary trees:

A B 1.2 "xyz"

106CS 538 Spring 2008©

S-Expressions are built andaccessed using the predefinedfunctions cons, car and cdr.cons builds a new S-Expressionfrom two S-Expressions thatrepresent the left and rightchildren.cons(E1,E2) = (E1 . E2)car returns are left subtree of anS-Expression.car (E1 . E2) = E1cdr returns are right subtree of anS-Expression.cdr (E1 . E2) = E2

C A

A B B C

107CS 538 Spring 2008©

ListsIn Lisp and Scheme lists are aspecial, widely-used form of S-Expressions.() represents the empty or null list(A) is the list containing A.By definition, (A) ≡ (A . () )

(A B) represents the listcontaining A and B. By definition,(A B) ≡ (A . (B . () ) )

In general, (A B C ... Z) ≡(A . (B . (C . ... (Z . () ) ... )))

(A B C )≡

A

B

C ()

108CS 538 Spring 2008©

Function CallsIn List and Scheme, function callsare represented as lists.(A B C) means:Evaluate A (to a function)Evaluate B and C (as parameters)Call A with B and C as itsparametersUse the value returned by the callas the “meaning” of (A B C).cons, car and cdr are predefinedsymbols bound to built-infunctions that build and accesslists and S-Expressions.Literals (of type integer, real,rational, complex, string,character and boolean) evaluateto themselves.

Page 28: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

109CS 538 Spring 2008©

For example (⇒ means “evaluatesto”)(cons 1 2) ⇒ (1 . 2)

(cons 1 () ) ⇒ (1)

(car (cons 1 2)) ⇒ 1

(cdr (cons 1 ())) ⇒ ()

But,(car (1 2)) fails during

execution!Why?The expression (1 2) looks like acall, but 1 isn’t a function! Weneed some way to “quote”symbols and lists we don’t wantevaluated.(quote arg)

is a special function that returnsits argument unevaluated.

110CS 538 Spring 2008©

Thus (quote (1 2)) doesn’t tryto evaluate the list (1 2); it justreturns it.Since quotation is so often used,it may be abbreviated using asingle quote. That is(quote arg) ≡ 'arg

Thus(car '(a b c)) ⇒ a

(cdr '( (A) (B) (C))) ⇒( (B) (C) )

(cons 'a '1) ⇒ (a . 1)

But,('cdr '(A B)) fails!

Why?

111CS 538 Spring 2008©

User-defined FunctionsThe list(lambda (args) (body))

evaluates to a function with(args) as its argument list and(body) as the function body.No quotes are needed for(args) or (body).Thus(lambda (x) (+ x 1)) evaluatesto the increment function.Similarly,((lambda (x) (+ x 1)) 10) ⇒11

112CS 538 Spring 2008©

We can bind values andfunctions to global symbolsusing the define function.The general form is(define id object)

id is not evaluated but objectis. id is bound to the valueobject evaluates to.For example,(define pi 3.1415926535)

(define plus1 (lambda (x) (+ x 1)) )

(define pi*2 (* pi 2))

Once a symbol is defined, itevaluates to the value it isbound to:(plus1 12) ⇒ 13

Page 29: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

113CS 538 Spring 2008©

Since functions are frequentlydefined, we may abbreviate(define id (lambda (args) (body)) )

as(define (id args) (body) )

Thus(define (plus1 x) (+ x 1))

114CS 538 Spring 2008©

Conditional Expressions inScheme

A predicate is a function thatreturns a boolean value. Byconvention, in Scheme, predicatenames end with “?”For example, number? symbol? equal? null? list?

In conditionals, #f is false, andeverything else, including #t, istrue.The if expression is(if pred E1 E2)

First pred is evaluated.Depending on its value (#f ornot), either E1 or E2 is evaluated(but not both) and returned as thevalue of the if expression.

115CS 538 Spring 2008©

For example,(if (= 1 (+ 0 1))

'Yes 'No )

(define (fact n) (if (= n 0) 1 (* n (fact (- n 1))) ))

116CS 538 Spring 2008©

Generalized ConditionalThis is similar to a switch or case:(cond (p1 e1) (p2 e2) ... (else en))

Each of the predicates (p1, p2, ...)is evaluated until one is true (≠#f). Then the correspondingexpression (e1, e2, ...) isevaluated and returned as thevalue of the cond. else acts like apredicate that is always true.Example:

(cond

((= a 1) 2) ((= a 2) 3) (else 4) )

Page 30: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

117CS 538 Spring 2008©

Recursion in SchemeRecursion is widely used inScheme and most other functionalprogramming languages.Rather than using a loop to stepthrough the elements of a list orarray, recursion breaks a problemon a large data structure into asimpler problem on a smaller datastructure.A good example of this approachis the append function, whichjoins (or appends) two lists intoone larger list containing all theelements of the two input lists (inthe correct order).Note that cons is not append.cons adds one element to thehead of an existing list.

118CS 538 Spring 2008©

Thus(cons '(a b) '(c d)) ⇒ ((a b) c d)(append '(a b) '(c d)) ⇒ (a b c d)

The append function is predefinedin Scheme, as are many otheruseful list-manipulating functions(consult the Scheme definition forwhat’s available).It is instructive to define appenddirectly to see its recursiveapproach:(define (append L1 L2) (if (null? L1) L2 (cons (car L1) (append (cdr L1) L2)) ))

119CS 538 Spring 2008©

Let’s trace (append '(a b) '(c d))

Our definition is(define (append L1 L2) (if (null? L1) L2 (cons (car L1) (append (cdr L1) L2)) ))

Now L1 = (a b) and L2 = (c d).(null? L1) is false, so weevaluate(cons (car L1) (append (cdr L1) L2))= (cons (car '(a b))

(append (cdr '(a b)) '(c d)))= (cons 'a (append '(b) '(c d))

We need to evaluate (append '(b) '(c d))

In this call, L1 = (b) and L2 = (c d).L1 is not null, so we evaluate

120CS 538 Spring 2008©

(cons (car L1) (append (cdr L1) L2))= (cons (car '(b)) (append (cdr '(b)) '(c d)))

= (cons 'b (append '() '(c d))

We need to evaluate (append '() '(c d))

In this call, L1 = () and L2 = (c d).L1 is null, so we return (c d).Therefore(cons 'b (append '() '(c d)) =(cons 'b '(c d)) = (b c d) =(append '(b) '(c d))

Finally,(append '(a b) '(c d)) =(cons 'a (append '(b) '(c d)) =

(cons 'a '(b c d)) = (a b c d)

Note:Source files for append, and otherScheme examples, are in~cs538-1/public/scheme/example1.scm,~cs538-1/public/scheme/example2.scm,etc.

Page 31: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

121CS 538 Spring 2008©

Reversing a ListAnother useful list-manipulationfunction is rev, which reversesthe members of a list. That is, thelast element becomes the firstelement, the next-to-last elementbecomes the second element, etc.For example,(rev '(1 2 3)) ⇒ (3 2 1)

The definition of rev isstraightforward:(define (rev L) (if (null? L) L (append (rev (cdr L))

(list (car L)) ) ))

122CS 538 Spring 2008©

As an example, consider(rev '(1 2))

Here L = (1 2). L is not null so weevaluate(append (rev (cdr L)) (list (car L))) =(append (rev (cdr '(1 2))) (list (car '(1 2)))) =

(append (rev '(2)) (list 1)) =(append (rev '(2)) '(1))

We must evaluate (rev '(2))

Here L = (2). L is not null so weevaluate(append (rev (cdr L)) (list (car L))) =

(append (rev (cdr '(2))) (list (car '(2)))) =(append (rev ())(list 2)) =(append (rev ())'(2))

We must evaluate (rev '())

Here L = (). L is null so (rev '())= ()

123CS 538 Spring 2008©

Thus (append (rev ())'(2)) =(append () '(2)) = (2) = (rev '(2))

Finally, recall (rev '(1 2)) =(append (rev '(2)) '(1)) =(append '(2) '(1)) = (2 1)

As constructed, rev only reversesthe “top level” elements of a list.That is, members of a list thatthemselves are lists aren’treversed.For example, (rev '( (1 2) (3 4))) = ((3 4) (1 2))

We can generalize rev to alsoreverse list members that happento be lists.To do this, it will be convenient touse Scheme’s let construct.

124CS 538 Spring 2008©

The Let ConstructScheme allows us to create localnames, bound to values, for usein an expression.The structure is(let ( (id1 val1) (id2 val2) ... ) expr )

In this construct, val1 isevaluated and bound to id1,which will exist only within thislet expression. If id1 is alreadydefined (as a global or parametername) the existing definition ishidden and the local definition,bound to val1, is used. Thenval2 is evaluated and bound toid2, .... Finally, expr is evaluatedin a scope that includes id1, id2,...

Page 32: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

125CS 538 Spring 2008©

For example,(let ( (a 10) (b 20) )

(+ a b)) ⇒ 30

Using a let, the definition ofrevall, a version of rev thatreverses all levels of a list, is easy:

(define (revall L)

(if (null? L) L (let ((E (if (list? (car L)) (revall (car L)) (car L) ))) (append (revall (cdr L))

(list E)) ) ))

(revall '( (1 2) (3 4))) ⇒ ((4 3) (2 1))

126CS 538 Spring 2008©

SubsetsAnother good example ofScheme’s recursive style ofprogramming is subsetcomputation.Given a list of distinct atoms, wewant to compute a list of allsubsets of the list values.For example,(subsets '(1 2 3)) ⇒ ( () (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3))

The order of atoms and sublists isunimportant, but all possiblesubsets of the list values must beincluded.Given Scheme’s recursive style ofprogramming, we need arecursive definition of subsets.

127CS 538 Spring 2008©

That is, if we have a list of allsubsets of n atoms, how do weextend this list to one containingall subsets of n+1 values?First, we note that the number ofsubsets of n+1 values is exactlytwice the number of subsets of nvalues.For example,(subsets '(1 2) ) ⇒( () (1) (2) (1 2)), which

contains 4 subsets.(subsets '(1 2 3)) contains 8subsets (as we saw earlier).Moreover, the extended list (ofsubsets for n+1 values) is simplythe list of subsets for n valuesplus the result of “distributing”the new value into each of theoriginal subsets.

128CS 538 Spring 2008©

Thus (subsets '(1 2 3)) ⇒( () (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3)) =( () (1) (2) (1 2) ) plus

( (3) (1 3) (2 3) (1 2 3) )

This insight leads to a conciseprogram for subsets.We will let (distrib L E) be afunction that “distributes” E intoeach list in L.For example,(distrib '(() (1) (2) (1 2)) 3) =

( (3) (3 1) (3 2) (3 1 2) )

(define (distrib L E) (if (null? L) () (cons (cons E (car L)) (distrib (cdr L) E)) ))

Page 33: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

129CS 538 Spring 2008©

We will let (extend L E) extend alist L by distributing element Ethrough L and then appendingthis result to L.For example,(extend '( () (a) ) 'b) ⇒ ( () (a) (b) (b a))

(define (extend L E) (append L (distrib L E)))

Now subsets is easy:

(define (subsets L)

(if (null? L) (list ()) (extend (subsets (cdr L))

(car L)) ))

130CS 538 Spring 2008©

Data Structures in SchemeIn Scheme, lists and S-expressionsare basic. Arrays can be simulatedusing lists, but access to elements“deep” in the list can be slow(since a list is a linked structure).To access an element deep withina list we can use:• (list-tail L k)

This returns list L after removingthe first k elements. For example,(list-tail '(1 2 3 4 5) 2) ⇒(3 4 5)

• (list-ref L k)This returns the k-th element in L(counting from 0). For example,(list-ref '(1 2 3 4 5) 2) ⇒ 3

131CS 538 Spring 2008©

Vectors in SchemeScheme provides a vector typethat directly implements onedimensional arrays.Literals are of the form #( ... )For example, #(1 2 3) or#(1 2.0 "three")

The function (vector? val)tests whether val is a vector ornot.(vector? 'abc) ⇒ #f

(vector? '(a b c)) ⇒ #f

(vector? #(a b c)) ⇒ #t

The function (vector v1 v2...) evaluates v1, v2, ... and putsthem into a vector.(vector 1 2 3) ⇒ #(1 2 3)

132CS 538 Spring 2008©

The function (make-vector k val)

creates a vector composed of kcopies of val. Thus(make-vector 4 (/ 1 2)) ⇒ #(1/2 1/2 1/2 1/2)

The function (vector-ref vect k)returns the k-th element of vect,starting at position 0. It isessentially the same as vect[k]in C or Java. For example,(vector-ref #(2 4 6 8 10) 3) ⇒ 8

The function(vector-set! vect k val) setsthe k-th element of vect, startingat position 0, to be val. It isessentially the same asvect[k]=val in C or Java. Thevalue returned by the function isunspecified. The suffix “!” in set!indicates that the function has aside-effect.

Page 34: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

133CS 538 Spring 2008©

For example,(define v #(1 2 3 4 5))(vector-set! v 2 0)v ⇒ #(1 2 0 4 5)

Vectors aren’t lists (and listsaren’t vectors).Thus (car #(1 2 3)) doesn’twork.There are conversion routines:• (vector->list V) converts

vector V to a list containing thesame values as V. For example,(vector->list #(1 2 3)) ⇒ (1 2 3)

• (list->vector L) converts list Lto a vector containing the samevalues as L. For example,(list->vector '(1 2 3)) ⇒#(1 2 3)

134CS 538 Spring 2008©

• In general Scheme names aconversion function from type T totype Q as T->Q. For example,string->list converts a stringinto a list containing thecharacters in the string.

135CS 538 Spring 2008©

Records and StructsIn Scheme we can represent arecord, struct, or class object asan association list of the form((obj1 val1) (obj2 val2) ...)

In the association list, which is alist of (object value) sublists,object serves as a “key” to locatethe desired sublist.For example, the association list( (A 10) (B 20) (C 30) )

serves the same role asstruct

{ int a = 10; int b = 20; int c = 30;}

136CS 538 Spring 2008©

The predefined Scheme function(assoc obj alist)

checks alist (an association list)to see if it contains a sublist withobj as its head. If it does, the liststarting with obj is returned;otherwise #f (indicating failure) isreturned.For example,(define L '( (a 10) (b 20) (c 30) ) )

(assoc 'a L) ⇒ (a 10)

(assoc 'b L) ⇒ (b 20)

(assoc 'x L) ⇒ #f

Page 35: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

137CS 538 Spring 2008©

We can use non-atomic objects askeys too!(define price-list

'( ((bmw m5) 71095)

((bmw z4) 40495)

((jag xj8) 56975)

((mb sl500) 86655)

)

)

(assoc '(bmw z4) price-list)⇒ ((bmw z4) 40495)

138CS 538 Spring 2008©

Using assoc, we can easily definea structure function:(structure key alist) willreturn the value associated withkey in alist; in C or Javanotation, it returns alist.key.(define (structure key alist) (if (assoc key alist)

(car (cdr (assoc key alist))) #f ))

We can improve this function intwo ways:• The same call to assoc is made

twice; we can save the valuecomputed by using a letexpression.

• Often combinations of car and cdrare needed to extract a value.

139CS 538 Spring 2008©

Scheme has a number ofpredefined functions that combineseveral calls to car and cdr intoone function. For example,(caar x) ≡ (car (car x))(cadr x) ≡ (car (cdr x))(cdar x) ≡ (cdr (car x))(cddr x) ≡ (cdr (cdr x))

Using these two insights we cannow define a better version ofstructure

(define (structure key alist) (let ((p (assoc key alist))) (if p (cadr p) #f ) ) )

140CS 538 Spring 2008©

What does assoc do if more thanone sublist with the same keyexists?It returns the first sublist with amatching key. In fact, thisproperty can be used to make asimple and fast function thatupdates association lists:(define (set-structure key alist val) (cons (list key val) alist))

Page 36: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

141CS 538 Spring 2008©

If we want to be more space-efficient, we can create a versionthat updates the internal structureof an association list, usingset-cdr! which changes the cdrvalue of a list:(define (set-structure! key alist val) (let ( (p (assoc key alist))) (if p (begin (set-cdr! p (list val)) alist ) (cons (list key val) alist) ) ))

142CS 538 Spring 2008©

Functions are First-classObjects

Functions may be passed asparameters, returned as the valueof a function call, stored in dataobjects, etc.This is a consequence of the factthat(lambda (args) (body))

evaluates to a function just as(+ 1 1)

evaluates to an integer.

143CS 538 Spring 2008©

ScopingIn Scheme scoping is static(lexical). This means that non-local identifiers are bound tocontaining lambda parameters, orlet values, or globally definedvalues. For example,(define (f x) (lambda (y) (+ x y)))

Function f takes one parameter,x. It returns a function (of y), withx in the returned function boundto the value of x used when f wascalled.Thus(f 10) ≡ (lambda (y) (+ 10 y))

((f 10) 12) ⇒ 22

144CS 538 Spring 2008©

Unbound symbols are assumed tobe globals; there is a run-timeerror if an unbound global isreferenced. For example,(define (p y) (+ x y))

(p 20) ; error -- x is unbound

(define x 10)

(p 20) ⇒ 30

We can use let bindings to createprivate local variables forfunctions:(define F (let ( (X 1) ) (lambda () X) ))

F is a function (of no arguments).(F) calls F.(define X 22)

(F) ⇒ 1;X used in F is private

Page 37: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

145CS 538 Spring 2008©

We can encapsulate internal statewith a function by using private,let-bound variables:(define cnt (let ( (I 0) ) (lambda ()

(set! I (+ I 1)) I) ))

Now, (cnt) ⇒ 1

(cnt) ⇒ 2

(cnt) ⇒ 3

etc.

146CS 538 Spring 2008©

Let Bindings can be SubtleYou must check to see if the let-bound value is created when thefunction is created or when it iscalled.Compare(define cnt

(let ( (I 0) ) (lambda ()

(set! I (+ I 1)) I) ) )vs. (define reset (lambda () (let ( (I 0) )

(set! I (+ I 1)) I) ) )(reset) ⇒ 1, (reset) ⇒ 1, etc.

147CS 538 Spring 2008©

Simulating Class ObjectsUsing association lists and privatebound values, we can encapsulatedata and functions. This gives usthe effect of class objects.(define (point x y) (list (list 'rect (lambda () (list x y))) (list 'polar (lambda () (list (sqrt (+ (* x x) (* y y))) (atan (/ x y)) ) ) ) ))

A call (point 1 1) creates anassociation list of the form( (rect funct) (polar funct) )

148CS 538 Spring 2008©

We can use structure to accesscomponents:(define p (point 1 1) )

( (structure 'rect p) ) ⇒ (1 1)

( (structure 'polar p) ) ⇒

( )2 π4---

Page 38: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

149CS 538 Spring 2008©

We can add new functionality byjust adding new (id function)pairs to the association list.(define (point x y) (list (list 'rect (lambda () (list x y))) (list 'polar (lambda () (list (sqrt (+ (* x x) (* y y))) (atan (/ x y)) ))) (list 'set-rect! (lambda (newx newy) (set! x newx) (set! y newy) (list x y) )) (list 'set-polar! (lambda (r theta) (set! x (* r (sin theta))) (set! y (* r (cos theta))) (list r theta) ))))

150CS 538 Spring 2008©

Now we have(define p (point 1 1) )

( (structure 'rect p) ) ⇒ (1 1)

( (structure 'polar p) ) ⇒

( )

((structure 'set-polar! p) 1 π/4)⇒ (1 π/4)

( (structure 'rect p) ) ⇒

( )

2 π4---

12

------- 12

-------

151CS 538 Spring 2008©

Limiting Access to InternalStructure

We can improve upon ourassociation list approach byreturning a single function(similar to a C++ or Java object)rather than an explicit list of (idfunction) pairs.The function will take the name ofthe desired operation as one of itsarguments.

152CS 538 Spring 2008©

First, let’s differentiate between(define def1 (let ( (I 0) ) (lambda () (set! I (+ I 1)) I) ))

and(define (def2) (let ( (I 0) ) (lambda () (set! I (+ I 1)) I) ))

def1 is a zero argument functionthat increments a local variableand returns its updated value.def2 is a a zero argumentfunction that generates a functionof zero arguments (thatincrements a local variable andreturns its updated value). Eachcall to def2 creates a differentfunction.

Page 39: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

153CS 538 Spring 2008©

Stack Implemented as aFunction

(define ( stack ) (let ( (s () ) ) (lambda (op . args) ; var # args (cond ((equal? op 'push!)

(set! s (cons (car args) s)) (car s)) ((equal? op 'pop!) (if (null? s) #f (let ( (top (car s)) ) (set! s (cdr s)) top ))) ((equal? op 'empty?) (null? s)) (else #f) ) ) ))

154CS 538 Spring 2008©

(define stk (stack));new empty stack

(stk 'push! 1) ⇒ 1 ;s = (1)

(stk 'push! 3) ⇒ 3 ;s = (3 1)

(stk 'push! 'x) ⇒ x ;s = (x 3 1)

(stk 'pop!) ⇒ x ;s = (3 1)

(stk 'empty?) ⇒ #f ;s = (3 1)

(stk 'dump) ⇒ #f ;s = (3 1)

155CS 538 Spring 2008©

Higher-Order FunctionsA higher-order function is afunction that takes a function as aparameter or one that returns afunction as its result.A very important (and useful)higher-order function is map,which applies a function to a listof values and produces a list orresults:(define (map f L) (if (null? L) () (cons (f (car L)) (map f (cdr L))) ))

Note: In Scheme’s built-inimplementation of map, the orderof function application isunspecified.

156CS 538 Spring 2008©

(map sqrt '(1 2 3 4 5)) ⇒ (1 1.414 1.732 2 2.236)

(map (lambda(x) (* x x)) '(1 2 3 4 5)) ⇒ (1 4 9 16 25)

Map may also be used withmultiple argument functions bysupplying more than one list ofarguments:(map + '(1 2 3) '(4 5 6)) ⇒ (5 7 9)

Page 40: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

157CS 538 Spring 2008©

The Reduce FunctionAnother useful higher-orderfunction is reduce, which reducesa list of values to a single value byrepeatedly applying a binaryfunction to the list values.This function takes a binaryfunction, a list of data values, andan identity value for the binaryfunction:(define (reduce f L id) (if (null? L) id (f (car L) (reduce f (cdr L) id)) ))

(reduce + '(1 2 3 4 5) 0) ⇒ 15(reduce * '(1 2 4 6 8 10) 1) ⇒3840

158CS 538 Spring 2008©

(reduce append '((1 2 3) (4 5 6) (7 8)) ())⇒ (1 2 3 4 5 6 7 8)

(reduce expt '(2 2 2 2) 1) ⇒

= 65536

(reduce expt '(2 2 2 2 2) 1)⇒ 265536

(string-length (number->string(reduce expt '(2 2 2 2 2) 1)))

⇒ 19729 ; digits in 265536

2222

159CS 538 Spring 2008©

Sharing vs. CopyingIn languages without side-effectsan object can be copied bycopying a pointer (reference) tothe object; a complete new copyof the object isn’t needed.Hence in Scheme (define A B)normally means

A B

160CS 538 Spring 2008©

But, if side-effects are possible wemay need to force a physical copyof an object or structure:(define (copy obj)

(if (pair? obj)

(cons (copy (car obj))(copy (cdr obj)))

obj

))

Page 41: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

161CS 538 Spring 2008©

For example,(define A '(1 2))

(define B (cons A A))

B = ( (1 2) 1 2)

A B

1

2 ()

162CS 538 Spring 2008©

(set-car! (car B) 10)

B = ( (10 2) 10 2)

(define C (cons (copy A) (copy A)))

A B

10

2 ()

C

10

2 ()

10

2 ()

163CS 538 Spring 2008©

(set-car! (car C) 20)

C = ((20 2) 10 2)

Similar concerns apply tostrings and vectors, becausetheir internal structure can bechanged.

C

20

2 ()

10

2 ()

164CS 538 Spring 2008©

Shallow & Deep CopyingA copy operation that copies apointer (or reference) rather thanthe object itself is a shallow copy.For example, In Java,Object O1 = new Object();

Object O2 = new Object();

O1 = O2; // shallow copy

If the structure within an object isphysically copied, the operation isa deep copy.In Java, for objects that supportthe clone operation,O1 = O2.clone(); // deep copy

Even in Java’s deep copy (via theclone() operation), objectsreferenced from within an objectare shallow copied. Thus given

Page 42: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

165CS 538 Spring 2008©

class List {

int value;

List next;

}

List L,M;

M = L.clone();

L.value and M.value areindependent, but L.next andM.next refer to the same Listobject.A complete deep copy, that copiesall objects linked directly orindirectly, is expensive and trickyto implement.(Consider a complete copy of acircular linked list).

166CS 538 Spring 2008©

Equality Checking in SchemeIn Scheme = is used to test fornumeric equality (includingcomparison of different numerictypes). Non-numeric argumentscause a run-time error. Thus(= 1 1) ⇒ #t

(= 1 1.0) ⇒ #t

(= 1 2/2) ⇒ #t

(= 1 1+0.0i) ⇒ #t

167CS 538 Spring 2008©

To compare non-numeric values,we can use either:pointer equivalence (do the twooperands point to the sameaddress in memory)structural equivalence (do the twooperands point to structures withthe same size, shape andcomponents, even if they are indifferent memory locations)In general pointer equivalence isfaster but less accurate.

Scheme implements both kinds ofequivalence tests.(eqv? obj1 obj2)

This tests if obj1 and obj2 arethe exact same object. This worksfor atoms and pointers to thesame structure.

168CS 538 Spring 2008©

(equal? obj1 obj2)

This tests if obj1 and obj2 arethe same, component bycomponent. This works foratoms, lists, vectors and strings.(eqv? 1 1) ⇒ #t

(eqv? 1 (+ 0 1)) ⇒ #t

(eqv? 1/2 (- 1 1/2)) ⇒ #t

(eqv? (cons 1 2) (cons 1 2)) ⇒#f

(eqv? "abc" "abc") ⇒ #f

(equal? 1 1) ⇒ #t

(equal? 1 (+ 0 1)) ⇒ #t

(equal? 1/2 (- 1 1/2)) ⇒ #t

(equal? (cons 1 2) (cons 1 2)) ⇒#t

(equal? "abc" "abc") ⇒ #t

In general it is wise to use equal?unless speed is a critical factor.

Page 43: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

169CS 538 Spring 2008©

I/O in SchemeScheme has simple read and writefunctions, directed to the“standardin” and “standard out” files.(read)

Reads a single Scheme object (anatom, string, vector or list) fromthe standard in file. No quoting isneeded.(write obj)

Writes a single object, obj, to thestandard out file.(display obj)

Writes obj to the standard out filein a more readable format.(Strings aren’t quoted, andcharacters aren’t escaped.)(newline)

Forces a new line on standard outfile.

170CS 538 Spring 2008©

PortsPorts are Scheme objects thatinterface with systems files. I/O tofiles is normally done through aport object bound to a systemfile.(open-input-file "path to file")

This returns an input portassociated with the "path tofile" string (which is systemdependent). A run-time error issignalled if "path to file"specifies an illegal or inaccessiblefile.(read port)

Reads a single Scheme object (anatom, string, vector or list) fromport, which must be an inputport object.

171CS 538 Spring 2008©

(eof-object? obj)

When the end of an input file isreached, a special eof-object isreturned. eof-object? testswhether an object is this specialend-of-file marker.(open-output-file "path to file")

This returns an output portassociated with the "path tofile" string (which is systemdependent). A run-time error issignalled if "path to file"specifies an illegal or inaccessiblefile.(write obj port)

Writes a single object, obj, to theoutput port specified by port.

172CS 538 Spring 2008©

(display obj port)

Writes obj to the output portspecified by port. display uses amore readable format than writedoes. (Strings aren’t quoted, andcharacters aren’t escaped.)(close-input-port port)

This closes the input portspecified by port.(close-output-port port)

This closes the output portspecified by port.

Page 44: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

173CS 538 Spring 2008©

Example—Reading & Echoing aFile

We will iterate through a file,reading and echoing its contents.We need a good way to doiteration; recursion is neithernatural nor efficient here.Scheme provides a nicegeneralization of the letexpression that is similar to C’sfor loop.(let X ( (id1 val1) (id2 val2) ... ) ... (X v1 v2 ...))

A name for the let (X in theexample) is provided. As usual,val1 is evaluated and bound toid1, val2 is evaluated and boundto id2, etc. In the body of the let,the let may be “called” (using its

174CS 538 Spring 2008©

name) with a fresh set of valuesfor the let variables. Thus (X v1v2 ...) starts the next iterationof the let with id1 bound to v1,id2, bound to v2, etc.The calls look like recursion, butthey are implemented as loopiterations.For example, in(let loop ( (x 1) (sum 0) ) (if (<= x 10) (loop (+ x 1) (+ sum x))

sum ))

we sum the values of x from 1 to10.Compare it tofor (x=1,sum=0; x <= 10; sum+=x,x+=1)

{}

175CS 538 Spring 2008©

Now a function to read and echo afile is straightforward:(define (echo filename) (let (

(p (open-input-file filename))) (let loop ( (obj (read p))) (if (eof-object? obj) #t ;normal termination (begin (write obj) (newline) (loop (read p)) ) ) ) ))

176CS 538 Spring 2008©

We can create an alternative toecho that uses(call-with-input-file filename function)

This function opens filename,creates an input port from it, andthen calls function with thatport as an argument:(define (echo2 filename) (call-with-input-file filename (lambda(port)

(let loop ( (obj (read port))) (if (eof-object? obj) #t (begin (write obj) (newline) (loop (read port)) ) ) ) )) )

Page 45: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

177CS 538 Spring 2008©

Control Flow in SchemeNormally, Scheme’s control flow issimple and recursive:• The first argument is evaluated to

get a function.

• Remaining arguments areevaluated to get actual parameters.

• Actual parameters are bound to thefunction’s formal parameters.

• The functions’ body is evaluated toobtain the value of the functioncall.

This approach routinely leads todeeply nested expressionevaluation.

178CS 538 Spring 2008©

As an example, consider a simplefunction that multiplies a list ofintegers:(define (*list L) (if (null? L) 1 (* (car L)(*list (cdr L))) ))

The call (*list '(1 2 3 4 5))expands to(* 1 (* 2 (* 3 (* 4 (* 5 1)))))

But,what if we get clever and decideto improve this function by notingthat if 0 appears anywhere in listL, the product must be 0?

179CS 538 Spring 2008©

Let’s try(define (*list0 L) (cond ((null? L) 1) ((= 0 (car L)) 0) (else (* (car L) (*list0 (cdr L)))) ))

This helps a bit—we never gopast a zero in L, but we stillunnecessarily do a sequence ofpending multiplies, all of whichmust yield zero!Can we escape from a sequenceof nested calls once we knowthey’re unnecessary?

180CS 538 Spring 2008©

ExceptionsIn languages like Java, astatement may throw anexception that’s caught by anenclosing exception handler.Code between the statement thatthrows the exception and thehandler that catches it isabandoned.Let’s solve the problem ofavoiding multiplication of zero inJava, using its exceptionmechanism:class Node {

int val;

Node next;

}

class Zero extends Throwable{};

Page 46: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

181CS 538 Spring 2008©

int mult (Node L) {

try {

return multNode(L);

} catch (Zero z) {

return 0;

}

}

int multNode(Node L)

throws Zero {

if (L == null)

return 1;

else if (L.val == 0)

throw new Zero();

else return L.val * multNode(L.next);

}

In this implementation, nomultiplies by zero are ever done.

182CS 538 Spring 2008©

ContinuationsIn our Scheme implementation of*list, we’d like a way to delaydoing any multiplies until weknow no zeros appear in the list.One approach is to build acontinuation—a function thatrepresents the context in which afunction’s return value will beused:(define (*listC L con)

(cond ((null? L) (con 1)) ((= 0 (car L)) 0) (else (*listC (cdr L) (lambda (n) (* n (con (car L))))) ) ))

183CS 538 Spring 2008©

The top-level call is(*listC L (lambda (x) x))

For ordinary lists *listC expandsto a series of multiplies, just like*list did.(define (id x) x)

(*listC '(1 2 3) id) ⇒(*listC '(2 3) (lambda (n) (* n (id 1)))) ≡(*listC '(2 3) (lambda (n) (* n 1))) ⇒(*listC '(3) (lambda (n) (* n (* 2 1)))) ≡(*listC '(3) (lambda (n) (* n 2))) ⇒(*listC () (lambda (n) (* n (* 3 2)))) ≡(*listC () (lambda (n) (* n 6)))

⇒ (* 1 6) ⇒ 6

184CS 538 Spring 2008©

But for a list with a zero in it, weget a different execution path:(*listC '(1 0 3) id) ⇒(*listC '(0 3)(lambda (n) (* n (id 1)))) ⇒ 0

No multiplies are done!

Page 47: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

185CS 538 Spring 2008©

Another Example ofContinuations

Let’s redo our list multiplyexample so that if a zero is seenin the list we return a functionthat computes the product of allthe non-zero values and aparameter that is the“replacement value” for theunwanted zero value. Thefunction gives the caller a chanceto correct a probable error in theinput data.We create(*list2 L) ≡Product of all integers in L ifno zero appears

else(lambda (n) (* n product-of-all-nonzeros-in-L)

186CS 538 Spring 2008©

(define (*list2 L) (*listE L id))

(define (*listE L con) (cond ((null? L) (con 1)) ((= 0 (car L)) (lambda(n) (* (con n) (*listE (cdr L) id)))) (else (*listE (cdr L) (lambda(m) (* m (con (car L)))))) ))

187CS 538 Spring 2008©

In the following, we check to seeif *list2 returns a number or afunction. If a function is returned,we call it with 1, effectivelyremoving 0 from the list(let ( (V (*list2 L)) )

(if (number? V)

V

(V 1)

)

)

188CS 538 Spring 2008©

For ordinary lists *list2 expandsto a series of multiplies, just like*list did.(*listE '(1 2 3) id) ⇒(*listE '(2 3) (lambda (m) (* m (id 1)))) ≡(*listE '(2 3) (lambda (m) (* m 1))) ⇒(*listE '(3) (lambda (m) (* m (* 2 1)))) ≡(*listE '(3) (lambda (m) (* m 2))) ⇒(*listE () (lambda (m) (* m (* 3 2)))) ≡(*listE () (lambda (n) (* n 6)))

⇒ (* 1 6) ⇒ 6

Page 48: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

189CS 538 Spring 2008©

But for a list with a zero in it, weget a different execution path:(*listE '(1 0 3) id) ⇒(*listE '(0 3) (lambda (m) (* m (id 1)))) ⇒(lambda (n) (* (con n) (* listE '(3) id))) ≡(lambda (n) (* (* n 1)

(* listE '(3) id))) ≡(lambda (n) (* (* n 1) 3))

This function multiplies n, thereplacement value for 0, by 1 and3, the non-zero values in the inputlist.

190CS 538 Spring 2008©

But note that only one zero valuein the list is handled correctly!Why?(define (*listE L con) (cond ((null? L) (con 1)) ((= 0 (car L)) (lambda(n) (* (con n)

(*listE (cdr L) id)))) (else (*listE (cdr L) (lambda(m) (* m (con (car L)))))) ))

191CS 538 Spring 2008©

Continuations in SchemeScheme provides a built-inmechanism for creatingcontinuations. It has a long name:call-with-current-continuation

This name is often abbreviated ascall/cc

(perhaps using define).call/cc takes a single function asits argument. That function alsotakes a single argument. That is,we use call/cc as

(call/cc funct) wherefunct ≡ (lambda (con) (body))call/cc calls the function that itis given with the “currentcontinuation” as the function’sargument.

192CS 538 Spring 2008©

Current ContinuationsWhat is the current continuation?It is itself a function of oneargument. The currentcontinuation function representsthe execution context withinwhich the call/cc appears. Theargument to the continuation is avalue to be substituted as thereturn value of call/cc in thatexecution context.For example, given(+ (fct n) 3)

the current continuation for(fct n) is (lambda (x) (+ x 3)

Given (* 2 (+ (fct z) 10))

the current continuation for (fct z) is(lambda (m) (* 2 (+ m 10))

Page 49: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

193CS 538 Spring 2008©

To use call/cc to grab acontinuation in (say) (+ (fct n) 3)we make (fct n) the body of afunction of one argument. Call thatargument return. We therefore build(lambda (return) (fct n))

Then(call/cc (lambda (return) (fct n)))

binds the current continuation toreturn and executes (fct n).We can ignore the currentcontinuation bound to returnand do a normal returnorwe can use return to force areturn to the calling context of thecall/cc.The call (return value) forcesvalue to be returned as the valueof call/cc in its context of call.

194CS 538 Spring 2008©

Example:

(define (g con) (con 5))

Now during evaluation no divideby zero error occurs. Rather, when(g return) is called, 5 is passedto con, which is bound to return.Therefore 5 is used as the value ofthe call to call/cc, and 50 iscomputed.

(* (call/cc (lambda(return) (/ (g return) 0))) 10)

return

195CS 538 Spring 2008©

Continuations are JustFunctions

Continuations may be saved invariables or data structures andcalled in the future to “reactive” acompleted or suspendedcomputation.(define CC ())(define (F) (let ( (v (call/cc (lambda(here) (set! CC here) 1))))

(display "The ans is: ") (display v)(newline)

) )

This displays The ans is: 1

At any time in the future, (CC 10)will display The ans is: 10

196CS 538 Spring 2008©

List Multiplication RevisitedWe can use call/cc toreimplement the original *list toforce an immediate return of 0(much like a throw in Java):(define (*listc L return) (cond ((null? L) 1) ((= 0 (car L)) (return 0)) (else (* (car L)

(*listc (cdr L) return)))) )

(define (*list L) (call/cc (lambda (return) (*listc L return)) ))

A 0 in L forces a call of (return0) which makes 0 the value ofcall/cc.

Page 50: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

197CS 538 Spring 2008©

Interactive Replacement ofError Values

Using continuations, we can alsoredo *listE so that zeroes canbe replaced interactively! Multiplezeroes (in both original andreplacement values) are correctlyhandled.(define (*list L) (let ( (V (call/cc (lambda (here) (*liste L here)))) ) (if (number? V) V (begin (display "Enter new value for 0") (newline) (newline) (V (read)) ) ) ))

198CS 538 Spring 2008©

(define (*liste L return) (if (null? L) 1 (let loop ((value (car L))) (if (= 0 value) (loop (call/cc (lambda (x) (return x)))) (* value (*liste (cdr L) return)) ) ) ))

If a zero is seen, *liste passesback to the caller (via return) acontinuation that will set the nextvalue of value. This value ischecked, so if it is itself zero, asubstitute is requested. Eachoccurrence of zero forces a returnto the caller for a substitute value.

199CS 538 Spring 2008©

Implementing Coroutines withcall/cc

Coroutines are a very handygeneralization of subroutines. Acoroutine may suspend itsexecution and later resume fromthe point of suspension. Unlikesubroutines, coroutines do nohave to complete their executionbefore they return.Coroutines are useful forcomputation of long or infinitestreams of data, where we wish tocompute some data, use it,compute additional data, use it,etc.Subroutines aren’t always able tohandle this, as we may need tosave a lot of internal state toresume with the correct nextvalue.

200CS 538 Spring 2008©

Producer/Consumer usingCoroutines

The example we will use is one ofa consumer of a potentiallyinfinite stream of data. The nextinteger in the stream (representedas an unbounded list) is read. Callthis value n. Then the next nintegers are read and summedtogether. The answer is printed,and the user is asked whetheranother sum is required. Since wedon’t know in advance how manyintegers will be needed, we’ll usea coroutine to produce the datalist in segments, requestinganother segment as necessary.

Page 51: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

201CS 538 Spring 2008©

(define (consumer)

(next 0); reset next function (let loop ((data (moredata))) (let ( (sum+restoflist (sum-n-elems (car data) (cons 0 (cdr data))))) (display (car sum+restoflist)) (newline) (display "more? ") (if (equal? (read) ’y) (if (= 1

(length sum+restoflist)) (loop (moredata))

(loop (cdr sum+restoflist)) )

#t ; Normal completion ) ) ))

202CS 538 Spring 2008©

Next, we’ll consider sum-n-elems, which adds the firstelement of list (a running sum) tothe next n elements on the list.We’ll use moredata to extend thedata list as needed.(define (sum-n-elems n list) (cond ((= 0 n) list) ((null? (cdr list)) (sum-n-elems n

(cons (car list)(moredata)))) (else (sum-n-elems (- n 1) (cons (+ (car list) (cadr list)) (cddr list))))

))

203CS 538 Spring 2008©

The function moredata is calledwhenever we need more data.Initially a producer function iscalled to get the initial segment ofdata. producer actually returnsthe next data segment plus acontinuation (stored inproducer-cc) used to resumeexecution of producer when thenext data segment is required.

204CS 538 Spring 2008©

(define moredata (let ( (producer-cc () ) ) (lambda () (let ( (data+cont (if (null? producer-cc) (call/cc (lambda (here)

(producer here))) (call/cc (lambda (here) (producer-cc here))) ) )) (set! producer-cc (cdr data+cont)) (car data+cont) ) ) ))

Page 52: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

205CS 538 Spring 2008©

Function (next z) returns thenext z integers in an infinitesequence that starts at 1. A valuez=0 is a special flag indicatingthat the sequence should be resetto start at 1.(define next (let ( (i 1)) (lambda (z) (if (= 0 z) (set! i 1) (let loop

((cnt z) (val i) (ints () )) (if (> cnt 0) (loop (- cnt 1) (+ val 1) (append ints (list val))) (begin (set! i val) ints ) ) )) ) ) )

206CS 538 Spring 2008©

The function producer generatesan infinite sequence of integers(1,2,3,...). It suspends every 5/10/15/25 elements and returnscontrol to moredata.(define (producer initial-return) (let loop ( (return initial-return) ) (set! return (call/cc (lambda (here)

(return (cons (next 5) here))))) (set! return (call/cc (lambda (here)

(return (cons (next 10) here))))) (set! return (call/cc (lambda (here) (return (cons (next 15) here))))) (loop (call/cc (lambda (here)

(return (cons (next 25) here)))))) )

207CS 538 Spring 2008©

Reading Assignment• MULTILISP: a language for concurrent

symbolic computation,by Robert H. Halstead(linked from class web page)

208CS 538 Spring 2008©

Lazy EvaluationLazy evaluation is sometimescalled “call by need.” We do anevaluation when a value is used;not when it is defined.Scheme provides for lazyevaluation:(delay expression)

Evaluation of expression isdelayed. The call returns a“promise” that is essentially alambda expression.(force promise)

A promise, created by a call todelay, is evaluated. If the promisehas already been evaluated, thevalue computed by the first call toforce is reused.

Page 53: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

209CS 538 Spring 2008©

Example:Though and is predefined, writinga correct implementation for it isa bit tricky.The obvious program(define (and A B)

(if A

B

#f

)

)

is incorrect since B is alwaysevaluated whether it is needed ornot. In a call like(and (not (= i 0)) (> (/ j i) 10))

unnecessary evaluation might befatal.

210CS 538 Spring 2008©

An argument to a function is strictif it is always used. Non-strictarguments may cause failure ifevaluated unnecessarily.With lazy evaluation, we candefine a more robust andfunction:(define (and A B)

(if A

(force B)

#f

)

)

This is called as:(and (not (= i 0)) (delay (> (/ j i) 10)))

Note that making the programmerremember to add a call to delayis unappealing.

211CS 538 Spring 2008©

Delayed evaluation also allows usa neat implementation ofsuspensions.The following definition of aninfinite list of integers clearlyfails:(define (inflist i)

(cons i (inflist (+ i 1))))

But with use of delays we get thedesired effect in finite time:(define (inflist i) (cons i (delay (inflist (+ i 1)))))

Now a call like (inflist 1)creates

1 promise for(inflist 2)

212CS 538 Spring 2008©

We need to slightly modify howwe explore suspended infinitelists. We can’t redefine car andcdr as these are far toofundamental to tamper with.Instead we’ll define head andtail to do much the same job:(define head car)

(define (tail L)

(force (cdr L)))

head looks at car values whichare fully evaluated.tail forces one level ofevaluation of a delayed cdr andsaves the evaluated value in placeof the suspension (promise).

Page 54: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

213CS 538 Spring 2008©

Given(define IL (inflist 1))

(head (tail IL)) returns 2 andexpands IL into

2 promise for(inflist 3)

1

214CS 538 Spring 2008©

Exploiting ParallelismConventional proceduralprogramming languages aredifficult to compile formultiprocessors.Frequent assignments make itdifficult to find independentcomputations.Consider (in Fortran):

do 10 I = 1,1000 X(I) = 0 A(I) = A(I+1)+1 B(I) = B(I-1)-1 C(I) = (C(I-2) + C(I+2))/210 continue

This loop defines 1000 values forarrays X, A, B and C.

215CS 538 Spring 2008©

Which computations can be donein parallel, partitioning parts of anarray to several processors, eachoperating independently?• X(I) = 0

Assignments to X can be readilyparallelized.

• A(I) = A(I+1)+1Each update of A(I) uses an A(I+1)value that is not yet changed. Thusa whole array of new A values canbe computed from an array of “old”A values in parallel.

• B(I) = B(I-1)-1This is less obvious. Each B(I)uses B(I-1) which is defined interms of B(I-2), etc. Ultimately allnew B values depend only on B(0)and I. That is, B(I) = B(0) - I. Sothis computation can beparallelized, but it takes a fairamount of insight to realize it.

216CS 538 Spring 2008©

• C(I) = (C(I-2) + C(I+2))/2It is clear that even and oddelements of C don’t interact. Hencetwo processors could computeeven and odd elements of C inparallel. Beyond this, since bothearlier and later C values are usedin each computation of an element,no further means of parallelevaluation is evident. Serialevaluation will probably be neededfor even or odd values.

Page 55: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

217CS 538 Spring 2008©

Exploiting Parallelism inScheme

Assume we have a shared-memory multiprocessor. We mightbe able to assign differentprocessors to evaluate variousindependent subexpressions.For example, consider(map (lambda(x) (* 2 x)) '(1 2 3 4 5))We might assign a processor toeach list element and computethe lambda function on eachconcurrently:

1 2 3 4 5

2 4 6 8 10

Processor 1 Processor 5...

218CS 538 Spring 2008©

How is Parallelism Found?There are two approaches:• We can use a “smart” compiler that is

able to find parallelism in existingprograms written in standard serialprogramming languages.

• We can add features to an existingprogramming language that allows aprogrammer to show where parallelevaluation is desired.

219CS 538 Spring 2008©

ConcurrentizationConcurrentization (often calledparallelization) is process ofautomatically finding potentialconcurrent execution in a serialprogram.Automatically finding currentexecution is complicated by anumber of factors:• Data Dependence

Not all expressions areindependent. We may need todelay evaluation of an operator orsubprogram until its operands areavailable.Thus in(+ (* x y) (* y z))

we can’t start the addition untilboth multiplications are done.

220CS 538 Spring 2008©

• Control DependenceNot all expressions need be (orshould be) evaluated.In(if (= a 0)

0

(/ b a))

we don’t want to do the divisionuntil we know a ≠ 0.

• Side EffectsIf one expression can write avalue that another expressionmight read, we probably will needto serialize their execution.Consider(define rand! (let ((seed 99))

(lambda () (set! seed (mod (* seed 1001) 101101)) seed)) )

Page 56: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

221CS 538 Spring 2008©

Now in(+ (f (rand!)) (g (rand!)))

we can’t evaluate (f (rand!))and (g (rand!)) in parallel,because of the side effect of set!in rand!. In fact if we did, f and gmight see exactly the same“random” number! (Why?)

• GranularityEvaluating an expressionconcurrently has an overhead (tosetup a concurrent computation).Evaluating very simpleexpressions (like (car x) or(+ x 1)) in parallel isn’t worththe overhead cost.Estimating where the “breakeven” threshold is may be tricky.

222CS 538 Spring 2008©

Utility of ConcurrentizationConcurrentization has been mostsuccessful in engineering andscientific programs that are veryregular in structure, evaluatinglarge multidimensional arrays insimple nested loops. Many verycomplex simulations (weather,fluid dynamics, astrophysics) arerun on multiprocessors afterextensive concurrentization.Concurrentization has been farless successful on non-scientificprograms that don’t use largearrays manipulated in nested forloops. A compiler, for example, isdifficult to run (in parallel) on amultiprocessor.

223CS 538 Spring 2008©

Concurrentization withinProcessors

Concurrentization is usedextensively within many modernuniprocessors. Pentium andPowerPC processors routinelyexecute several instructions inparallel if they are independent(e.g., read and write distinctregisters). This are superscalarprocessors.These processors also routinelyspeculate on execution paths,“guessing” that a branch will (orwon’t) be taken even before thebranch is executed! This allowsfor more concurrent executionthan if strictly “in order” executionis done. These processors arecalled “out of order” processors.

224CS 538 Spring 2008©

Adding Parallel Features toProgramming Languages.

It is common to take an existingserial programming language andadd features that supportconcurrent or parallel execution.For example versions for Fortran(like HPF—High PerformanceFortran) add a parallel do loopthat executes individual iterationsin parallel.Java supports threads, which maybe executed in parallel.Synchronization and mutualexclusion are provided to avoidunintended interactions.

Page 57: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

225CS 538 Spring 2008©

MultilispMultilisp is a version of Schemeaugmented with three parallelevaluation mechanisms:• Pcall

Arguments to a call are evaluatedin parallel.

• FutureEvaluation of an expression startsimmediately. Rather than waitingfor completion of the computation,a “future” is returned. This futurewill eventually transform itself intothe result value (when thecomputation completes)

• DelayEvaluation is delayed until theresult value is really needed.

226CS 538 Spring 2008©

The Pcall MechanismPcall is an extension to Scheme’sfunction call mechanism thatcauses the function and itsarguments to be all computed inparallel.Thus(pcall F X Y Z)

causes F, X, Y and Z to all beevaluated in parallel. When allevaluations are done, F is calledwith X, Y and Z as its parameters(just as in ordinary Scheme).Compare(+ (* X Y) (* Y Z))

with(pcall + (* X Y) (* Y Z))

227CS 538 Spring 2008©

It may not look like pcall cangive you that much parallelexecution, but in the context ofrecursive definitions, the effectcan be dramatic.Consider treemap, a version ofmap that operates on binary trees(S-expressions).

(define (treemap fct tree)

(if (pair? tree)

(pcall cons

(treemap fct (car tree))

(treemap fct (cdr tree))

)

(fct tree)

))

228CS 538 Spring 2008©

Look at the execution of treemapon the tree (((1 . 2) . (3 . 4)) .

((5 . 6) . (7 . 8)))

We start with one call that usesthe whole tree. This splits intotwo parallel calls, one operatingon((1 . 2) . (3 . 4))

and the other operating on((5 . 6) . (7 . 8))

Each of these calls splits into 2calls, and finally we have 8independent calls, each operatingon the values 1 to 8.

Page 58: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

229CS 538 Spring 2008©

FuturesEvaluation of an expression as afuture is the most interestingfeature of Multilisp.The call(future expr)

begins the evaluation of expr.But rather than waiting for expr’sevaluation to complete, the call tofuture returns immediately witha new kind of data object—afuture. This future is actually an“IOU.” When you try to use thevalue of the future, thecomputation of expr may or maynot be completed. If it is, you seethe value computed instead of thefuture—it automaticallytransforms itself. Thus evaluationof expr appears instantaneous.

230CS 538 Spring 2008©

If the computation of expr is notyet completed, you are forced towait until computation iscompleted. Then you may use thevalue and resume execution.But this is exactly what ordinaryevaluation does anyway—youbegin evaluation of expr and waituntil evaluation completes andreturns a value to you!

To see the usefulness of futures,consider the usual definition ofScheme’s map function:

(define (map f L) (if (null? L) () (cons (f (car L)) (map f (cdr L))) ))

231CS 538 Spring 2008©

If we have a call(map slow-function long-list)

where slow-function executesslowly and long-list is a largedata structure, we can expect towait quite a while forcomputation of the result list tocomplete.

Now consider fastmap, a versionof map that uses futures:(define (fastmap f L) (if (null? L) () (cons (future (f (car L))) (fastmap f (cdr L)) ) ))

Now look at the call(fastmap slow-function long-list)

232CS 538 Spring 2008©

We will exploit a useful aspect offutures—they can be cons’edtogether without delay, even if thecomputation isn’t completed yet.Why? Well a cons just stores a pairof pointers, and it really doesn’tmatter what the pointersreference (a future or an actualresult value).The call to fastmap can actuallyreturn before any of the call toslow-function have completed:

future1

future2

future3 ...

Page 59: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

233CS 538 Spring 2008©

Eventually all the futuresautomatically transformthemselves into data values:

Note that pcall can beimplemented using futures.

answer1

answer2

answer3 ...

234CS 538 Spring 2008©

That is, instead of(pcall F X Y Z)

we can use((future F)

(future X) (future Y) (future Z))

In fact the latter version isactually more parallel—executionof F can begin even if all theparameters aren’t completelyevaluated.

235CS 538 Spring 2008©

Another Example of FuturesThe following function,partition, will take a list and adata value (called pivot).partition will partition the listinto two sublists:(a) Those elements ≤ pivot

(b) Those elements > pivot(define (partition pivot L) (if (null? L) (cons () () ) (let ((tail-part

(partition pivot (cdr L)))) (if (<= (car L) pivot) (cons

(cons (car L) (car tail-part))(cdr tail-part))

(cons(car tail-part))(cons (car L) (cdr tail-part))

)) ) )

236CS 538 Spring 2008©

We want to add futures topartition, but where?It makes sense to use a futurewhen a computation may belengthy and we may not need touse the value computedimmediately.What computation fits thatpattern?The computation of tail-part.We’ll mark it in a blue box to showwe plan to evaluate it using afuture:

Page 60: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

237CS 538 Spring 2008©

(define (partition pivot L) (if (null? L) (cons () () ) (let ((tail-part

(partition pivot (cdr L)))) (if (<= (car L) pivot) (cons

(cons (car L) (car tail-part))(cdr tail-part))

(cons(car tail-part))(cons (car L) (cdr tail-part))

)) ) )

But this one change isn’t enough!We soon access the car and cdrof tail-part, which forces us towait for its computation tocomplete. To avoid this delay, wecan place the four references tocar or cdr of tail-part intofutures too:

238CS 538 Spring 2008©

(define (partition pivot L) (if (null? L) (cons () () ) (let ((tail-part

(partition pivot (cdr L)))) (if (<= (car L) pivot) (cons

(cons (car L) (car tail-part))(cdr tail-part))

(cons(car tail-part))(cons (car L) (cdr tail-part))

)) ) )

239CS 538 Spring 2008©

Now we can build the initial partof the partitioned list (thatinvolving pivot and (car L)independently of the recursive callof partition, which completesthe rest of the list.For example,(partition 17 '(5 3 8 ...))

creates a future (call it future1)to compute(partition 17 '(3 8 ...))

It also creates future2 tocompute (car tail-part) andfuture3 to compute (cdr tail-part). The call builds

5 future2

future3

240CS 538 Spring 2008©

Reading Assignment• Introduction to Standard ML

(linked from class web page)

• Webber: Chapters 5, 7, 9, 11

Page 61: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

241CS 538 Spring 2008©

ML—Meta LanguageSML is Standard ML, a popular MLvariant.ML is a functional language that isdesigned to be efficient and type-safe. It demonstrates that afunctional language need not useScheme’s odd syntax and neednot bear the overhead of dynamictyping.SML’s features and innovationsinclude:1. Strong, compile-time typing.2. Automatic type inferencerather than user-supplied typedeclarations.3. Polymorphism, including “typevariables.”

242CS 538 Spring 2008©

4. Pattern-directed Programmingfun len([]) = 0 | len(a::b) = 1+len(b);

5. Exceptions6. First-class functions7. Abstract Data Types

coin of int |bill of int |check of string*real;val dime = coin(10);

A good ML reference is“Elements of ML Programming,”by Jeffrey Ullman(Prentice Hall, 1998)

243CS 538 Spring 2008©

SML is InteractiveYou enter a definition orexpression, and SML returns aresult with an inferred type.The command use "file name";

loads a set of ML definitions froma file.For example (SML responses arein blue):21;val it = 21 : int

(2 div 3);val it = 0 : int

true;val it = true : bool

"xyz";val it = "xyz" : string

244CS 538 Spring 2008©

Basic SML Predefined Types• Unit

Its only value is (). Type unit issimilar to void in C; it is usedwhere a type is needed, but no“real” type is appropriate. Forexample, a call to a write functionmay return unit as its result.

• IntegerConstants are sequences of digits.Negative values are prefixed witha ~ rather than a - (- is a binarysubtraction operator). Forexample, ~123 is negative 123.Standard operators include+ - * div mod< > <= >= = <>

Page 62: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

245CS 538 Spring 2008©

• RealBoth fractional (123.456) andexponent forms (10e7) areallowed. Negative signs andexponents use ~ rather than -(~10.0e~12).Standard operators include+ - * /< > <= >=

Note that = and <> aren’t allowed!(Why?)Conversion routines includereal(int) to convert an int to areal,floor(real) to take the floor ofa real,ceil(real) to take the ceiling ofa real.round(real) to round a real,trunc(real) to truncate a real.

246CS 538 Spring 2008©

For example, real(3) returns3.0, floor(3.1) returns 3,ceiling(3.3) returns 4,round(~3.6) returns ~4,trunc(3.9) returns 3.Mixed mode expressions, like1 + 2.5 aren’t allowed; you mustdo explicit conversion, likereal(1) + 2.5

• StringsStrings are delimited by doublequotes. Newlines are \n, tabs are\t, and \" and \\ escape doublequotes and backslashes. E.g. "Byenow\n" The ^ operator isconcatenation."abc" ^ "def" = "abcdef"

The usual relational operators areprovided: < > <= >= = <>

247CS 538 Spring 2008©

• CharactersSingle characters are delimited bydouble quotes and prefixed by a#. For example, #"a" or #"\t". Acharacter is not a string of lengthone. The str function may beused to convert a character into astring. Thus str(#"a") = "a"

• BooleanConstants are true and false.Operators include andalso (short-circuit and), orelse (short-circuitor), not, = and <>.A conditional expression,(if boolval v1 else v2) isavailable.

248CS 538 Spring 2008©

TuplesA tuple type, composed of two ormore values of any type isavailable.Tuples are delimited byparentheses, and values areseparated by commas.Examples include:(1,2);val it = (1,2) : int * int

("xyz",1=2);val it = ("xyz",false) : string * bool

(1,3.0,false);val it = (1,3.0,false) : int * real * bool

(1,2,(3,4));val it = (1,2,(3,4)) :int * int * (int * int)

Page 63: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

249CS 538 Spring 2008©

Equality is checkedcomponentwise:(1,2) = (0+1,1+1);val it = true : bool

(1,2,3) = (1,2) causes acompile-time type error (tuplesmust be of the same length andhave corresponding types to becompared).#i selects the i-th component ofa tuple (counting from 1). Hence#2(1,2,3);val it = 2 : int

250CS 538 Spring 2008©

ListsLists are required to have a singleelement type for all theirelements; their length isunbounded.Lists are delimited by [ and ] andelements are separated bycommas.Thus [1,2,3] is an integer list.The empty (or null) list is [] ornil.The cons operator is ::Hence [1,2,3] ≡ 1::2::3::[]

Lists are automatically typed byML:[1,2];val it = [1,2] : int list

251CS 538 Spring 2008©

ConsCons is an infix operatorrepresented as ::The left operand of :: is anyvalue of type T.The right operand of :: is any listof type T list.The result of :: is a list of typeT list.

Hence :: is polymorphic.[] is the empty list. It has a type'a list. The symbol 'a, read as“alpha” or “tic a” is a type variable.Thus [] is a polymorphicconstant.

252CS 538 Spring 2008©

List EqualityTwo lists may be compared forequality if they are of the sametype. Lists L1 and L2 areconsidered equal if:(1) They have the same number of elements(2) Corresponding members of

the two lists are equal.

List Operatorshd ≡ head of list operator ≈ car

tl ≡ tail of list operator ≈ cdr

null ≡ null list predicate ≈ null?

@ ≡ infix list append operator ≈append

Page 64: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

253CS 538 Spring 2008©

RecordsTheir general form is{name1=val1, name2=val2, ... }

Field selector names are local to arecord.For example:{a=1,b=2};

val it = {a=1,b=2} : {a:int, b:int}

{a=1,b="xyz"};val it = {a=1,b="xyz"} : {a:int, b:string}

{a=1.0,b={c=[1,2]}};val it = {a=1.0,b={c=[1,2]}} :{a:real, b:{c:int list}}

254CS 538 Spring 2008©

The order of fields is irrelevant;equality is tested using fieldnames.{a=1,b=2}={b=2,a=2-1};val it = true : bool

#id extracts the field named idfrom a record.#b {a=1,b=2} ;

val it = 2 : int

255CS 538 Spring 2008©

IdentifiersThere are two forms:• Alphanumeric (excluding reserved

words)

Any sequence of letters, digits,single quotes and underscores;must begin with a letter or singlequote.Case is significant. Identifiers thatbegin with a single quote are typevariables.Examples include:abc a10 'polar sum_of_20

• Symbolic

Any sequence (except predefinedoperators) of! % & + - / : < = > ? @ \ ~ ^ | #Usually used for user-definedoperators.Examples include: ++ <=> !=

256CS 538 Spring 2008©

CommentsOf form(* text *)

May cross line boundaries.

Declaration of ValuesThe basic form isval id = expression;

This defines id to be bound toexpression; ML answers with thename and value defined and theinferred type.For exampleval x = 10*10;val x = 100 : int

Page 65: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

257CS 538 Spring 2008©

Redefinition of an identifier is OK,but this is redefinition notassignment;Thusval x = 100;

val x = (x=100);

is fine; there is no type error eventhough the first x is an integerand then it is a boolean.val x = 100 : int

val x = true : bool

258CS 538 Spring 2008©

Examplesval x = 1;val x = 1 : int

val z = (x,x,x);val z = (1,1,1) : int * int * int

val L = [z,z];val L = [(1,1,1),(1,1,1)] : (int * int * int) list

val r = {a=L};val r = {a=[(1,1,1),(1,1,1)]} :{a:(int * int * int) list}

After rebinding, the “nearest”(most recent) binding is used.The and symbol (not boolean and)is used for simultaneous binding:val x = 10;val x = 10 : int

val x = true and y = x;val x = true : bool

val y = 10 : int

259CS 538 Spring 2008©

Local definitions are temporaryvalue definitions:local

val x = 10

in

val u = x*x;

end;val u = 100 : int

Let bindings are used inexpressions:let

val x = 10

in 5*x

end;val it = 50 : int

260CS 538 Spring 2008©

Patterns Scheme (and most otherlanguages) use access ordecomposition functions to accessthe components of a structuredobject.Thus we might write(let ( (h (car L) (t (cdr L)) )

body )

Here car and cdr are used asaccess functions to locate theparts of L we want to access.In ML we can access componentsof lists (or tuples, or records)directly by using patterns. Thecontext in which the identifierappears tells us the part of thestructure it references.

Page 66: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

261CS 538 Spring 2008©

val x = (1,2);val x = (1,2) : int * int

val (h,t) = x;val h = 1 : int

val t = 2 : int

val L = [1,2,3];val L = [1,2,3] : int list

val [v1,v2,v3] = L;val v1 = 1 : int

val v2 = 2 : int

val v3 = 3 : int

val [1,x,3] = L;val x = 2 : int

val [1,rest] = L;(* This is illegal. Why? *)

val yy::rest = L;val yy = 1 : int

val rest = [2,3] : int list

262CS 538 Spring 2008©

WildcardsAn underscore (_) may be used asa “wildcard” or “don’t care”symbol. It matches part of astructure without defining an newbinding.val zz::_ = L;val zz = 1 : int

Pattern matching works in recordstoo.val r = {a=1,b=2};val r = {a=1,b=2} : {a:int, b:int}

val {a=va,b=vb} = r;val va = 1 : int

val vb = 2 : int

val {a=wa,b=_}=r;val wa = 1 : int

val {a=za, ...}=r;val za = 1 : int

263CS 538 Spring 2008©

Patterns can be nested too.val x = ((1,3.0),5);val x = ((1,3.0),5) : (int * real) * int

val ((1,y),_)=x;val y = 3.0 : real

264CS 538 Spring 2008©

FunctionsFunctions take a single argument(which can be a tuple).Function calls are of the formfunction_name argument;

For examplesize "xyz";

cos 3.14159;

The more conventional formsize("xyz"); or cos(3.14159);

is OK (the parentheses around theargument are allowed, butunnecessary).The form (size "xyz") or(cos 3.14159)

is OK too.

Page 67: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

265CS 538 Spring 2008©

Note that the callplus(1,2);

passes one argument, the tuple(1,2)

to plus.The call dummy();passes one argument, the unitvalue, to dummy.All parameters are passed byvalue.

266CS 538 Spring 2008©

Function TypesThe type of a function in ML isdenoted as T1->T2. This says thata parameter of type T1 is mappedto a result of type T2.The symbol fn denotes a valuethat is a function.Thussize;val it = fn : string -> int

not;val it = fn : bool -> bool

Math.cos;val it = fn : real -> real

(Math is an ML structure—anexternal library member thatcontains separately compileddefinitions).

267CS 538 Spring 2008©

User-Defined FunctionsThe general form isfun name arg = expression;

ML answers back with the namedefined, the fact that it is afunction (the fn symbol) and itsinferred type.For example,fun twice x = 2*x;val twice = fn : int -> int

fun twotimes(x) = 2*x;val twotimes = fn : int -> int

fun fact n =

if n=0

then 1

else n*fact(n-1);val fact = fn : int -> int

268CS 538 Spring 2008©

fun plus(x,y):int = x+y;val plus = fn : int * int -> int

The :int suffix is a typeconstraint.It is needed to help ML decide that+ is integer plus rather than realplus.

Page 68: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

269CS 538 Spring 2008©

Patterns In FunctionDefinitions

The following defines a predicatethat tests whether a list, L is null(the predefined null functionalready does this).fun isNull L = if L=[] then true elsefalse;val isNull = fn : 'a list -> bool

However, we can decompose thedefinition using patterns to get asimpler and more elegantdefinition: fun isNull [] = true

| isNull(_::_) = false;val isNull = fn : 'a list -> bool

270CS 538 Spring 2008©

The “|” divides the functiondefinition into different argumentpatterns; no explicit conditionallogic is needed. The definitionthat matches a particular actualparameter is automaticallyselected.fun fact(1) = 1

| fact(n) = n*fact(n-1);val fact = fn : int -> int

If patterns that cover all possiblearguments aren’t specified, youmay get a run-time Matchexception.If patterns overlap you may get awarning from the compiler.

271CS 538 Spring 2008©

fun append([],L) = L

| append(hd::tl,L) =hd::append(tl,L);

val append = fn : 'a list * 'a list -> 'a list

If we add the patternappend(L,[]) = L

we get a redundant patternwarning (Why?)fun append ([],L) = L

| append(hd::tl,L) =hd::append(tl,L)

| append(L,[]) = L;stdIn:151.1-153.20 Error: matchredundant

(nil,L) => ...

(hd :: tl,L) => ...

--> (L,nil) => ...

272CS 538 Spring 2008©

But a more precise decompositionis fine:fun append ([],L) = L

| append(hd::tl,hd2::tl2) = hd::append(tl,hd2::tl2)

| append(hd::tl,[]) = hd::tl;val append = fn : 'a list * 'a list -> 'a list

Page 69: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

273CS 538 Spring 2008©

Function Types Can bePolytypes

Recall that 'a, 'b, ... representtype variables. That is, any validtype may be substituted for themwhen checking type correctness.ML said the type of append isval append = fn : 'a list * 'a list -> 'a list

Why does 'a appear three times?We can define eitherNull, apredicate that determineswhether either of two lists is nullasfun eitherNull(L1,L2) = null(L1) orelse null(L2);val eitherNull = fn : ’a list * ’b list -> bool

Why are both 'a and 'b used ineitherNull’s type?

274CS 538 Spring 2008©

CurryingML chooses the most general(least-restrictive) type possible foruser-defined functions.Functions are first-class objects,as in Scheme.The function definitionfun f x y = expression;

defines a function f (of x) thatreturns a function (of y).Reducing multiple argumentfunctions to a sequence of oneargument functions is calledcurrying (after Haskell Curry, amathematician who popularizedthe approach).

275CS 538 Spring 2008©

Thusfun f x y = x :: [y];val f = fn : 'a -> 'a -> 'a list

says that f takes a parameter x,of type 'a, and returns a function(of y, whose type is 'a) thatreturns a list of 'a.Contrast this with the moreconventionalfun g(x,y) = x :: [y];val g = fn : 'a * 'a -> 'a list

Here g takes a pair of arguments(each of type 'a) and returns avalue of type 'a list.The advantage of currying is thatwe can bind one argument andleave the remaining argument(s)free.

276CS 538 Spring 2008©

For examplef(1);

is a legal call. It returns a functionof typefn : int -> int list

The function returned isequivalent tofun h b = 1 :: [b];val h = fn : int -> int list

Page 70: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

277CS 538 Spring 2008©

Map RevisitedML supports the map function,which can be defined asfun map(f,[]) = [] | map(f,x::y) = (f x) :: map(f,y);val map =fn : ('a -> 'b) * 'a list -> 'b list

This type says that map takes apair of arguments. One is afunction from type 'a to type 'b.The second argument is a list oftype 'a. The result is a list of type'b.In curried form map is defined asfun map f [] = [] | map f (x::y) = (f x) :: map f y;val map = fn : ('a -> 'b) -> 'a list -> 'b list

278CS 538 Spring 2008©

This type says that map takes oneargument that is a function fromtype 'a to type 'b. It returns afunction that takes an argumentthat is a list of type 'a and returnsa list of type 'b.The advantage of the curried formof map is that we can now use mapto create “specialized” functionsin which the function that ismapped is fixed.For example,val neg = map not;val neg = fn : bool list -> bool list

neg [true,false,true];val it = [false,true,false] : bool list

279CS 538 Spring 2008©

Power Sets RevisitedLet’s compute power sets in ML.We want a function pow that takesa list of values, viewed as a set,and which returns a list of lists.Each sublist will be one of thepossible subsets of the originalargument.For example,pow [1,2] = [[1,2],[1],[2],[]]

We first define a version of consin curried form:fun cons h t = h::t;val cons = fn : 'a -> 'a list -> 'a list

280CS 538 Spring 2008©

Now we define pow. We define thepowerset of the empty list, [], tobe [[]]. That is, the power set ofthe empty set is set that containsonly the empty set.For a non-empty list, consisting ofh::t, we compute the power setof t, which we call pset. Then thepower set for h::t is just hdistributed through psetappended to pset.We distribute h through pset veryelegantly: we just map thefunction (cons h) to pset. (consh) adds h to the head of any list itis given. Thus mapping (cons h)to pset adds h to all lists in pset.

Page 71: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

281CS 538 Spring 2008©

The complete definition is simplyfun pow [] = [[]] | pow (h::t) =

let

val pset = pow t

in

(map (cons h) pset) @ pset

end;val pow = fn : 'a list -> 'a list list

Let’s trace the computation ofpow [1,2].Here h = 1 and t = [2]. We needto compute pow [2].Now h = 2 and t = [].We know pow [] = [[]],so pow [2] =(map (cons 2) [[]])@[[]] =([[2]])@[[]] = [[2],[]]

282CS 538 Spring 2008©

Therefore pow [1,2] =

(map (cons 1) [[2],[]])@[[2],[]] =

[[1,2],[1]]@[[2],[]] =[[1,2],[1],[2],[]]

283CS 538 Spring 2008©

Composing FunctionsWe can define a compositionfunction that composes twofunctions into one:fun comp (f,g)(x) = f(g(x));val comp = fn :('a -> 'b) * ('c -> 'a) -> 'c -> 'b

In curried form we havefun comp f g x = f(g(x));

val comp = fn :('a -> 'b) ->('c -> 'a) -> 'c -> 'b

For example,fun sqr x:int = x*x;

val sqr = fn : int -> int

comp sqr sqr;

val it = fn : int -> int

comp sqr sqr 3;

val it = 81 : int

284CS 538 Spring 2008©

In SML o (lower-case O) is the infixcomposition operator.Hencesqr o sqr ≡ comp sqr sqr

Page 72: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

285CS 538 Spring 2008©

Lambda TermsML needs a notation to writedown unnamed (anonymous)functions, similar to the lambdaexpressions Scheme uses.That notation isfn arg => body;

For example,val sqr = fn x:int => x*x;val sqr = fn : int -> int

In fact the notation used to definefunctions,fun name arg = body;

is actually just an abbreviation forthe more verboseval name = fn arg => body;

286CS 538 Spring 2008©

An anonymous function can beused wherever a function value isneeded.For example,map (fn x => [x]) [1,2,3];val it =[[1],[2],[3]] : int list list

We can use patterns too:(fn [] => [] |(h::t) => h::h::t);val it = fn : 'a list -> 'a list

(What does this function do?)

287CS 538 Spring 2008©

Polymorphism vs. OverloadingML supports polymorphism.A function may accept a polytype(a set of types) rather than asingle fixed type.In all cases, the same functiondefinition is used. Details of thesupplied type are irrelevant andmay be ignored.For example,fun id x = x;val id = fn : 'a -> 'a

fun toList x = [x];val toList = fn : 'a -> 'a list

288CS 538 Spring 2008©

Overloading, as in C++ and Java,allows alternative definitions ofthe same method or operator,with selection based on type.Thus in Java + may representinteger addition, floating pointaddition or string concatenation,even though these are reallyrather different operations.In ML +, -, * and = areoverloaded.When = is used (to test equality),ML deduces that an equality typeis required. (Most,but not all, typescan be compared for equality).When ML decides an equality typeis needed, it uses a type variablethat begins with two tics ratherthan one.fun eq(x,y) = (x=y);val eq = fn : ''a * ''a -> bool

Page 73: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

289CS 538 Spring 2008©

Defining New Types in MLWe can create new names forexisting types (typeabbreviations) usingtype id = def;

For example,type triple = int*real*string;type triple = int * real * string

type rec1= {a:int,b:real,c:string};type rec1 = {a:int, b:real, c:string}

type 'a triple3 = 'a*'a*'a;type 'a triple3 = 'a * 'a * 'a

type intTriple = int triple3;type intTriple = int triple3

These type definitions areessentiality macro-like namesubstitutions.

290CS 538 Spring 2008©

The Datatype MechanismThe datatype mechanismspecifies new data types usingvalue constructors.For example,datatype color = red|blue|green;

datatype color = blue | green |red

Pattern matching works too usingthe type’s constructors:fun translate red = "rot" | translate blue = "blau" | translate green = "gruen";val translate = fn : color -> stringfun jumble red = blue | jumble blue = green | jumble green = red;

val jumble = fn : color -> color

translate (jumble green);

val it = "rot" : string

291CS 538 Spring 2008©

SML ExamplesSource code for most of the SMLexamples presented here may befound in~cs538-1/public/sml/class.sml

292CS 538 Spring 2008©

Parameterized ConstructorsThe constructors used to definedata types may be parameterized:datatype money = none | coin of int

| bill of int

| iou of real * string;datatype money = bill of int | coin of int | iou of real * string | none

Now expressions like coin(25)or bill(5) oriou(10.25,"Lisa") representvalid values of type money.

Page 74: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

293CS 538 Spring 2008©

We can also define values andfunctions of type money:val dime = coin(10);val dime = coin 10 : money

val deadbeat =iou(25.00,"Homer Simpson");val deadbeat = iou (25.0,"Homer Simpson") : money

fun amount(none) = 0.0

| amount(coin(cents)) = real(cents)/100.0

| amount(bill(dollars)) = real(dollars)

| amount(iou(amt,_)) = 0.5*amt; val amount = fn : money -> real

294CS 538 Spring 2008©

Polymorphic DatatypesA user-defined data type may bepolymorphic. An excellentexample isdatatype 'a option = none | some of 'a;datatype 'a option = none | some of 'a

val zilch = none;val zilch = none : 'a option

val mucho =some(10e10);val mucho =some 100000000000.0 : real option

type studentInfo = {name:string, ssNumber:int option};type studentInfo = {name:string,ssNumber:int option}

295CS 538 Spring 2008©

val newStudent ={name="Mystery Man", ssNumber=none}:studentInfo;val newStudent ={name="Mystery Man", ssNumber=none} : studentInfo

296CS 538 Spring 2008©

Datatypes may be RecursiveRecursive datatypes allow linkedstructures without explicitpointers.datatype binTree = null| leaf

| node of binTree * binTree;datatype binTree =leaf | node of binTree * binTree | null

fun size(null) = 0

| size(leaf) = 1

| size(node(t1,t2)) = size(t1)+size(t2) + 1val size = fn : binTree -> int

Page 75: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

297CS 538 Spring 2008©

Recursive Datatypes may bePolymorphic

datatype 'a binTree = null| leaf of 'a| node of 'a binTree * 'a binTree

datatype 'a binTree = leaf of 'a | node of 'a binTree * 'a binTree | null

fun frontier(null) = [] | frontier(leaf(v)) = [v] | frontier(node(t1,t2)) = frontier(t1) @ frontier(t2)

val frontier = fn : 'a binTree -> 'a list

298CS 538 Spring 2008©

We can model n-ary trees by usinglists of subtrees:datatype 'a Tree = null| leaf of 'a| node of 'a Tree list;datatype 'a Tree = leaf of 'a |node of 'a Tree list | null

fun frontier(null) = []

| frontier(leaf(v)) = [v]

| frontier(node(h::t)) = frontier(h) @frontier(node(t))

| frontier(node([])) = []val frontier = fn : 'a Tree -> 'a list

299CS 538 Spring 2008©

Abstract Data TypesML also provides abstract datatypes in which theimplementation of the type ishidden from users.The general form isabstype name = implementation

with

val and fun definitions

end;

Users may access the name of theabstract type and the val and fundefinitions that follow the with,but the implementation may beused only with the body of theabstype definition.

300CS 538 Spring 2008©

Exampleabstype 'a stack = stk of 'a list

with

val Null = stk([])

fun empty(stk([])) = true

| empty(stk(_::_)) = false

fun top(stk(h::_)) = h fun pop(stk(_::t)) = stk(t) fun push(v,stk(L)) = stk(v::L)

endtype 'a stack

val Null = - : 'a stack

val empty = fn : 'a stack -> bool

val top = fn : 'a stack -> 'a

val pop = fn : 'a stack -> 'a stack

val push = fn : 'a * 'a stack -> 'a stack

Page 76: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

301CS 538 Spring 2008©

Local value and functiondefinitions, not to be exported tousers of the type can be createdusing the local definitionmechanism described earlier:local

val and fun definitions

in

exported definitions

end;

302CS 538 Spring 2008©

abstype 'a stack = stk of 'a listwithlocal fun size(stk(L))=length(L); in val Null = stk([]) fun empty(s) = (size(s) = 0)fun top(stk(h::_)) = h

fun pop(stk(_::t)) = stk(t) fun push(v,stk(L)) = stk(v::L) endendtype 'a stackval Null = - : 'a stackval empty = fn : 'a stack -> boolval top = fn : 'a stack -> 'aval pop = fn : 'a stack -> 'a stackval push = fn : 'a * 'a stack -> 'a stack

303CS 538 Spring 2008©

Why are abstract data typesuseful?Because they hide animplementation of a type from auser, allowing implementationchanges without any impact onuser programs.Consider a simple implementationof queues:abstype 'a queue = q of 'a list

with

val Null = q([])

fun front(q(h::_)) = h fun rm(q(_::t)) = q(t) fun enter(v,q(L)) = q(rev(v::rev(L)))

endtype 'a queue

val Null = - : 'a queue

val front = fn : 'a queue -> 'a

304CS 538 Spring 2008©

val rm = fn : 'a queue -> 'a queue

val enter = fn : 'a * 'a queue -> 'a queue

This implementation of queues isvalid, but somewhat inefficient. Inparticular to enter a new valueonto the rear end of a queue, wedo the following:fun enter(v,q(L)) = q(rev(v::rev(L)))

We reverse the list thatimplements the queue, add thenew value to the head of thereversed queue then reverse thelist a second time.

Page 77: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

305CS 538 Spring 2008©

A more efficient (but less obvious)implementation of a queue is tostore it as two lists. One listrepresents the “front” of thequeue. It is from this list that weextract the front value, and fromwhich we remove elements.The other list represents the“back” of the queue (in reversedorder). We add elements to therear of the queue by addingelements to the front of the list.From time to time, when the frontlist becomes null, we “promote”the rear list into the front list (byreversing it). Now access to boththe front and the back of thequeue is fast and direct. The newimplementation is:

306CS 538 Spring 2008©

abstype 'a queue = q of 'a list * 'a list

with

val Null = q([],[])

fun front(q(h::_,_)) = h | front(q([],L)) = front(q(rev(L),[])) fun rm(q(_::t,L)) = q(t,L) | rm(q([],L)) = rm(q(rev(L),[])) fun enter(v,q(L1,L2)) = q(L1,v::L2)

end

type 'a queue

val Null = - : 'a queueval front = fn :'a queue -> 'a

val rm = fn :'a queue -> 'a queueval enter = fn :'a * 'a queue -> 'a queue

307CS 538 Spring 2008©

From the user’s point of view, thetwo implementations are identical(they export exactly the same setof values and functions). Hencethe new implementation canreplace the old implementationwithout any impact at all to theuser (except, of course,performance!).

308CS 538 Spring 2008©

Exception HandlingOur definitions of stacks andqueues are incomplete.Reconsider our definition ofstack:abstype 'a stack = stk of 'a list

with

val Null = stk([])

fun empty(stk([])) = true

| empty(stk(_::_)) = false

fun top(stk(h::_)) = h fun pop(stk(_::t)) = stk(t) fun push(v,stk(L)) = stk(v::L)

end

What happens if we evaluatetop(Null);

Page 78: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

309CS 538 Spring 2008©

We see “match failure” since ourdefinition of top is incomplete!In ML we can raise anexception if an illegal orunexpected operation occurs.Asking for the top of an emptystack ought to raise anexception since the requestedvalue does not exist.ML contains a number ofpredefined exceptions,includingMatch Empty Div Overflow

(exception names usuallybegin with a capital letter).Predefined exception are raisedby illegal values or operations.If they are not caught, the run-time prints an error message.

310CS 538 Spring 2008©

fun f(1) = 2;val f = fn : int -> int

f(2);uncaught exception nonexhaustivematch failure

hd [];uncaught exception Empty

1000000*1000000;uncaught exception overflow

(1 div 0);uncaught exception divide by zero

1.0/0.0;

val it = inf : real

(inf is the IEEE floating-pointstandard “infinity” value)

311CS 538 Spring 2008©

User Defined ExceptionsNew exceptions may bedefined asexception name;

orexception name of type;

For exampleexception IsZero;exception IsZero

exception NegValue of real;exception NegValue of real

312CS 538 Spring 2008©

Exceptions May be RaisedThe raise statement raises(throws) an exception:raise exceptionName;

orraise exceptionName(expr);

For examplefun divide(a,0) = raise IsZero | divide(a,b) = a div b;val divide = fn : int * int -> int

divide(10,3);val it = 3 : int

divide(10,0);uncaught exception IsZero

Page 79: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

313CS 538 Spring 2008©

val sqrt = Real.Math.sqrt;val sqrt = fn : real -> real

fun sqroot(x) = if x < 0.0 then raise NegValue(x) else sqrt(x);val sqroot = fn : real -> real

sqroot(2.0);val it = 1.41421356237 : real

sqroot(~2.0);uncaught exception NegValue

314CS 538 Spring 2008©

Exception HandlersYou may catch an exception bydefining a handler for it:(expr) handle exception1 => val1 || exception2 => val2 || ... ;

For example,(sqroot ~100.0) handle NegValue(v) => (sqrt (~v));val it = 10.0 : real

315CS 538 Spring 2008©

Stacks RevisitedWe can add an exception,EmptyStk, to our earlier stacktype to handle top or popoperations on an empty stack:abstype 'a stack = stk of 'a listwith val Null = stk([]) exception EmptyStk fun empty(stk([])) = true | empty(stk(_::_)) = false fun top(stk(h::_)) = h | top(stk([])) = raise EmptyStk fun pop(stk(_::t)) = stk(t) | pop(stk([])) = raise EmptyStk fun push(v,stk(L)) = stk(v::L)end

316CS 538 Spring 2008©

type 'a stackval Null = - : 'a stackexception EmptyStkval empty = fn : 'a stack -> boolval top = fn : 'a stack -> 'aval pop = fn : 'a stack -> 'a stackval push = fn : 'a * 'a stack ->'a stack

pop(Null);uncaught exception EmptyStktop(Null) handle EmptyStk => 0;val it = 0 : int

Page 80: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

317CS 538 Spring 2008©

User-Defined OperatorsSML allows users to definesymbolic operators composedof non-alphanumericcharacters. This meansoperator-like symbols can becreated and used. Care must betaken to avoid predefinedoperators (like +, -, ^, @, etc.).If we wish, we can redo ourstack definition using symbolsrather than identifiers. Wemight use the followingsymbols:top |=

pop <==

push ==>

null <@>

empty <?>

318CS 538 Spring 2008©

We can have expressions like<?> <@>;val it = true : bool

|= (==> (1,<@>));val it = 1 : int

Binary functions, like ==> (push)are much more readable if theyare infix. That is, we’d like to beable to write1 ==> 2+3 ==> <@>

which pushes 2+3, then 1 ontoan empty stack.To make a function (eitheridentifier or symbolic) infixrather than prefix we use thedefinitioninfix level name

orinfixr level name

319CS 538 Spring 2008©

level is an integer representingthe “precedence” level of theinfix operator. 0 is the lowestprecedence level; higherprecedence operators areapplied before lowerprecedence operators (in theabsence of explicitparentheses).infix defines a left-associativeoperator (groups from left toright). infixr defines a right-associative operator (groupsfrom right to left).Thusfun cat(L1,L2) = L1 @ L2;

infix 5 cat

makes cat a left associativeinfix operator at the same

320CS 538 Spring 2008©

precedence level as @. We cannow write[1,2] cat [3,4,5] cat [6,7];val it = [1,2,3,4,5,6,7] : int list

The standard predefinedoperators have the followingprecedence levels:Level Operator3 o

4 = <> < > <= >=

5 :: @

6 + - ^

7 * / div mod

Page 81: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

321CS 538 Spring 2008©

If we define ==> (push) asinfixr 2 ==>

then1 ==> 2+3 ==> <@>

will work as expected,evaluating expressions like 2+3before doing any pushes, withpushes done right to left.

322CS 538 Spring 2008©

abstype 'a stack = stk of 'a list

with

val <@> = stk([])

exception emptyStk

fun <?>(stk([])) = true

| <?>(stk(_::_)) = false

fun |=(stk(h::_)) = h

| |=(stk([])) = raise emptyStk

fun <==(stk(_::t)) = stk(t)

| <==(stk([])) = raise emptyStk

fun ==>(v,stk(L)) = stk(v::L)

infixr 2 ==>

end

323CS 538 Spring 2008©

type 'a stack

val <@> = - : 'a stack

exception emptyStk

val <?> = fn : 'a stack -> bool

val |= = fn : 'a stack -> 'a

val <== = fn : 'a stack -> 'a stack

val ==> = fn : 'a * 'a stack ->'a stack

infixr 2 ==>

Now we can writeval myStack = 1 ==> 2+3 ==> <@>;val myStack = - : int stack

|= myStack;val it = 1 : int

|= (<== myStack);val it = 5 : int

324CS 538 Spring 2008©

Using Infix Operators asValues

Sometimes we simply want touse an infix operator as asymbol whose value is afunction.For example, givenfun dupl f v = f(v,v);val dupl =fn : ('a * 'a -> 'b) -> 'a -> 'b

we might try the calldupl ^ "abc";

This fails because SML tries toparse dupl and "abc" as theoperands of ^.To pass an operator as anordinary function value, weprefix it with op which tells the

Page 82: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

325CS 538 Spring 2008©

SML compiler that the followingsymbol is an infix operator.

Thusdupl op ^ "abc";val it = "abcabc" : string

works fine.

326CS 538 Spring 2008©

The Case ExpressionML contains a case expressionpatterned on switch and casestatements found in otherlanguages.As in function definitions,patterns are used to chooseamong a variety of values.The general form of the case iscase expr of

pattern1 => expr1|

patternn => expr2|

...

patternn => exprn;

If no pattern matches, a Matchexception is thrown.

327CS 538 Spring 2008©

It is common to use _ (thewildcard) as the last pattern ina case.Examples includecase c of

red => "rot" |

blue => "blau" |

green => "gruen";

case pair of

(1,_) => "win" |

(2,_) => "place" |

(3,_) => "show" |

(_,_) => "loser";

case intOption of

none => 0 |

some(v) => v;

328CS 538 Spring 2008©

Imperative Features of MLML provides references to heaplocations that may be updated.This is essentially the same asaccess to heap objects viareferences (Java) or pointers (Cand C++).The expressionref val

creates a reference to a heaplocation initialized to val. Forexample, ref 0; val it = ref 0 : int ref

The prefix operator ! fetchesthe value contained in a heaplocation (just as * dereferencesa pointer in C or C++).

Page 83: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

329CS 538 Spring 2008©

Thus ! (ref 0); val it = 0 : int

The expressionref := val

updates the heap locationreferenced by ref to containval. The unit value, (), isreturned.Henceval x = ref 0;val x = ref 0 : int ref

!x;val it = 0 : int

x:=1;val it = () : unit

!x;val it = 1 : int

330CS 538 Spring 2008©

Sequential CompositionExpressions or statements aresequenced using “;”. Henceval a = (1+2;3+4);val a = 7 : int

(x:=1;!x);val it = 1 : int

Iterationwhile expr1 do expr2

implements iteration (andreturns unit); Thus(while false do 10);val it = () : unit

while !x > 0 do x:= !x-1;val it = () : unit

!x;val it = 0 : int

331CS 538 Spring 2008©

Simple I/OThe function print;

val it = fn : string -> unit

prints a string onto standardoutput.For example,print("Hello World\n");

Hello World

The conversion routines Real.toString; val it = fn : real -> string

Int.toString; val it = fn : int -> string

Bool.toString; val it = fn : bool -> string

332CS 538 Spring 2008©

convert a value (real, int orbool) into a string. Unlike Java,the call must be explicit.For example,print(Int.toString(123));123

Also available areReal.fromString;val it = fn : string -> realoption

Int.fromString;val it = fn : string -> intoption

Bool.fromString;val it = fn : string -> booloption

which convert from a string toa real or int or bool if possible.(That’s why the option type isused).

Page 84: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

333CS 538 Spring 2008©

For example,case (Int.fromString("123")) of

SOME(i) => i | NONE => 0;val it = 123 : int

case (Int.fromString( "One two three")) of

SOME(i) => i | NONE => 0;val it = 0 : int

334CS 538 Spring 2008©

Text I/OThe structure TextIO contains awide variety of I/O types,values and functions. You loadthese by entering:open TextIO;

Among the values loaded are• type instream

This is the type that representsinput text files.

• type outstreamThis is the type that representsoutput text files.

• type vector = stringMakes vector a synonym forstring.

• type elem = charMakes elem a synonym for char.

335CS 538 Spring 2008©

• val stdIn : instreamval stdOut : outstreamval stdErr : outstreamPredefined input & output streams.

• val openIn : string -> instreamval openOut : string -> outstreamOpen an input or output stream.For example,val out = openOut("/tmp/test1");val out = - : outstream

• val input : instream -> vectorRead a line of input into a string(vector is defined as equivalent tostring). For example (user input isin red):val s = input(stdIn);Hello! val s = "Hello!\n" : vector

336CS 538 Spring 2008©

• val inputN : instream * int -> vectorRead the next N input charactersinto a string. For example,val t = inputN(stdIn,3);abcdeval t = "abc" : vector

• val inputAll : instream -> vectorRead the rest of the input file into astring (with newlines separatinglines). For example,val u = inputAll(stdIn); Four score and seven years ago ... val u = "Four score and\nseven years ago ...\n" : vector

• val endOfStream : instream -> boolAre we at the end of this inputstream?

Page 85: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

337CS 538 Spring 2008©

• val output : outstream * vector -> unitOutput a string on the specifiedoutput stream. For example,output(stdOut, "That’s all folks!\n");That’s all folks!

338CS 538 Spring 2008©

String OperationsML provides a wide variety ofstring manipulation routines.Included are:• The string concatenation operator,

^ "abc" ^ "def" = "abcdef"

• The standard 6 relationaloperators: < > <= >= = <>

• The string size operator:val size : string -> intsize ("abcd");val it = 4 : int

• The string subscripting operator(indexing from 0):val sub = fn : string * int -> charsub("abcde",2);val it = #"c" : char

339CS 538 Spring 2008©

• The substring functionval substring :string * int * int -> stringThis function is called assubstring(string,start,len)start is the starting position,counting from 0.len is the length of the desiredsubstring. For example,substring("abcdefghij",3,4)val it = "defg" : string

• Concatenation of a list of stringsinto a single string:concat : string list -> stringFor example,concat ["What’s"," up","?"];val it = "What’s up?" : string

340CS 538 Spring 2008©

• Convert a character into a string:str : char -> stringFor example, str(#"x");val it = "x" : string

• “Explode” a string into a list ofcharacters:explode : string -> char listFor example,explode("abcde");val it =[#"a",#"b",#"c",#"d",#"e"] :char list

• “Implode” a list of characters into astring.implode : char list -> stringFor example,implode[#"a",#"b",#"c",#"d",#"e"];val it = "abcde" : string

Page 86: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

341CS 538 Spring 2008©

Structures and SignaturesIn C++ and Java you can groupvariable and functiondefinitions into classes. In Javayou can also group classes intopackages.In ML you can group value,exception and functiondefinitions into structures.You can then import selecteddefinitions from the structure(using the notationstructure.name) or you can openthe structure, therebyimporting all the definitionswithin the structure.(Examples used in this sectionmay be found at~cs538-1/public/sml/struct.sml)

342CS 538 Spring 2008©

The general form of a structuredefinition isstructure name =struct

val, exception and fun definitions

end

For example,structure Mapping =struct exception NotFound; val create = []; fun lookup(key,[]) = raise NotFound | lookup(key, (key1,value1)::rest) = if key = key1 then value1 else lookup(key,rest);

343CS 538 Spring 2008©

fun insert(key,value,[]) = [(key,value)] | insert(key,value, (key1,value1)::rest) = if key = key1 then (key,value)::rest else (key1,value1):: insert(key,value,rest);end;

We can access members of thisstructure as Mapping.name. ThusMapping.insert(538,"languages",[]);

val it = [(538,"languages")] :(int * string) list

open Mapping;exception NotFound

val create : 'a list

val insert : ''a * 'b * (''a * 'b) list -> (''a * 'b) list

val lookup : ''a * (''a * 'b)list -> 'b

344CS 538 Spring 2008©

SignaturesEach structure has a signature,which is it type.For example, Mapping’ssignature isstructure Mapping :

sig

exception NotFound

val create : 'a list

val insert : ''a * 'b * (''a * 'b) list -> (''a * 'b) list

val lookup : ''a * (''a * 'b) list -> 'b

end

Page 87: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

345CS 538 Spring 2008©

You can define a signature assignature name = sig

type definitions for values, functions and exceptions

end

For example,signature Str2IntMapping =sig exception NotFound; val lookup:

string * (string*int) list -> int;

end;

346CS 538 Spring 2008©

Signatures can be used to• Restrict the type of a value or

function in a structure.

• Hide selected definitions thatappear in a structure

For examplestructure Str2IntMap :

Str2IntMapping = Mapping;

defines a new structure,Str2IntMap, created byrestricting Mapping to theStr2IntMapping signature. Whenwe do this we get

347CS 538 Spring 2008©

open Str2IntMap; exception NotFound

val lookup : string * (string * int) list -> int

Only lookup and NotFound arecreated, and lookup is limited tokeys that are strings.

348CS 538 Spring 2008©

Extending ML’s PolymorphismIn languages like C++ and Javawe must use types like void* orObject to simulate thepolymorphism that MLprovides. In ML wheneverpossible a general type (apolytype) is used rather than afixed type. Thus infun len([]) = 0 | len(a::b) = 1 + len(b);

we get a type of 'a list -> int

because this is the mostgeneral type possible that isconsistent with len’s definition.Is this form of polymorphismgeneral enough to capture the

Page 88: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

349CS 538 Spring 2008©

general idea of makingprogram definitions as type-independent as possible?It isn’t, and to see why considerthe following ML definition of amerge sort. A merge sortoperates by first splitting a listinto two equal length sublists.The following function doesthis:fun split [] = ([],[]) | split [a] = ([a],[]) | split (a::b::rest) = let val (left,right) = split(rest) in (a::left, b::right) end;

350CS 538 Spring 2008©

After the input list is split intotwo halves, each half isrecursively sorted, then thesorted halves are mergedtogether into a single list.The following ML functionmerges two sorted lists intoone:

fun merge([],[]) = [] | merge([],hd::tl) = hd::tl | merge(hd::tl,[]) = hd::tl | merge(hd::tl,h::t) = if hd <= h then hd::merge(tl,h::t) else h::merge(hd::tl,t)

351CS 538 Spring 2008©

With these two subroutines, adefinition of a sort is easy:fun sort [] = [] | sort([a]) = [a] | sort(a::b::rest) = let val (left,right) = split(a::b::rest) in merge(sort(left),

sort(right)) end;

352CS 538 Spring 2008©

This definition looks verygeneral—it should work for alist of any type.Unfortunately, when ML typesthe functions we get a surprise:val split = fn : 'a list -> 'a list * 'a listval merge = fn : int list * int list -> int listval sort = fn : int list -> int list

split is polymorphic, but mergeand sort are limited to integerlists!Where did this restriction comefrom?

Page 89: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

353CS 538 Spring 2008©

The problem is that we did acomparison in merge using the<= operator, and ML typed thisas an integer comparison.We can make our definition ofsort more general by adding acomparison function, le(a,b)as a parameter to merge andsort. If we curry this parameterwe may be able to hide it fromend users. Our updateddefinitions are:fun merge(le,[],[]) = [] | merge(le,[],hd::tl) = hd::tl | merge(le,hd::tl,[]) = hd::tl | merge(le,hd::tl,h::t) = if le(hd,h) then hd::merge(le,tl,h::t) else h::merge(le,hd::tl,t)

354CS 538 Spring 2008©

fun sort le [] = [] | sort le [a] = [a] | sort le (a::b::rest) = let val (left,right) = split(a::b::rest) in merge(le, sort le left,

sort le right) end;

Now the types of merge andsort are:val merge = fn : ('a * 'a -> bool) * 'a list * 'a list -> 'a listval sort = fn : ('a * 'a -> bool) -> 'a list -> 'a list

We can now “customize” sortby choosing a particulardefinition for the le parameter:fun le(a,b) = a <= b;val le = fn : int * int -> bool

355CS 538 Spring 2008©

fun intsort L = sort le L;val intsort = fn : int list -> int listintsort( [4,9,0,2,111,~22,8,~123]);val it = [~123,~22,0,2,4,8,9,111]: int list

fun strle(a:string,b) = a <= b;val strle = fn : string * string -> bool

fun strsort L = sort strle L;val strsort =fn : string list -> string liststrsort( ["aac","aaa","ABC","123"]);val it =["123","ABC","aaa","aac"] :string list

356CS 538 Spring 2008©

Making the comparison relationan explicit parameter works,but it is a bit ugly andinefficient. Moreover, if we haveseveral functions that dependon the comparison relation, weneed to ensure that they all usethe same relation. Thus if wewish to define a predicateinOrder that tests if a list isalready sorted, we can use:fun inOrder le [] = true | inOrder le [a] = true | inOrder le (a::b::rest) = le(a,b) andalso inOrder le (b::rest);val inOrder = fn : ('a * 'a -> bool) -> 'a list -> bool

Now sort and inOrder need touse the same definition of le.But how can we enforce this?

Page 90: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

357CS 538 Spring 2008©

The structure mechanism westudied earlier can help. We canput a single definition of le inthe structure, and share it:structure Sorting =struct fun le(a,b) = a <= b;

fun split [] = ([],[]) | split [a] = ([a],[]) | split (a::b::rest) = let val (left,right) = split rest in

(a::left,b::right) end; fun merge([],[]) = [] | merge([],hd::tl) = hd::tl | merge(hd::tl,[]) = hd::tl | merge(hd::tl,h::t) = if le(hd,h) then hd::merge(tl,h::t) else h::merge(hd::tl,t)

358CS 538 Spring 2008©

fun sort [] = [] | sort([a]) = [a] | sort(a::b::rest) = let val (left,right) = split(a::b::rest) in merge(sort(left), sort(right)) end; fun inOrder [] = true | inOrder [a] = true | inOrder (a::b::rest) = le(a,b) andalso inOrder (b::rest);end;structure Sorting : sig val inOrder : int list -> bool val le : int * int -> bool val merge : int list * int list -> int list val sort : int list -> int list val split : 'a list -> 'a list * 'a list end

359CS 538 Spring 2008©

To sort a type other thanintegers, we replace thedefinition of le in the structure.But rather than actually editthat definition, ML gives us apowerful mechanism toparameterize a structure. Thisis the functor, which allows usto use one or more structuresas parameters in the definitionof a structure.

360CS 538 Spring 2008©

FunctorsThe general form of a functor isfunctor name (structName:signature) = structure definition;

This functor will create aspecific version of the structuredefinition using the structureparameter passed to it.For our purposes this is ideal—we pass in a structure definingan ordering relation (the lefunction). This then creates acustom version of all thefunctions defined in thestructure body, using thespecific le definition provided.

Page 91: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

361CS 538 Spring 2008©

We first definesignature Order =

sig

type elem

val le : elem*elem -> bool

end;

This defines the type of astructure that defines a lepredicate defined on a pair oftypes called elem.An example of such a structureisstructure IntOrder:Order =

struct

type elem = int;

fun le(a,b) = a <= b;

end;

362CS 538 Spring 2008©

Now we just define a functorthat creates a Sorting structurebased on an Order structure:functor MakeSorting(O:Order) =structopen O; (* makes le available*)

fun split [] = ([],[]) | split [a] = ([a],[]) | split (a::b::rest) = let val (left,right) = split rest in (a::left,b::right) end;

fun merge([],[]) = [] | merge([],hd::tl) = hd::tl | merge(hd::tl,[]) = hd::tl | merge(hd::tl,h::t) = if le(hd,h) then hd::merge(tl,h::t) else h::merge(hd::tl,t)

363CS 538 Spring 2008©

fun sort [] = [] | sort([a]) = [a] | sort(a::b::rest) = let val (left,right) = split(a::b::rest) in merge(sort(left),

sort(right)) end;

fun inOrder [] = true | inOrder [a] = true | inOrder (a::b::rest) = le(a,b) andalso inOrder (b::rest);end;

364CS 538 Spring 2008©

Nowstructure IntSorting = MakeSorting(IntOrder);

creates a custom structure forsorting integers: IntSorting.sort [3,0,~22,8];val it = [~22,0,3,8] : elem list

To sort strings, we just define astructure containing an ledefined for strings with Orderas its signature (i.e., type) andpass it to MakeSorting:structure StrOrder:Order =

struct

type elem = string

fun le(a:string,b) = a <= b;

end;

Page 92: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

365CS 538 Spring 2008©

structure StrSorting = MakeSorting(StrOrder);

StrSorting.sort( ["cc","abc","xyz"]);val it = ["abc","cc","xyz"] : StrOrder.elem list

StrSorting.inOrder( ["cc","abc","xyz"]);val it = false : bool

StrSorting.inOrder( [3,0,~22,8]);stdIn:593.1-593.32 Error:operator and operand don’t agree[literal]operator domain: strOrder.elem

list operand: int list in expression:

StrSorting.inOrder (3 :: 0 ::~22 :: <exp> :: <exp>)

366CS 538 Spring 2008©

The SML Basis LibrarySML provides a wide variety ofuseful types and functions,grouped into structures, thatare included in the BasisLibrary.A web page fully documentingthe Basis Library is linked fromthe ML page that is part of theProgramming Languages Linkspage on the CS 538 home page.Many useful types, operatorsand functions are “preloaded”when you start the SMLcompiler. These are listed in the“Top-level Environment” sectionof the Basis Librarydocumentation.

367CS 538 Spring 2008©

Many other useful definitionsmust be explicitly fetched fromthe structures they are definedin.For example, the Math structurecontains a number of usefulmathematical values andoperations.You may simply enteropen Math;

while will load all thedefinitions in Math. Doing thismay load more definitions thanyou want. What’s worse, adefinition loaded may redefinea definition you currently wantto stay active. (Recall that MLhas virtually no overloading, sofunctions with the same name

368CS 538 Spring 2008©

in different structures arecommon.)A more selective way to accessa definition is to qualify it withthe structure’s name. HenceMath.pi;val it = 3.14159265359 : real

gets the value of pi defined inMath.Should you tire of repeatedlyqualifying a name, you can (ofcourse) define a local value tohold its value. Thusval pi = Math.pi;val pi = 3.14159265359 : real

works fine.

Page 93: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

369CS 538 Spring 2008©

An Overview of Structures inthe Basis Library

The Basis Library contains awide variety of usefulstructures. Here is an overviewof some of the most importantones.• Option

Operations for the option type.• Bool

Operations for the bool type.• Char

Operations for the char type.• String

Operations for the string type.• Byte

Operations for the byte type.

370CS 538 Spring 2008©

• Int

Operations for the int type.• IntInf

Operations for an unboundedprecision integer type.

• Real

Operations for the real type.• Math

Various mathematical values andoperations.

• List

Operations for the list type.• ListPair

Operations on pairs of lists.• Vector

A polymorphic type forimmutable (unchangeable)sequences.

371CS 538 Spring 2008©

• IntVector, RealVector,BoolVector, CharVector

Monomorphic types forimmutable sequences.

• Array

A polymorphic type for mutable(changeable) sequences.

• IntArray, RealArray,BoolArray, CharArray

Monomorphic types for mutablesequences.

• Array2

A polymorphic 2 dimensionalmutable type.

• IntArray2, RealArray2,BoolArray2, CharArray2

Monomorphic 2 dimensionalmutable types.

• TextIO

Character-oriented text IO.

372CS 538 Spring 2008©

• BinIO

Binary IO operations.• OS, Unix, Date, Time, Timer

Operating systems types andoperations.

Page 94: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

373CS 538 Spring 2008©

ML Type InferenceOne of the most novel aspects ofML is the fact that it infers typesfor all user declarations.How does this type inferencemechanism work?Essentially, the ML compilercreates an unknown type for eachdeclaration the user makes. Itthen solves for these unknownsusing known types and a set oftype inference rules. That is, for auser-defined identifier i, ML wantsto determine T(i), the type of i.

374CS 538 Spring 2008©

The type inference rules are:1. The types of all predefinedliterals, constants and functionsare known in advance. They maybe looked-up and used. Forexample,2 : int

true : bool

[] : 'a list

:: : 'a * 'a list -> 'a list

2. All occurrences of the samesymbol (using scoping rules) havethe same type.

3. In the expressionI = J

we know T(I) = T(J).

375CS 538 Spring 2008©

4. In a conditional(if E1 then E2 else E3)

we know thatT(E1) = bool,T(E2) = T(E3) = T(conditional)

5. In a function call(f x)

we know that if T(f) = 'a -> 'b then T(x) = 'a and T(f x) = 'b

6. In a function definitionfun f x = expr;

if t(x) = 'a and T(expr) = 'b then T(f) = 'a -> 'b

7. In a tuple (e1,e2, ..., en)

if we know that T(ei) = 'ai 1 ≤ i ≤ n

then T(e1,e2, ..., en) = 'a1*'a2*...*'an

376CS 538 Spring 2008©

8. In a record { a=e1,b=e2, ... }

if T(ei) = 'ai 1 ≤ i ≤ n then the type of the record =

{a:'a1, b:'a2, ...}

9. In a list [v1,v2, ... vn]

if we know that T(vi) = 'ai 1 ≤ i ≤ n

then we know that'a1='a2=...='an andT([v1,v2, ... vn]) = 'a1 list

Page 95: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

377CS 538 Spring 2008©

To Solve for Types:1. Assign each untyped symbol its

own distinct type variable.2.Use rules (1) to (9) to solve for and

simplify unknown types.3. Verify that each solution “works”

(causes no type errors)throughout the program.

ExamplesConsiderfun fact(n)=if n=1 then 1 else n*fact(n-1);

To begin, we’ll assign typevariables:T(fact) = 'a -> 'b(fact is a function)T(n) = 'c

378CS 538 Spring 2008©

Now we begin to solve for thetypes 'a, 'b and 'c mustrepresent.We know (rule 5) that 'c = 'asince n is the argument of fact.We know (rule 3) that 'c = T(1)= int since n=1 is part of thedefinition.We know (rule 4) that T(1) =T(if expression)='b since the ifexpression is the body of fact.Thus, we have‘a = 'b ='c = int, soT(fact) = int -> int

T(n) = int

These types are correct for alloccurrences of fact and n in thedefinition.

379CS 538 Spring 2008©

A Polymorphic Function:fun leng(L) =

if L = []

then 0

else 1+len(tl L);

To begin, we know thatT([]) = 'a list andT(tl) = 'b list -> 'b list

We assign types to leng and L:T(leng) = 'c -> 'd

T(L) = 'e

Since L is the argument of leng,'e = 'c

From the expression L=[] weknow'e = 'a list

380CS 538 Spring 2008©

From the fact that 0 is the resultof the then, we know the ifreturns an int, so 'd = int.Thus T(leng) = 'a list -> intandT(L) = 'a list

These solutions are typecorrect throughout thedefinition.

Page 96: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

381CS 538 Spring 2008©

Type Inference for PatternsType inference works forpatterns too.Considerfun leng [] = 0

| leng (a::b) = 1 + leng b;

We first create type variables:T(leng) = 'a -> 'b

T(a) = 'c

T(b) = 'd

From leng [] we conclude that'a = 'e list

From leng [] = 0 we concludethat'b = int

From leng (a::b) we concludethat

382CS 538 Spring 2008©

'c ='e and 'd = 'e list

Thus we haveT(leng) = 'e list -> int

T(a) = 'e

T(b) = 'e list

This solution is type correctthroughout the definition.

383CS 538 Spring 2008©

Not Everything can beAutomatically Typed in ML

Let’s try to typefun f x = (x x);

We assumeT(f) = 'a -> 'b

t(x) = 'c

Now (as usual) 'a = 'c since x isthe argument of f.From the call (x x) we concludethat 'c must be of the form 'd ->'e (since x is being used as afunction).Moreover, 'c = 'd since x is anargument in (x x).Thus 'c = 'd ->'e = 'c ->'e.But 'c = 'c->'e has no solution,so in ML this definition is invalid.We can’t pass a function to itself

384CS 538 Spring 2008©

as an argument—the type systemdoesn’t allow it.In Scheme this is allowed:(define (f x) (x x))

but a call like(f f)

certainly doesn’t do anythinggood!

Page 97: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

385CS 538 Spring 2008©

Type UnionsLet’s try to typefun f g = ((g 3), (g true));

Now the type of g is 'a -> 'bsince g is used as a function.The call (g 3) says 'a = int andthe call (g true) says 'a =boolean.Does this mean g is polymorphic?That is, is the type of ff : ('a->'b)->'b*'b?NO!All functions have the type 'a ->'b but not all functions can bepassed to f.Consider not: bool->bool.The call (not 3) is certainlyillegal.

386CS 538 Spring 2008©

What we’d like in this case is aunion type. That is, we’d like to beable to type g as(int|bool)->'b which MLdoesn’t allow.Fortunately, ML does allow typeconstructors, which are just whatwe need.Givendatatype T = I of int|B of bool;

we can redefine f asfun f g = (g (I(3)), g (B(true)));val f = fn : (T -> 'a) -> 'a * 'a

387CS 538 Spring 2008©

Finally, note that in a definitionlikelet val f =

fn x => x (* id function*)in (f 3,f true)end;

type inference works fine:val it = (3,true) : int * bool

Here we define f in advance, soits type is known when calls toit are seen.

388CS 538 Spring 2008©

Reading Assignment• Webber: Chapters 19, 20 and 22

Page 98: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

389CS 538 Spring 2008©

PrologProlog presents a view ofprogramming that is verydifferent from most otherprogramming languages.A famous text book is entitled“Algorithms + Data Structures =Programs”This formula represents well theconventional approach toprogramming that mostprogramming languages support.In Prolog there is an alternativerule of programming:“Algorithms = Logic + Control”This rule encompasses a non-procedural view of programming.Logic (what the program is tocompute) comes first.

390CS 538 Spring 2008©

Then control (how to implementthe logic) is considered.In Prolog we program the logic ofa program, but the Prolog systemautomatically implements thecontrol.Logic is essential—control is justefficiency.

391CS 538 Spring 2008©

Logic ProgrammingProlog implements logicprogramming.In fact Prolog meansProgramming in Logic.

In Prolog programs arestatements of rules and facts.Program execution isdeduction—can an answer beinferred from known rules andfacts.Prolog was developed in 1972by Kowalski and Colmerauer atthe University of Marseilles.

392CS 538 Spring 2008©

Elementary Data Objects• In Prolog integers and atoms are the

elementary data objects.

• Integers are ordinary integer literalsand values.

• Atoms are identifiers that begin with alower-case letter (much like symbolicvalues in Scheme).

• In Prolog data objects are calledterms.

• In Prolog we define relations amongterms (integers, atoms or otherterms).

• A predicate names a relation.Predicates begin with lower-caseletters.

• To define a predicate, we write clausesthat define the relation.

Page 99: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

393CS 538 Spring 2008©

• There are two kinds of programclauses, facts and rules.

• A fact is a predicate that prefixes asequence of terms, and which endswith a period (“.”).

As an example, consider thefollowing facts which define“fatherOf” and “motherOf”relations.

fatherOf(tom,dick).

fatherOf(dick,harry).

fatherOf(jane,harry).

motherOf(tom,judy).

motherOf(dick,mary).

motherOf(jane,mary).

The symbols fatherOf and motherOfare predicates. The symbols tom,dick, harry, judy, mary and jane areatoms.

394CS 538 Spring 2008©

Once we have entered rules andfacts that define relations, we canmake queries (ask the Prologsystem questions).Prolog has two interactive modesthat you can switch between.To enter definition mode (to definerules and facts) you enter[user].

You then enter facts and rules,terminating this phase with ^D(end of file).Alternatively, you can enter['filename'].

to read in rules and facts stored inthe file named filename.

395CS 538 Spring 2008©

When you start Prolog, or afteryou leave definitions mode, youare in query mode.In query mode you see a promptof the form| ?- or ?- (depending on thesystem you are running).In query mode, Prolog allows youto ask whether a relation amongterms is true or false.Thus given our definition ofmotherOf and fatherOfrelations, we can ask:| ?- fatherOf(tom,dick).yes

A “yes” response means thatProlog is able to conclude fromthe facts and rules it has beengiven that the relation querieddoes hold.

396CS 538 Spring 2008©

| ?- fatherOf(georgeW,george).no

A “no” response to a query meansthat Prolog is unable to concludethat the relation holds from whatit has been told. The relation mayactually be true, but Prolog maylack necessary facts or rules todeduce this.

Page 100: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

397CS 538 Spring 2008©

Variables in QueriesOne of the attractive features ofProlog is the fact that variablesmay be included in queries. Avariable always begins with acapital letter.When a variable is seen, Prologtries to find a value (binding) forthe variable that will make thequeried relation true.For example,fatherOf(X,harry).

asks Prolog to find an value for Xsuch that X’s father is harry.When we enter the query, Prologgives us a solution (if one can befound): ?- fatherOf(X,harry).

X = dick

398CS 538 Spring 2008©

If no solution can be found, it tellsus so:| ?- fatherOf(Y,jane).no

Since solutions to queries neednot be unique, Prolog will give usalternate solutions if we ask forthem. We do so by entering a “;”after a solution is printed. We geta “no” when no more solutionscan be found:| ?- fatherOf(X,harry).

X = dick ;X = jane ;no

399CS 538 Spring 2008©

Variables may be placedanywhere in a query. Thus wemay ask| ?- fatherOf(jane,X).

X = harry ;no

We may use more than onevariable if we wish:| ?- fatherOf(X,Y).X = tom,

Y = dick ;X = dick,

Y = harry ;X = jane,

Y = harry ;

no(This query displays all thefatherOf relations).

400CS 538 Spring 2008©

Conjunction of GoalsMore than one relation can beincluded as the “goal” of a query.A comma (“,”) is used as an ANDoperator to indicate a conjunctionof goals—all must be satisfied bya solution to the query.| ?-fatherOf(jane,X),motherOf(jane,Y).

X = harry,

Y = mary ;no

A given variable may appear morethan once in a query. The samevalue of the variable must beused in all places in which thevariable appears (this is calledunification).

Page 101: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

401CS 538 Spring 2008©

For example,| ?-fatherOf(tom,X),fatherOf(X,harry).

X = dick ;no

402CS 538 Spring 2008©

Rules in PrologRules allow us to state that arelation will hold depending onthe truth (correctness) of otherrelations.In effect a rules says,“If I know that certain relationshold, then I also know that thisrelation holds.”A rule in Prolog is of the formrel1 :- rel2, rel3, ... reln.

This says rel1 can be assumedtrue if we can establish that rel2and rel3 and all relations to relnare true.rel1 is called the head of the rule.

rel2 to reln form the body of therule.

403CS 538 Spring 2008©

ExampleThe following two rules define agrandMotherOf relation using themotherOf and fatherOfrelations:grandMotherOf(X,GM) :-

motherOf(X,M), motherOf(M,GM).

grandMotherOf(X,GM) :-

fatherOf(X,F), motherOf(F,GM).

| ?- grandMotherOf(tom,GM).

GM = mary ;no

| ?- grandMotherOf(dick,GM).no

| ?- grandMotherOf(X,mary).

X = tom ;no

404CS 538 Spring 2008©

As is the case for allprogramming, in all languages,you must be careful when youdefine a rule that it correctlycaptures the idea you have inmind.Consider the following rule thatdefines a sibling relationbetween two people:sibling(X,Y) :-motherOf(X,M), motherOf(Y,M),fatherOf(X,F), fatherOf(Y,F).

This rule says that X and Y aresiblings if each has the samemother and the same father.But the rule is wrong!Why?

Page 102: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

405CS 538 Spring 2008©

Let’s give it a try:| ?- sibling(X,Y).

X = Y = tom

Darn! That’s right, you can’t beyour own sibling. So we refine therule to force X and Y to bedistinct:sibling(X,Y) :- motherOf(X,M), motherOf(Y,M), fatherOf(X,F), fatherOf(Y,F), not(X=Y).

(A few Prolog systems use “\+” fornot; but most include a notrelation.)| ?- sibling(X,Y).X = dick,

Y = jane ;X = jane,

Y = dick ;no

406CS 538 Spring 2008©

Note that distinct but equivalentsolutions (like X = dick,Y = jane vs.X = jane,Y = dick) often appear

in Prolog solutions. You maysometimes need to “filter out”solutions that are effectivelyredundant (perhaps byformulating stricter or moreprecise rules).

407CS 538 Spring 2008©

How Prolog Solves QueriesThe unique feature of Prolog isthat it automatically chooses thefacts and rules needed to solve aquery.But how does it make its choice?It starts by trying to solve eachgoal in a query, left to right (recallgoals are connected using “,”which is the and operator).For each goal it tries to match acorresponding fact or the head ofa corresponding rule.

A fact or head of rule matches agoal if:• Both use the same predicate.

• Both have the same number ofterms following the predicate.

408CS 538 Spring 2008©

• Each term in the goal and fact orrule head match (are equal),possibly binding a free variable toforce a match.

For example, assume we wish tomatch the following goal:x(a,B)

This can match the factx(a,b).

or the head of the rulex(Y,Z) :- Y = Z.

But x(a,B) can’t matchy(a,b) (wrong predicate name)orx(b,d) (first terms don’t match)orx(a,b,c) (wrong number ofterms).

Page 103: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

409CS 538 Spring 2008©

If we succeed in matching a rule,we have solved the goal inquestion; we can go on to matchany remaining goals.If we match the head of a rule, wearen’t done—we add the body ofthe rule to the list of goals thatmust be solved.Thus if we match the goal x(a,B)with the rulex(Y,Z) :- Y = Z.

then we must solve a=B which isdone by making B equal to a.

410CS 538 Spring 2008©

BacktrackingIf we reach a point where a goalcan’t be matched, or the body of arule can’t be matched, webacktrack to the last (mostrecent) spot where a choice ofmatching a particular fact or rulewas made. We then try to match adifferent fact or rule. If this failswe go back to the next previousplace where a choice was madeand try a different match there.We try alternatives until we areable to solve all the goals in ourquery or until all possible choiceshave been tried and found to fail.If this happens, we answer “no”the query can’t be solved.As we try to match facts and ruleswe try them in their order ofdefinition.

411CS 538 Spring 2008©

ExampleLet’s trace how| ?- grandMotherOf(tom,GM).

is solved.Recall thatgrandMotherOf(X,GM) :- motherOf(X,M), motherOf(M,GM).

grandMotherOf(X,GM) :- fatherOf(X,F), motherOf(F,GM).fatherOf(tom,dick).fatherOf(dick,harry).fatherOf(jane,harry).motherOf(tom,judy).motherOf(dick,mary).motherOf(jane,mary).

412CS 538 Spring 2008©

We try the first grandMotherOfrule first.This forces X = tom. We have tosolve motherOf(tom,M), motherOf(M,GM).

We now try to solvemotherOf(tom,M)

This forces M = judy.We then try to solvemotherOf(judy,GM)

None of the motherOf rulesmatch this goal, so we backtrack.No other motherOf rule can solvemotherOf(tom,M)

so we backtrack again and try thesecond grandMotherOf rule:

Page 104: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

413CS 538 Spring 2008©

grandMotherOf(X,GM) :- fatherOf(X,F), motherOf(F,GM).

This matches, forcing X = tom.We have to solvefatherOf(tom,F),motherOf(F,GM).We can match the first goal withfatherOf(tom,dick).

This forces F = dick.We then must solvemotherOf(dick,GM)

which can be matched bymotherOf(dick,mary).

We have matched all our goals, sowe know the query is true, withGM = mary.

414CS 538 Spring 2008©

List Processing in PrologProlog has a notation similar to“cons cells” of Lisp and Scheme.The “.” functor (predicate name)acts like cons.Hence .(a,b) in Prolog isessentially the same as (a . b)in Scheme.Lists in Prolog are formed muchthe same way as in Scheme andML:[] is the empty list[1,2,3] is an abbreviation for.(1, .(2, .(3,[])))

just as(1,2,3) in Scheme is anabbreviation for(cons 1 (cons 2 (cons 3 () )))

415CS 538 Spring 2008©

The notation [H|T] represents alist with H matching the head ofthe list and T matching the rest ofthe list.Thus [1,2,3] ≡ [1| [2,3]] ≡[1,2| [3]] ≡ [1,2,3| [] ]

As in ML, “_” (underscore) can beused as a wildcard or “don’t care”symbol in matches.Given the fact p([1,2,3,4]).

The query | ?- p([X|Y]).

answersX = 1,

Y = [2,3,4]

416CS 538 Spring 2008©

The queryp([_,_,X|Y]).

answersX = 3,

Y = [4]

Page 105: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

417CS 538 Spring 2008©

List Operations in PrologList operations are defined usingrules and facts. The definitionsare similar to those used inScheme or ML, but they are non-procedural.That is, you don’t given anexecution order. Instead, you giverecursive rules and non-recursive“base cases” that characterize theoperation you are defining.Consider append: append([],L,L).

append([H|T1],L2,[H|T3]) :- append(T1,L2,T3).

The first fact says that an emptylist (argument 1) appended to anylist L (argument 2) gives L(argument 3) as its answer.

418CS 538 Spring 2008©

The rule in line 2 says that if youtake a list that begins with H andhas T1 as the rest of the list andappend it to a list L then theresulting appended list will beginwith H.Moreover, the rest of the resultinglist, T3, is the result of appendingT1 (the rest of the first list) withL2 (the second input list).The query | ?- append([1],[2,3],[1,2,3]).

answersYes

because with H=1, T1=[], L2=[2,3] and T3=[2,3] it must bethe case thatappend([],[2,3],[2,3]) is trueand fact (1) says that this is so.

419CS 538 Spring 2008©

Inverting Inputs and OutputsIn Prolog the division between“inputs” and “outputs” isintentionally vague. We canexploit this. It is often possible to“invert” a query and ask whatinputs would compute a givenoutput. Few other languagesallow this level of flexibility. Consider the queryappend([1],X,[1,2,3]).

This asks Prolog to find a list Xsuch that if we append [1] to Xwe will get [1,2,3]. Prolog answersX = [2,3]

How does it choose this answer?

420CS 538 Spring 2008©

First Prolog tries to match thequery against fact (1) or rule (2).Fact (1) doesn’t match (the firstarguments differ) so we matchrule (2).This gives us H=1, T1=[], L2=Xand T3 = [2,3].We next have to solve the body ofrule (2) which isappend([],L2,[2,3]).

Fact (1) matches this, and tells usthat L2=[2,3]=X, and that’s ouranswer!

Page 106: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

421CS 538 Spring 2008©

The Member RelationA common predicate whenmanipulating lists is amembership test—is a givenvalue a member of a list?An “obvious” definition is arecursive one similar to what wemight program in Scheme or ML:member(X,[X|_]).member(X,[_|Y]):- member(X,Y).

This definition states that the firstargument, X, is a member of thesecond argument (a list) if Xmatches the head of the list or if Xis (recursively) a member of therest of the list.

Note that we don’t have to “tell”Prolog that X can’t be a memberof an empty list—if we don’t tell

422CS 538 Spring 2008©

Prolog that something is true, itautomatically assumes that itmust be false.Thus saying nothing aboutmembership in an empty list isthe same as saying thatmembership in an empty list isimpossible.Since inputs and outputs in arelation are blurred, we can usemember in an unexpected way—toiterate through a list of values.

If we want to know if any memberof a list L satisfies a predicate p,we cansimply write:member(X,L),p(X).

423CS 538 Spring 2008©

There is no explicit iteration orsearching. We simply ask Prologto find an X such thatmember(X,L) is true (X is in L)and p(X) is true. Backtrackingwill find the “right” value for X (ifany such X exists).This is sometimes called the“guess and verify” technique.Thus we can querymember(X,[3,-3,0,10,-10]), (X > 0).

This asks for an X in the list[3,-3,0,10,-10] which isgreater than 0.

Prolog answersX = 3 ;X = 10 ;

424CS 538 Spring 2008©

Note too that our “obvious”definition of member is not theonly one possible.An alternative definition (which isfar less obvious) ismember(X,L) :- append(_,[X|_],L).

This definition says X is a memberof L if I can take some list (whosevalue I don’t care about) andappend it to a list that begins withX (and which ends with values Idon’t care about) and get a listequal to L.Said more clearly, X is a memberof L if X is anywhere in the“middle” of L.Prolog solves a query involvingmember by partitioning the list Lin all possible ways, and checkingto see if X ever is the head of the

Page 107: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

425CS 538 Spring 2008©

second list. Thus formember(X,[1,2,3]), it tries thepartition [] and [1,2,3](exposing 1 as a possible X), then[1] and [2,3] (exposing 2) andfinally [1,2] and [3] (exposing3).

426CS 538 Spring 2008©

Sorting AlgorithmsSorting algorithms are goodexamples of Prolog’s definitionalcapabilities. In a Prolog definitionthe “logic” of a sorting algorithmis apparent, stripped of thecumbersome details of datastructures and control structuresthat dominate algorithms in otherprogramming languages.Consider the simplest possiblesort imaginable, which we’ll callthe “naive sort.”At the simplest level a sorting of alist L requires just two things:• The sorting is a permutation (a

reordering) of the values in L.

• The values are “in order”(ascending or descending).

427CS 538 Spring 2008©

We can implement this concept ofa sort directly in Prolog. We(a) permute an input list(b) check if it is in sorted order(c) repeat (a) & (b) until a sorting

is found.

428CS 538 Spring 2008©

PermutationsLet’s first look at howpermutations are defined inProlog. In most languagesgenerating permutations is non-trivial—you need data structuresto store the permutations you aregenerating and control structuresto visit all permutations in someorder.In Prolog, permutations aredefined quite concisely, thoughwith a bit of subtlety:perm(X,Y) will be true if list Y is apermutation of list X.Only two definitions are needed:perm([],[]).

perm(L,[H|T]) :- append(V,[H|U],L), append(V,U,W), perm(W,T).

Page 108: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

429CS 538 Spring 2008©

The first definition,perm([],[]).

is trivial. An empty list may onlybe permuted into another emptylist.The second definition is rathermore complex:perm(L,[H|T]) :-append(V,[H|U],L), append(V,U,W), perm(W,T).

This rule says a list L may bepermuted in to a list that beginswith H and ends with list T if:(1) L may be partitioned into two

lists, V and [H|U]. (That is, H is somewhere in the

“middle” of L).(2) Lists V and U (all of L except H) may be appended into list W.(3) List W may be permuted into T.

430CS 538 Spring 2008©

Let’s see perm in action:| ?- perm([1,2,3],X).

X = [1,2,3] ;

X = [1,3,2] ;

X = [2,1,3] ;

X = [2,3,1] ;

X = [3,1,2] ;

X = [3,2,1] ;no

We’ll trace how the first fewanswers are computed. Notethough that all permutations aregenerated, and with no apparentdata structures or controlstructures.We start with L=[1,2,3] andX=[H|T].We first solveappend(V,[H|U],L), which

431CS 538 Spring 2008©

simplifies toappend(V,[H|U],[1,2,3]).One solution to this goal isV = [], H = 1, U = [2,3]

We next solve append(V,U,W)which simplifies toappend([],[2,3],W).The only solution for this isW=[2,3].Finally, we solve perm(W,T),which simplifies toperm([2,3],T).One solution to this is T=[2,3].This gives us our first solution:[H|T]=[1,2,3].To get our next solution webacktrack. Where is the mostrecent place we made a choice ofhow to solve a goal?

432CS 538 Spring 2008©

It was at perm([2,3],T). Wechose T=[2,3], but T=[3,2] isanother solution. Using thissolution, we get out next answer[H|T]=[1,3,2].Let’s try one more. We backtrackagain. No more solutions arepossible for perm([2,3],T), sowe backtrack to an earlier choicepoint.At append(V,[H|U],[1,2,3])another solution isV=[1], H = 2, U = [3]

Using this binding, we solveappend(V,U,W) which simplifiesto append([1],[3],W). Thesolution to this must be W=[1,3].We then solve perm(W,T) whichsimplifies to perm([1,3],T). Onesolution to this is T=[1,3]. This

Page 109: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

433CS 538 Spring 2008©

makes our third solution for[H|T] = [2,1,3].You can check out the otherbindings that lead to the lastthree solutions.

434CS 538 Spring 2008©

A Permutation SortNow that we know how togenerate permutations, thedefinition of a permutation sort isalmost trivial.We define an inOrder relationthat characterizes our notion ofwhen a list is properly sorted:inOrder([]).

inOrder([_]).

inOrder([A,B|T]) :- A =< B, inOrder([B|T]).

These definitions state that a nulllist, and a list with only oneelement are always in sortedorder. Longer lists are in order ifthe first two elements are inproper order. (A=<B) checks thisand then the rest of the list,

435CS 538 Spring 2008©

excluding the first element, ischecked.Now our naive permutation sort isonly one line long:naiveSort(L1,L2) :- perm(L1,L2), inOrder(L2).

And the definition works too!| ?-naiveSort([1,2,3],[3,2,1]).no

?- naiveSort([3,2,1],L).

L = [1,2,3] ;no

| ?-naiveSort([7,3,88,2,1,6,77, -23,5],L).L = [-23,1,2,3,5,6,7,77,88]

436CS 538 Spring 2008©

Though this sort works, it ishopelessly inefficient—itrepeatedly “shuffles” the inputuntil it happens to find anordering that is sorted. Theprocess is largely undirected. Wedon’t “aim” toward a correctordering, but just search until weget lucky.

Page 110: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

437CS 538 Spring 2008©

A Bubble SortPerhaps the best known sortingtechnique is the interchange or“bubble” sort. The idea is simple.We examine a list of values,looking for a pair of adjacentvalues that are “out of order.” Ifwe find such a pair, we swap thetwo values (placing them incorrect order). Otherwise, thewhole list must be in sorted orderand we are done.In conventional languages weneed a lot of code to search forout-of-order pairs, and tosystematically reorder them. InProlog, the whole sort may bedefined in a few lines:

438CS 538 Spring 2008©

bubbleSort(L,L) :- inOrder(L).

bubbleSort(L1,L2) :- append(X,[A,B|Y],L1), A > B, append(X,[B,A|Y],T), bubbleSort(T,L2).

The first line says that if L isalready in sorted order, we aredone.The second line is a bit morecomplex. It defines what it meansfor a list L2 to be a sorting for listL1, using our insight that weshould swap out-of-orderneighbors. We first partition listL1 into two lists, X and [A,B|Y].This “exposes” two adjacentvalues in L, A and B. Next weverify that A and B are out-of-order (A>B). Next, inappend(X,[B,A|Y],T), wedetermine that list T is just our

439CS 538 Spring 2008©

input L, with A and B swappedinto B followed by A.Finally, we verify thatbubbleSort(T,L2) holds. Thatis, T may be bubble-sorted intoL2.This approach is rather moredirected than our permutationsort—we look for an out-of-orderpair of values, swap them, andthen sort the “improved” list.Eventually there will be no moreout-of-order pairs, the list will bein sorted order, and we will bedone.

440CS 538 Spring 2008©

Merge SortAnother popular sort in the“merge sort” that we have alreadyseen in Scheme and ML. The ideahere is to first split a list of lengthL into two sublists of length L/2.Each of these two lists isrecursively sorted. Finally, the twosorted sublists are mergedtogether to form a completesorted list.The bubble sort can take timeproportional to n2 to sort nelements (as many as n2/2 swapsmay be needed). The merge sortdoes better—it takes timeproportional to n log2 n to sort nelements (a list of size n can onlybe split in half log2 n times).

Page 111: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

441CS 538 Spring 2008©

We first need Prolog rules on howto split a list into two equalhalves:split([],[],[]).split([A],[A],[]).split([A,B|T],[A|P1],[B|P2]) :- split(T,P1,P2).

The first two lines characterizetrivial splits. The third ruledistributes one of the first twoelements to each of the twosublists, and then recursivelysplits the rest of the list.

442CS 538 Spring 2008©

We also need rules thatcharacterize how to merge twosorted sublists into a completesorted list:

merge([],L,L).merge(L,[],L).merge([A|T1],[B|T2],[A|L2]) :- A =< B, merge(T1,[B|T2],L2).merge([A|T1],[B|T2],[B|L2]) :- A > B, merge([A|T1],T2,L2).

The first 2 lines handle mergingnull lists. The third line handlesthe case where the head of thefirst sublist is ≤ the head of thesecond sublist; the final rulehandles the case where the headof the second sublist is smaller.

443CS 538 Spring 2008©

With the above definitions, amerge sort requires only threelines:mergeSort([],[]).

mergeSort([A],[A]).mergeSort(L1,L2) :- split(L1,P1,P2), mergeSort(P1,S1),mergeSort(P2,S2),merge(S1,S2,L2).

The first two lines handle thetrivial cases of lists of length 0 or1. The last line contains the full“logic” of a merge sort: split theinput list, L into two half-sizedlists P1 and P2. Then merge sortP1 into S1 and P2 into S2. Finally,merge S1 and S2 into a sorted listL2. That’s it!

444CS 538 Spring 2008©

Quick SortThe merge sort partitions its inputlist rather blindly, alternatingvalues between the two lists.What if we partitioned the inputlist based on values rather thanpositions?The quick sort does this. It selectsa “pivot” value (the head of theinput list) and divides the inputinto two sublists based onwhether the values in the list areless than the pivot or greater thanor equal to the pivot. Next thetwo sublists are recursivelysorted. But now, after sorting, nomerge phase is needed. Rather,the two sorted sublists can simplybe appended, since we know allvalues in the first list are less thanall values in the second list.

Page 112: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

445CS 538 Spring 2008©

We need a Prolog relation thatcharacterizes how we will do ourpartitioning. We we definepartition(E,L1,L2,L3) to betrue if L1 can be partitioned intoL2 and L3 using E as the pivotelement. The necessary rules are:

partition(E,[],[],[]).partition(E,[A|T1],[A|T2],L3) :- A<E, partition(E,T1,T2,L3).partition(E,[A|T1],L2,[A|T3]) :- A>=E, partition(E,T1,L2,T3)

The first line defines a trivialpartition of a null list. The secondline handles the case in which thefirst element of the list to bepartitioned is less than the pivot,while the final line handles thecase in which the list head isgreater than or equal to the pivot.

446CS 538 Spring 2008©

With our notion of partitioningdefined, the quicksort itselfrequires only 2 lines:

qsort([],[]).qsort([A|T],L) :-partition(A,T,L1,L2),qsort(L1,S1),qsort(L2,S2),append(S1,[A|S2],L).

The first line defines a trivial sortof an empty list.The second line says to sort a listthat begins with A and ends withlist T, we partition T into sublistsL1 and L2, based on A. Then werecursively quick sort L1 into S1and L2 into S2. Finally we appendS1 to [A|S2](A must be > all values in S1 and Amust be ≤ all values in S2). Theresult is L, a sorting of [A|T].

447CS 538 Spring 2008©

Arithmetic in PrologThe = predicate can be used totest bound variables for equality(actually, identity).If one or both of =’s arguments arefree variables, = forces a bindingor an equality constraint.Thus| ?- 1=2.no

| ?- X=2.

X = 2

| ?- Y=X.

Y = X = _10751

| ?- X=Y, X=joe.

X = Y = joe

448CS 538 Spring 2008©

Arithmetic Terms are SymbolicEvaluation of an arithmetic terminto a numeric value must beforced.That is, 1+2 is an infixrepresentation of the relation+(1,2). This term is not aninteger!Therefore| ?- 1+2=3.no

To force arithmetic evaluation, weuse the infix predicate is.The right-hand side of is must beall ground terms (literals orvariables that are already bound).No free (unbound) variables areallowed.

Page 113: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

449CS 538 Spring 2008©

Hence|?- 2 is 1+1.yes

| ?- X is 3*4.X = 12

| ?- Y is Z+1.! Instantiation error in argument2 of is/2! goal: _10712 is _10715+1

The requirement that the right-hand side of an is relation beground is essentially procedural.It exists to avoid having to invertcomplex equations. Consider,

(0 is (I**N)+(J**N)-K**N)), N>2.

450CS 538 Spring 2008©

Counting in PrologRules that involve counting oftenuse the is predicate to evaluate anumeric value.Consider the relation len(L,N)that is true if the length of list L isN.len([],0).

len([_|T],N) :- len(T,M), N is M+1.

| ?- len([1,2,3],X).

X = 3

| ?- len(Y,2).

Y = [_10903,_10905]

The symbols _10903 and _10905are “internal variables” created asneeded when a particular value isnot forced in a solution.

451CS 538 Spring 2008©

Debugging PrologCare is required in developing andtesting Prolog programs becausethe language is untyped;undeclared predicates or relationsare simply treated as false.Thus in a definition like adj([A,B|_]) :- A=B.

adj([_,B|T]) :- adk([B|T]).

| ?- adj([1,2,2]).no

(Some Prolog systems warn whenan undefined relation isreferenced, but many othersdon’t).

452CS 538 Spring 2008©

Similarly, givenmember(A,[A|_]).

member(A,[_|T]) :- member(A,[T]).

| ?- member(2,[1,2]).

Infinite recursion! (Why?)

If you’re not sure what is goingon, Prolog’s trace feature is veryhandy.The commandtrace.

turns on tracing. (notrace turnstracing off).Hence| ?- trace.yes

[trace]

| ?- member(2,[1,2]).

Page 114: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

453CS 538 Spring 2008©

(1) 0 Call: member(2,[1,2]) ?

(1) 1 Head [1->2]:member(2,[1,2]) ?

(1) 1 Head [2]:member(2,[1,2]) ?

(2) 1 Call: member(2,[[2]]) ?

(2) 2 Head [1->2]:member(2,[[2]]) ?

(2) 2 Head [2]:member(2,[[2]]) ?

(3) 2 Call: member(2,[[]]) ?

(3) 3 Head [1->2]:member(2,[[]]) ?

(3) 3 Head [2]: member(2,[[]])?

(4) 3 Call: member(2,[[]]) ?

(4) 4 Head [1->2]:member(2,[[]]) ?

(4) 4 Head [2]: member(2,[[]])?

(5) 4 Call: member(2,[[]]) ?

454CS 538 Spring 2008©

Termination Issues in PrologSearching infinite domains (likeintegers) can lead to non-termination, with Prolog tryingevery value.Considerodd(1).

odd(N) :- odd(M), N is M+2.

| ?- odd(X).

X = 1 ;

X = 3 ;

X = 5 ;X = 7

455CS 538 Spring 2008©

A query | ?- odd(X), X=2.going into an infinite search,generating each and every oddinteger and finding none is equalto 2!The obvious alternative,odd(2) (which is equivalent toX=2, odd(X)) also does aninfinite, but fruitless search.We’ll soon learn that Prolog doeshave a mechanism to “cut off”fruitless searches.

456CS 538 Spring 2008©

Definition Order can MatterIdeally, the order of definition offacts and rules should not matter.But,in practice definition order canmatter. A good general guidelineis to define facts before rules. Tosee why, consider a very completedatabase of motherOf relationsthat goes back as far asmotherOf(cain,eve).

Now we defineisMortal(X) :- isMortal(Y), motherOf(X,Y).

isMortal(eve).

Page 115: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

457CS 538 Spring 2008©

These definitions state that thefirst woman was mortal, and allindividuals descended from herare also mortal.But when we try as trivial a queryas| ?- isMortal(eve).

we go into an infinite search!Why?Let’s trace what Prolog does whenit sees| ?- isMortal(eve).It matches with the first definitioninvolving isMortal, which isisMortal(X) :- isMortal(Y), motherOf(X,Y).

It sets X=eve and tries to solveisMortal(Y), motherOf(eve,Y).

It will then expand isMortal(Y)into

458CS 538 Spring 2008©

isMortal(Z), motherOf(Y,Z).

An infinite expansion ensues.The solution is simple—place the“base case” fact that terminatesrecursion first.If we useisMortal(eve).

isMortal(X) :- isMortal(Y), motherOf(X,Y).yes

| ?- isMortal(eve).

yes

But now another problem appears!If we ask| ?- isMortal(clarkKent).

we go into another infinite search!Why?The problem is that Clark Kent isfrom the planet Krypton, and

459CS 538 Spring 2008©

hence won’t appear in ourmotherOf database.Let’s trace the query.It doesn’t matchisMortal(eve).We next tryisMortal(clarkKent) :- isMortal(Y), motherOf(clarkKent,Y).

We try Y=eve, but eve isn’t Clark’smother. So we recurse, getting:isMortal(Z), motherOf(Y,Z),motherOf(clarkKent,Y).

But eve isn’t Clark’s grandmothereither! So we keep going furtherback, trying to find a chain ofdescendents that leads from eveto clarkKent. No such chainexists, and there is no limit tohow long a chain Prolog will try.

460CS 538 Spring 2008©

There is a solution though!We simply rewrite our recursivedefinition to be isMortal(X) :- motherOf(X,Y),isMortal(Y).

This is logically the same, butnow we work from the individualX back toward eve, rather thanfrom eve toward X. Since we haveno motherOf relation involvingclarkKent, we immediately stopour search and answer no!

Page 116: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

461CS 538 Spring 2008©

Extra-logical Aspects ofProlog

To make a Prolog program moreefficient, or to represent negativeinformation, Prolog needsfeatures that have a proceduralflavor. These constructs are called“extra-logical” because they gobeyond Prolog’s core of logic-based inference.

462CS 538 Spring 2008©

The CutThe most commonly used extra-logical feature of Prolog is the “cutsymbol,” “!”A ! in a goal, fact or rule “cuts off”backtracking.In particular, once a ! is reached(and automatically matched), wemay not backtrack across it. Therule we’ve selected and thebindings we’ve already selectedare “locked in” or “frozen.”For example, givenx(A) :- y(A,B), z(B), ! , v(B,C).

once the ! is hit we can’tbacktrack to resatisfy y(A,B) orz(B) in some other way. We arelocked into this rule, with thebindings of A and B already inplace.

463CS 538 Spring 2008©

We can backtrack to try varioussolutions to v(B,C).It is sometimes useful to haveseveral !’s in a rule. This allows usto find a partial solution, lock itin, find a further solution, thenlock it in, etc.For example, in a rulea(X) - b(X), !, c(X,Y), ! , d(Y).

we first try to satisfy b(X),perhaps trying several facts orrules that define the b relation.Once we have a solution to b(X),we lock it in, along with thebinding for X.Then we try to satisfy c(X,Y),using the fixed binding for X, butperhaps trying several bindingsfor Y until c(X,Y) is satisfied.We then lock in this match usinganother !.

464CS 538 Spring 2008©

Finally we check if d(Y) can besatisfied with the binding of Yalready selected and locked in.

Page 117: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

465CS 538 Spring 2008©

When are Cuts Needed?A cut can be useful in improvingefficiency, by forcing Prolog toavoid useless or redundantsearches.Consider a query likemember(X,list1),member(X,list2), isPrime(X).

This asks Prolog to find an X thatis in list1 and also in list2 andalso is prime.X will be bound, in sequence, toeach value in list1. We thencheck if X is also in list2, andthen check if X is prime.Assume we find X=8 is in list1and list2. isPrime(8) fails (ofcourse). We backtrack tomember(X,list2) and resatisfy itwith the same value of X.

466CS 538 Spring 2008©

But clearly there is never anypoint in trying to resatisfymember(X,list2). Once weknow a value of X is in list2, wetest it using isPrime(X). If itfails, we want to go right back tomember(X,list1) and get adifferent X.To create a version of memberthat never backtracks once it hasbeen satisfied we can use !.We definemember1(X,[X|_]) :- !.member1(X,[_|Y]) :- member1(X,Y).

Our query is nowmember(X,list1), member1(X,list2), isPrime(X).

(Why isn’t member1 used in bothterms?)

467CS 538 Spring 2008©

Expressing NegativeInformation

Sometimes it is useful to staterules about what can’t be true.This allows us to avoid long andfruitless searches.fail is a goal that always fails. Itcan be used to represent goals orresults that can never be true.Assume we want to optimize ourgrandMotherOf rules by statingthat a male can never be anyone’sgrandmother (and hence acomplete search of all motherOfand fatherOf relations isuseless).A rule to do this isgrandMotherOf(X,GM) :- male(GM), fail.

468CS 538 Spring 2008©

This rule doesn’t do quite whatwe hope it will!Why?The standard approach in Prologis to try other rules if the currentrule fails.Hence we need some way to “cutoff” any further backtracking oncethis negative rule is found to beapplicable.This can be done using grandMotherOf(X,GM) :- male(GM),!, fail.

Page 118: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

469CS 538 Spring 2008©

Other Extra-LogicalOperators

• assert and retract

These operators allow a Prologprogram to add new rules duringexecution and (perhaps) laterremove them. This allowsprograms to learn as theyexecute.•findall

Called as findall(X,goal,List)where X is a variable in goal. Allpossible solutions for X thatsatisfy goal are found and placedin List.For example,findall(X,(append(_,[X|_],[-1,2,-3,4]),(X<0)), L).

L = [-1,-3]

470CS 538 Spring 2008©

• var and nonvar

var(X) tests whether X isunbound (free).nonvar(Y) tests whether Y is

bound (no longer free).These two operators are useful intailoring rules to particularcombinations of bound andunbound variables. For example,grandMotherOf(X,GM) :- male(GM),!, fail.

might backfire if GM is not yetbound. We could set GM to aperson for whom male(GM) istrue, then fail because we don’twant grandmothers who are male!To remedy this problem. we usethe rule only when GM is bound.Our rule becomesgrandMotherOf(X,GM) :- nonvar(GM), male(GM),!, fail.

471CS 538 Spring 2008©

An Example of Extra-LogicalProgramming

Factorial is a very commonexample program. It’s well known,and easy to code in mostlanguages.In Prolog the “obvious” solution is:fact(N,1) :- N =< 1.

fact(N,F) :- N > 1, M is N-1, fact(M,G), F is N*G.

This definition is certainly correct.It mimics the usual recursivesolution.But,in Prolog “inputs” and “outputs”are less distinct than in mostlanguages.In fact, we can envision 4different combinations of inputs

472CS 538 Spring 2008©

and outputs, based on what isfixed (and thus an input) andwhat is free (and hence is to becomputed):1. N and F are both ground (fixed).

We simply must decide if F=N!2. N is ground and F is free. Thisis how fact is usually used. Wemust compute an F such that F=N!3. F is fixed and N is free. This isan uncommon usage. We mustfind an N such that F=N!, ordetermine that no such N ispossible.4. Both N and F are free. Wegenerate, in sequence, pairs of Nand F values such that F=N!

Page 119: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

473CS 538 Spring 2008©

Our solution works forcombinations 1 and 2 (where N isfixed), but not combinations 3and 4. (The problem is that N =<1 and N > 1 can’t be satisfiedwhen N is free).We’ll need to use nonvar and ! toform a solution that works for all4 combinations of inputs.We first handle the case where Nis ground:fact(1,1).fact(N,1) :- nonvar(N), N =< 1, ! .fact(N,F) :- nonvar(N), N > 1, !,M is N-1, fact(M,G), F is N*G, ! .

The first rule handles the basecase of N=1.The second rule handles the caseof N<1.

474CS 538 Spring 2008©

The third rule handles the case ofN >1. The value of F is computedrecursively. The first ! in each ofthese rules forces that rule to bethe only one used for the valuesof N that match. Moreover, thesecond ! in the third rule statesthat after F is computed, furtherbacktracking is useless; there isonly one F value for any given Nvalue.To handle the case where F isbound and N is free, we usefact(N,F) :- nonvar(F), !, fact(M,G), N is M+1, F2 is N*G, F =< F2, !, F=F2.

In this rule we generate N, F2pairs until F2 >= F. Then wecheck if F=F2. If this is so, wehave the N we want. Otherwise,no such N can exist and we fail(and answer no).

475CS 538 Spring 2008©

For the case where both N and Fare free we use:fact(N,F) :- fact(M,G), N is M+1, F is N*G.

This systematically generates N, Fpairs, starting with N=2, F=2 andthen recursively buildingsuccessor values (N=3, F=6, thenN=4, F=24, etc.)

476CS 538 Spring 2008©

Parallelism in PrologOne reason that Prolog is ofinterest to computer scientists isthat its search mechanism lendsitself to parallel evaluation.In fact, it supports two differentkinds of parallelism:• AND Parallelism

• OR Parallelism

Page 120: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

477CS 538 Spring 2008©

And ParallelismWhen we have a goal thatcontains subgoals connected bythe “,” (And) operator, we may beable to utilize “and parallelism.”Rather than solve subgoals insequence, we may be able tosolve them in parallel if bindingscan be properly propagated.Thus ina(X), b(X,Y), c(X,Z), d(Y,Z).

we may be able to first solvea(X), binding X, then solveb(X,Y) and c(X,Z) in parallel,binding Y and Z, then finally solved(Y,Z).

478CS 538 Spring 2008©

An example of this sort of andparallelism ismember(X,list1), member1(X,list2), isPrime(X).

Here we can let member(X,list1)select an X value, then testmember1(X,list2) andisPrime(X) in parallel. If one orthe other fails, we just selectanother X from list1 and retestmember1(X,list2) andisPrime(X) in parallel.

479CS 538 Spring 2008©

OR ParallelismWhen we match a goal we almostalways have a choice of severalrules or facts that may beapplicable. Rather than try themin sequence, we can try severalmatches of different facts or rulesin parallel. This is “or parallelism.”Thus given a(X) :- b(X).

a(Y) :- c(Y).

when we try to solvea(10).

we can simultaneously check bothb(10) and c(10).

480CS 538 Spring 2008©

Recall our definition ofmember(X,L) :- append(P,[X|S],L).

where append is defined asappend([],L,L).

append([X|L1],L2,[X|L3]) :- append(L1,L2,L3).

Assume we have the query| ? member(2,[1,2,3]).

This immediately simplifies toappend(P,[2|S],[1,2,3]).

Now there are two appenddefinitions we can try in parallel:(1) match append(P,[2|S],[1,2,3])with append([],L,L). Thisrequires that [2|S] = [1,2,3],which must fail.(2) matchappend(P,[2|S],[1,2,3]) withappend([X|L1],L2,[X,L3]).

Page 121: UW Computer Sciences User Pagespages.cs.wisc.edu/~fischer/cs538.s08/lectures/LectureAll.up.pdf · CS 538 Spring 2008 1 © CS 538 Introduction to the Theory and Design of Programming

481CS 538 Spring 2008©

This requires that P=[X|L1],[2|S]=L2, [1,2,3]=[X,L3].Simplifying, we require that X=1,P=[1|L1], L3=[2,3].Moreover we must solveappend(L1,L2,L3) whichsimplifies toappend(L1,[2|S],[2,3]).We can match this call to appendin two different ways, so orparallelism can be used again.When we try matchingappend(L1,[2|S],[2,3]) againstappend([],L,L) we get[2|S]=[2,3], which is satisfiableif S is bound to [3]. We thereforesignal back that the query is true.

482CS 538 Spring 2008©

Speculative ParallelismProlog also lends itself nicely tospeculative parallelism. In thisform of parallelism, we “guess” orspeculate that some computationmay be needed in the future andstart it early. This speculativecomputation can often be done inparallel with the main (non-speculative) computation.Recall our example ofmember(X,list1), member1(X,list2), isPrime(X).

After member(X,list1) hasgenerated a preliminary solutionfor X, it is tested (perhaps inparallel) by member1(X,list2)and isPrime(X).But this value of X may berejected by one or both of these

483CS 538 Spring 2008©

tests. If it is, we’ll askmember(X,list1) to find a newbinding for X. If we wish, this nextbinding can be generatedspeculatively, while the currentvalue of X is being tested. In thisway if the current value of X isrejected, we’ll have a new valueready to try (or know that noother binding of X is possible).If the current value of X isaccepted, the extra speculativework we did is ignored. It wasn’tneeded, but was useful insurancein case further X bindings wereneeded.