You are on page 1of 154

Ocaml for Experienced Programmers

Brian Hurt

Ocaml for Experienced Programmers by Brian Hurt Copyright 2004 by Brian Hurt

Dedication
This is for my parents, Jim and Gretchen, for not strangling me as a child (no matter how tempting that might have been at the time). Or later, for that matter.

Table of Contents
Introduction to this Book .................................................................................................... i What is Ocaml?............................................................................................................. i Why Learn Ocaml? ...................................................................................................... i What isnt Ocaml Good For?............................................................................ ii If Ocaml is so Great, Why isnt it More Popular? ......................................... ii A Statement of Purpose............................................................................................. iii Prerequisites................................................................................................................ iv A History of Ocaml .................................................................................................... iv Functional Programming as a Paradigm ................................................................. v Design Patterns .................................................................................................. v A Word on Pronouns ................................................................................................. vi Acknowledgments.............................................................................................................vii Forward ................................................................................................................................. ix 1. Intro to Ocaml....................................................................................................................1 Hello, World!.................................................................................................................1 Running Ocaml Code ..................................................................................................1 The Ocaml Top-Level Interpreter .....................................................................1 ocamlopt: the Ocaml Native Executable Compiler .......................................3 ocamlc and ocamlrun: the Ocaml Virtual Machine .......................................4 Simple Types and Expressions ...................................................................................5 Comments............................................................................................................5 Unit .......................................................................................................................5 Bool .......................................................................................................................5 Char ......................................................................................................................5 String ....................................................................................................................6 Int ..........................................................................................................................6 Float ......................................................................................................................9 Comparison Operations ..................................................................................12 Grouping and Precedence ...............................................................................13 If/Then/Else .....................................................................................................13 Exercises.......................................................................................................................14 2. Variables, Functions, and Tuples .................................................................................15 Variables ......................................................................................................................15 Functions .....................................................................................................................17 Function Types ..................................................................................................18 Not Returning a Value .....................................................................................20 Local Variables ..................................................................................................20 Multiple Local Variables ..................................................................................22 Local Functions .................................................................................................23 Multiple Local Functions.................................................................................23 The Relationship Between let and let/in ......................................................24 The Relationship Between Variables and Functions ...................................24 Functions as Arguments ..................................................................................25 A Complicated Example..................................................................................26 Tuples ...........................................................................................................................26 Tuples and Variables ........................................................................................28 Tuples as Parameters........................................................................................29 Another Extended Example- log2 ..................................................................30 Ignoring Parts of Tuples...................................................................................31 Universal Types.................................................................................................32 Exercises.......................................................................................................................33

3. TODO: Think of a Title .................................................................................................35 Explicit Typing............................................................................................................35 Type Declarations .............................................................................................35 Typing the Arguments of a Function.............................................................35 Type Denitions and Universal Types...........................................................36 Recursive Functions...................................................................................................37 Writting Recursive Functions..........................................................................37 A Example of Recursion: Towers of Hannoi.................................................38 An Example of Recursion: Root Finding.......................................................40 Separate Compilation ................................................................................................41 The Implementation .........................................................................................42 The Interface ......................................................................................................42 Compiling with Separate Modules ................................................................44 Accessing other Modules.................................................................................45 Exercises.......................................................................................................................46 4. Variant Types and Pattern Matching...........................................................................49 Simple Variant Types .................................................................................................49 Redening a Variant Tag .................................................................................50 Simple Pattern Matching...........................................................................................50 Fall-Through Pattern Matching ......................................................................52 Tagged Data ................................................................................................................53 Dening Tagged Types ....................................................................................53 Matching Tagged Types ...................................................................................55 Recursive Types ................................................................................................56 Universal Types and Tagged Types................................................................57 The option Type ................................................................................................58 Discard Variables in Pattern Matching ..........................................................58 Default Patterns ................................................................................................59 Pattern Matching on Other Types ..................................................................60 Exceptions ...................................................................................................................61 Dening Exceptions .........................................................................................61 Raising Exceptions............................................................................................62 Catching Exceptions.........................................................................................62 Assertions ..........................................................................................................64 Invalid Arguments ...........................................................................................65 An Extended Example: a Height Balanced Tree ....................................................66 An Overview of Binary Trees..........................................................................66 An Overview of Big-O Notation ....................................................................66 Balancing Trees..................................................................................................67 The Interface ......................................................................................................68 An Example Use of an Association ................................................................69 The Implementation of Assoc .........................................................................70 Records.........................................................................................................................75 Exercises.......................................................................................................................77 5. Lists and Recursion ........................................................................................................79 Linked Lists.................................................................................................................79 Introducing Ocaml Lists ..................................................................................79 Pattern Matching on Lists................................................................................80 Efciency of List Operations ...........................................................................83 Mixing Pattern Matching.................................................................................84 Circular Lists .....................................................................................................85 Recursion as Looping ................................................................................................86 Example: Walking a List ..................................................................................86 Example: Fibanocci Numbers .........................................................................87 Example: Reversing a List ...............................................................................88 Converting Loops Into Recursion ..................................................................88 Nested Loops.....................................................................................................91 Mutually Recursive Functions........................................................................92 Tail Recursion..............................................................................................................92 vi

Accumulation Parameters ...............................................................................93 Increment a List of Ints ....................................................................................95 Working Around Exceptions ..........................................................................97 Overcomming the Parameter Count Limit ...................................................98 Some Counterexamples ...................................................................................98 Two Examples: Sorting ..............................................................................................99 BubbleSort..........................................................................................................99 QuickSort .........................................................................................................100 Handrolled Stack Management .............................................................................101 A FIFO Queue implementation..............................................................................106 An Introduction to the List Module ......................................................................108 List.hd...............................................................................................................108 List.tl .................................................................................................................108 List.length ........................................................................................................109 List.nth..............................................................................................................109 List.rev..............................................................................................................109 List.rev_append ..............................................................................................110 List.append ......................................................................................................110 List.concat ........................................................................................................110 List.iter..............................................................................................................111 List.map............................................................................................................111 List.rev_map....................................................................................................112 List.fold_left.....................................................................................................112 List.fold_right..................................................................................................112 List.iter2............................................................................................................113 List.map2..........................................................................................................113 List.rev_map2..................................................................................................113 List.fold_left2...................................................................................................114 List.fold_right2................................................................................................114 List.for_all ........................................................................................................114 List.exists..........................................................................................................115 List.for_all2 ......................................................................................................115 List.exists2........................................................................................................116 List.mem...........................................................................................................116 List.memq ........................................................................................................116 List.nd ............................................................................................................117 List.lter ...........................................................................................................117 List.partition ....................................................................................................117 List.sort.............................................................................................................118 Exercises.....................................................................................................................118 6. Higher Order Functions...............................................................................................121 Partial Function Application ..................................................................................121 Returning Functions ................................................................................................123 Usefullness of Partial Function Application ........................................................125 Anonymous Functions ............................................................................................126 The Function Keyword ..................................................................................126 The fun Keyword............................................................................................128 More Pattern Matching............................................................................................129 The as Keyword ..............................................................................................129 The when Keyword ........................................................................................130 The Limitations of Pattern Matching...........................................................131 The Dangers of Default Matches ..................................................................131 Exercises.....................................................................................................................132 A. Garbage Collection Fundamentals...........................................................................133

vii

viii

Introduction to this Book


"Goat butts against the hedge, and his horns become entangled." Egg Shen Hi there. Im assuming youve just picked this book up off the shelf at the bookstore (or maybe just downloaded it from the web) and are idly thumbing through it going, "whats this book all about?" If so, youve hit the right page. The bulk of this book is dedicated to describing Ocaml, which means its chock full of technical details. In this section, however, Im talking on a meta-level, about the book itself and Ocaml in general. So read on! Trust me- both I the author and that store clerk who is nervously eyeing you want you to buy this book. Its my job to explain why you want to as well.

What is Ocaml?
Ocaml is a programming language. It is a production-oriented applications-level language designed for writing industrial-strength large applications, but unlike many languages marketed into the space, it takes full advantage of the advances made in computer science in the last thirty years.

Why Learn Ocaml?


Ocaml allows you to:

Write more code in less time Spend less time debugging Have higher quality code Have the code be more maintainable and reusable Without sacricing performance to do so

Literally- faster, better, cheaper, pick three. Here are some of the features that Ocaml provides as a language to have these advantages:

Garbage collection (no more memory leaks or dangling pointers) Bounds checked arrays (no more buffer overows, the most common cause of security aws in code) Auto-boxing of primitive types (no int vs. Integer, as in Java) Pattern matching (switch statements on steroids) and variational types (C enums on steroids). These make dening complicated data structures easy and simple. First class and higher order functions (dont worry if you dont know what these are- trust me, theyre nice). Polymorphic types aka universal types (again, dont worry about what these are, theyre nice) Multiple paradigms supported, including Object Oriented programming. Programs can be interpreted (like Perl), run in virtual machine (like Java), or compiled to native binaries (like C/C++) as desired. Fully buzzword compliant, like Java. A mathematics-like syntax which is both expressive and concise. A more expressive type system than Java/Pascal, so that the programmer doesnt have to work around or subvert the type system. i

Introduction to this Book


Type inference, so the programmer doesnt have to annotate every variable and function. Static type checking to detect errors at compile time- but without the inconvenience to the programmer normally associated with static type checking.

Thats a pretty big claim. I can already hear you thinking, "Yeah- and it leaps tall buildings in a single bound too, I bet." Heres the thing- computer science, the sort of thing pursued by egg-headed academics in ivory towers, has actually progressed quite a way in the last thirty years. They have come up with a large number of ideas, and some of those ideas really are practical in the day-to-day world of software engineering. Unfortunately, these ideas have languished in minor niche languages while the industry as a whole has focused on perfecting derivatives of Algol-60. Ocaml harnesses these advances and puts them to work in a pragmatic language. I want to make this clear here. Ocaml is a deeply pragmatic language. It wasnt written to prove some pet theory, or to investigate some approach. It was designed and written for real software engineers to write real code in. It may have a PhD, but it still has dirt under its ngernails. It is a pragmatic language in the same way that Perl or C++ are pragmatic languages. It doesnt assume that there is one right way to do everything, but instead allows for the program to do what is needed.

What isnt Ocaml Good For?


There is no such thing as a perfect language, good for all projects and all problems. In my opinion, languages that try to be everything to everybody end up being no good to anyone. Ocaml certainly doesnt try to be everything to everyone. It is an applications language. Its area of expertise is large projects (more than 10K lines of code) worked on by many programmers (three or more) over a long time frame (maintenance is a consideration). But being good at that area means being not so good in other areas. For example, Ocaml makes a so-so scripting language at best. It doesnt have the strongest string handling or process management facilities. In addition, it tends to be heavyweight for throw-away code. So its not really a good replacement for the things Bourne Shell, Perl, TCL/TK, and the like are good for. If all you need is a quick program to grub through log les looking for port scanners, Ocaml is probably a bad choice. Its also not a good glue language for binding programs written in other languages together, like Perl, Lua, and Visual Basic are. If you want to add a scripting language to add to your large application, Ocaml isnt a good choice (its a good choice to write the large application in, however). Another area Ocaml is bad at is banging directly on hardware. Ocaml would be a horrible choice to write an operating system, device driver, or low-level embedded program, or a hard real-time program in. In this arena, C is still king. And Ocaml doesnt have an enterprise applications server like J2EE, to handle distributed computation, load balancing, fail-over, etc. That is, it doesnt have one yet. This is a function of popularity and money, not fundamental capability. So, at least as I am writing this, if you can use J2EE, Java is still a better choice. But notice that this still leaves an awful lot of projects Ocaml is good for. Almost all of the programs you are actually aware of- the ofce suites, browsers, editors, compilers, web servers, games, etc.- can be written in Ocaml. And, in my opinion, theyd be better off if they were.

If Ocaml is so Great, Why isnt it More Popular?


There are a lot of theories as to why this is so. Most of them boil down to "Ocaml isnt already popular because Ocaml isnt already popular"- it doesnt have widespread industry acceptance, or vast crowds of programmers skilled in it, or the backing of ii

Introduction to this Book major industry players, or its own magazine at your local newspaper kiosk. All of these are the result of the language being popular, not the cause. Another theory is that it is insufciently close to what is already popular, and programmers will never learn a language in any major respects different from what they already know. The idea here is that most programmers really dont want to learn anything really new. So they only learn minor variants on what they already know. If the denition of an old dog is one who cant learn new tricks, most programmers are old dogs after only programming a year or two, then. I think (or at least I hope) the problem is simpler than that- that what Ocaml lacks is a Randal Schwartz. Perl was originally created by Larry Wall- but it wasnt until Randal Schwartz came along and wrote a good introduction to the language that it started to gain truly wide acceptance. This opinion is bolstered by the fact that a majority of the books about Ocaml tend to want to introduce advanced concepts (like higher order functions) as early as possible. In a way, this eagerness is understandable- this a feature which makes Ocaml stand out from most other programming languages. But it gives Ocaml a steeper learning curve than is really needed. If Im right, what is needed for Ocaml to start gaining wider acceptance is an easier approach. An analogy I like to make is that learning Ocaml is like climbing a mountain. Most approachs take the "north face approach"- all you have to do is scale that ve thousand foot high sheer cliff face, and traverse the glacier, and its only an easy stroll through a mountain meadow to the summit. Most people never even get to the glacier- they take one look at that mile high cliff and head back into town. What is needed is a "south face approach". This is the long way around- we cover a lot more ground. Like switchbacks on a mountain trail crossing and recrossing the same stream, the "south face" keeps recovering the same topics. But each time we cross the stream were higher up the side of the mountain, and each time we recover a topic we cover it in a deeper way. And while there are some rough spots, some points where the trail gets pretty steep, you never need to scale a cliff wall of learning. And the view from the summit is still spectacular, no matter how you got there. Id like to touch on one other reason many people give as to why Ocaml isnt more popular- it comes from Academia. Many programmers (most of whom have limited exposure to academia themselves) have a knee-jerk reaction to things from academianot just to dismiss them, but to actively deride them. Academics, they claim, "dont do real work", and as such are incapable of understanding real world problems, and implementing real world solutions. This, of course, is just another example of the "Not Invented Here" syndrome. Yes, acadmics come up with a lot of silly ideas- but then, so does industry, as any attendee of trade shows can tell you. If nothing else, developing a cross-platform, industrial quality, optimizing compiler qualies as real work. And it doesnt matter if its a college or a company that writes your check.

A Statement of Purpose
This book is aimed at the knowledgeable programmer interested in learning Ocaml. Its examples and structures are geared towards the pragmatic, not the theoreticalideas and concepts are introduced only when needed, and when they can be shown as useful. This book is not aimed at those wishing to learn to program- a certain level of knowledge of programming in the general sense is assumed. As much as I like Ocaml and wish more of the programming industry and free software/open source community used it, I dont think it makes a good language for teaching introductory programming. You dont learn to drive in an eighteen-wheeled truck, or learn to y in a large passenger jet, despite the fact that is what professional drivers and pilots often drive or y. You learn to drive in an economy sedan with an automatic transmission, and iii

Introduction to this Book learn to y in a Piper cub. Only after you have mastered the basics do you move on. Like a 747 or semi-truck, Ocaml has a lot of complexities which help the experienced programmer, but simply serve to confuse the beginner. This book is not intended to be textbook. I do provide some exercises, for those readers who wish to investigate further. But I provide answers to the exercises in the back of the book. The audience of this book is intended to be programmers learning on their own. This is not a reference manual. I dont document every corner of the language and library. Ocaml already has a reference manual, and a nice one at that, for free download from the web. This is not a theory book of programming language design. The structure and examples are chosen with an eye to towards usefulness and pragmatism over theoretical interest.

Prerequisites
I, the author, assume that you, the reader, already know how to program one or more currently popular languages- C, C++, Java, Perl, Python, or Ruby. I assume you have some knowledge of both the Procedural (C, Pascal, Fortran, etc.) and Object Oriented (C++, Java, Python, etc.) paradigms, although extensive knowledge of both is not required. Knowledge of a functional programming language- Lisp, Scheme, Haskell, Meta Language, etc.- is not required. In fact, I assume you dont really know functional programming at all (although it certainly isnt a disadvantage). If you do know a functional programming language, the structure of the book may be suboptimal. I hold off introducing certain concepts which are unfamiliar to programmers who dont know functional programming longer than other textbooks do. Yes, Ocaml has the features youd expect from a functional programming language (except no continuations, sorry). Ill introduce them when I can not only demonstrate the feature, but also why and how theyre useful.

A History of Ocaml
A long time ago, in a computer room far, far away... Back in the 1940s, the mathematician Alonzo Church (possibly inspired by ideas in Gdels Incompleteness Theorem) proposed the theory of Lambda Calculus as an alternative basis for computational theory to Turings Machine. Now, Lambda Calculus has about as much to do with learning Ocaml as Turing Machines have to do with learning Java (i.e. nothing at all), but Lambda Calculus and Fortran- which originally stood for FORmula TRANslator, and was intended to allow scientists and engineers to simply write formulas, and not code- combined in the mind of John McCarthy to form Lisp, in about 1958. Lisp was originally intended to be just a theoretical exercise, and McCarthy was as surprised as anyone when one of his grad students, Steve Russell, actually implemented the language. The language escaped into the wild, and real programmers started using this theoretical language for real problems. To this day, a large, active, thriving community of Lisp developers exists worldwide. But the invention of Lisp raised an interesting question. The core idea of Lisp was that you wrote functions- abstract equations with no side effects, rather like mathematics. It turned out that this style of code (which got termed "functional", as opposed to what Fortran, Pascal, C, Java, etc. all were, which was "imperative") was incredibly easy to reason about- so easy you could write programs to reason about the code for you. Could you actually prove the code was correct, or at least that it didnt contain any instances of this wide class of bugs? What were the limits? iv

Introduction to this Book In 1971, Robin Milner started the LCF project- the Logic of Computable Functions. This was an investigation into automatic theorem proving about code. The theoretical language LCF worked with was developed by Milner in 1973, and dubbed "Meta-Language" aka ML. ML had the same easy-to-analyze functional structure of Lisp, but with a more natural, mathematics-like notation heavily inuenced by Pascal. Once again, this theoretical language escaped into the wild, and once again real programmers proceeded to write real code in ML. Its descendents are still in active use and development, under the name Standard ML or SML. Are you noticing a trend here? One of the places where ML was being used for "real" work was in the Formel Project in the Institut National de Recherche en Informatique et en Automatique (National Institute for Research into Information and Automation- or INRIA) in France. Now, INRIA was doing computer science research into language design, but they needed a pragmatic, workaday language to actually build stuff in. Up until about 1987, the Formel project had been using ML, but ML had a number of shortcomings and mistakes. You never get things perfect the rst time around, and ML was no exception. So Guy Cousineau wrote a cleaned-up version of ML, xing most of the obvious aws, and built it on top of the Categorical Abstract Machine (basically Lisp), or CAM. As a pun, CAM-ML got shortened to CAML when it was released in 1987. In 1991, Xavier Leroy and Damien Doligez wrote Caml-light, CAML compiling directly to and running on a virtual machine, instead of compiling to and running on the CAM. This was faster and turned out to be more portable. In 1994, development of CAML ofcially passed from the Formel project to Projet Cristal. Xavier Leroy, Jrme Vouillon, and Didier Rmy added Objects and the ability to compile to native code in 1996. The rest, as they say, is history.

Functional Programming as a Paradigm


Ocaml is, at its core, a functional programming language. While it has both object oriented and imperative extensions, these are extensions onto the language. Functional programming is a paradigm of programming- right up there with Procedural (C/Pascal), Object Oriented (C++/Java), Logic (Prolog), or others. Now, the denition of a paradigm is murky at best, but in a general sense languages in the same paradigm want you to solve the same problem in more or less the same way. As such they tend to have the same features and concepts- objects, classes, inheritance, and overloading for Object Oriented, for example. Languages in the same paradigm share the same design patterns. Learning a language in a new paradigm is signicantly harder than learning a new language in a paradigm you already know. Moving from C++ to Java is easy (relatively) as both languages share a common paradigm. You dont have to learn new ways of solving (or thinking about) problems, you just have to learn the new way to express the old solutions. Anyone who learned to program in a procedural language (C, Pascal, PL/1, etc.) and then had to learn Object Oriented is familiar with the learning curve of a new paradigm. But moving between languages in the same paradigm isnt as useful. Its hard to make big changes in efciency- either for programmers or for programs- between languages within the same paradigm, because in some deep sense they arent that different. Big gains come with big changes- that was the core of the Object Oriented revolution that swept the programming industry in the late 1980s and early 1990s. So thats the warning of this section. You are learning a new paradigm of programming, and thus the going will harder than you might expect. But the warning comes with a message of hope. Functional programming doesnt introduce very many fundamentally new ideas. Mostly its about viewing old ideas, like functions, variables, types, and so on, in a new light.

Introduction to this Book

Design Patterns
One of the most powerful tools I have for introducing the new paradigm of functional programming is design patterns. For those of you who arent "design pattern infected", the concept comes from Christopher Alexander. He was an architect who wrote a series of books on how to design communities that worked, that were livable. His basic premise was that the same basic ideas kept showing up over and over again- the implementations were different, but the core idea was the same. The Gang of Four (Erich Gamma, Richard Helm, Ralph Jophnson, and John Vlissides) took this idea and applied it to Object Oriented programming. Once named, the design patterns themselves became a shorthand notation to discuss design- youve probably heard people talking about Iterators or Factory Methods or Decorators- these are all design patterns. For those of you who already knew about design patterns, they arent something unique to object oriented programming- every programming paradigm has them. Even functional programming. Or, I should say, especially functional programming. Design patterns are my secret weapon. They are the rst ideas that experienced functional programmers reach for, when thinking about how to solve problems. But rather than deriving them from rst principles, I can simply introduce them at out"Heres a design pattern- this works." This also makes your life easier. You dont need to worry about learning to think in a functional manner right away. When faced with a problem, ip through the index of design patterns I introduce and think to yourself "hmm- would this pattern be usefull? No. Ok, how about this pattern? Maybe- come back to it. How about this pattern?" After a while, youll ip through the list mentally faster than you could do it physically, and pick out those patterns most relevent to the problem at hand. You may even nd a "design esthetic" emerging. At which point, even if you dont know it, youll be thinking like an experienced functional programer.

A Word on Pronouns
One of the inevitable decisions an author faces in this day and age is, curiously enough, pronouns. English has the bad habit of using masculine pronouns for genderless cases- he is both third person male and third person genderless. Unfortunately, this creates a bias- especially in the realm of computer programming which is (in my opinion) already too male-dominated. So, to sidestep the entire problem, this book is written entirely in the rst person. I the writer will simply directly address you the reader. In addition to sidestepping the whole political correctness debate, I think it gives the book a chatty, friendly feel, dont you?

vi

Acknowledgments
This is my rst book, so of course the Acknowledgments section is going to be long, while I thank the academy and my goldsh for always believing in me. But, unlike the Oscars, you dont actually have to sit through the whole thing, unless youre scanning it for your name. If your name isnt here, it means that either Ive forgotten it (I do that), you didnt contribute signicant amounts to this book, or I hate you and wish youd go away. First, thanks to all the people who have made Ocaml possible- and Ocamls predecessors, Lisp and ML. There names are too numerous to list here, but without their work and ideas this book would not exist, and the state of the art of programming would be much less advanced. Thanks to the my friends- Robert "Chia" Fischer, Bret Indrelee, Tom Lunde, Melissa Reid, Kris Spiesz, Dave Stagner, Dan Taylor - for being test subjects/guinea pigs/proofreaders, and for putting up with my incessant raving about Ocaml, and ranting about every other programming language in existence. TODO: nish thanking people.

vii

Acknowledgments

viii

Forward
TODO: you should write the Forward last.

ix

Forward

Chapter 1. Intro to Ocaml


I hate you! Connor MacLeod Good. That is a perfect way to start. Ramirez

Hello, World!
Hello World! Its a classic. You cant start a book on a program without rst writing a program that outputs that classic phrase (with or without a comma)- or the related "Hello, Sailor!" The reason for this is obvious- it gives you immediate feedback that your program is compiling and running just ne. It also introduces some minimal set of I/O routines, which are useful for further investigations of the system. Debugging by print statements is a time-honored tradition among programmers. So thats the goal of this part of the chapter- to write a hello world program and get it to run. But rst, the code for Hello World:
print_string "Hello, world!\n";;

The rst thing to note is that print_string is a function. In Ocaml, to call a function with one argument, you just name the function, and then the argument. So what in C or Java would be f(x) in Ocaml would be f x. The function print_string takes a string and writes it to the standard output, much like Cs puts() (but without appending a trailing new line) or Javas System.out.print(). The second part is the string to print. Ocamls strings are very similar to C or Java strings- delineated by double quotes ("), and \n is the new line character. So the rst line of response from the computer is just the output of the program- in this case "Hello, world!" followed by a new line. Lastly comes the double semicolons- and no, thats not a typo. I meant two semicolons. Double semicolons signal the end of a global statement. A single semicolon has a different meaning Ill discuss in chapter 8. Two things to note here. First is that Ocaml is a free-form language, like C or Java. The presence or absence of white space (spaces, tabs, new lines, etc.) is only important in two ways: rst, it is important within strings or character constants, and second, it serves to separate tokens. Ocaml follows the longest-match rule like just about every other language in existence, so foobar is a single token. If you meant it to be two tokens, foo and bar, put a space in between them. The second thing is that Ocaml is a case-sensitive language. The function name print_string is different than the function name print_String. Again, this is like most every other language in existence.

Running Ocaml Code


Now that we have some Ocaml code, we need to get it to run. Which isnt as trivial in Ocaml as it is in most languages, as Ocaml gives not one, but three different ways of compiling and running the same code. This is a pattern with Ocaml. Like Perl, "there is more than one way to do it". All three ways will be introduced, demonstrated, and their various strengths and weaknesses discussed. Note that the goal here is not so much to tell you everything you need to know about the Ocaml tools, its to give you enough of a grounding that you can compile and modify the examples in this part of the book. These tools have many more capabilities than are discussed here. In addition, there are tools not even mentioned here to help in the compilation of large projects. 1

Chapter 1. Intro to Ocaml

The Ocaml Top-Level Interpreter


The rst way of running Ocaml programs that we are going to introduce is called the top-level interpreter. This is a program which reads in Ocaml statements one at a time and interprets them- much like many interpreted languages such as Perl and Lisp. This means of developing code provides a great deal of development exibility, as you can just type code in, run it, and see what it does. Unfortunately, this exibility comes at a performance cost. In addition to having to parse and "compile" every statement as its encountered, the top-level interpreter does little in the way of optimization, and executes the code in a non-JIT non-hot-spot-optimizing virtual machine. As such the top-level interpreter is the slowest (but most exible) of the three ways to execute Ocaml code. Running the Ocaml top-level is trivial: just type in the command line ocaml into a terminal window, and the system prints out the version of Ocaml being run and a prompt for more input, like:
$ ocaml Objective Caml version 3.06 #

To exit the Ocaml top-level, enter the command #quit;;, like:


$ ocaml Objective Caml version 3.06 # #quit;; $

and youre back at the command line. You can also enter the end of le character at the beginning of a line (control-D for Unix, control-Z for Windows) to exit the top-level interpreter. The top-level interpreter reads lines until it encounters one that ends with a double semicolon, at which point it parses, compiles, and runs the code just entered (which is why #quit;; needs to end with a double semicolon). To run the Hello World program written above, we run the Ocaml top-level interpreter and type (or cut and paste) the program at the prompt:
# print_string "Hello, world!\n";; Hello, world! - : unit = () #

The rst line after entering our program is the output, written to the standard output, of the program itself- in this case, "Hello, World!" followed by a new line. The second line of output is added by the top-level interpreter. The rst part of the line before the rst colon, is a -. This means that the statement computed a value (as opposed to dening a variable or a function, which well show in chapter 2). After the colon comes the type of the expression. The type of the expression is the return value from the function print_string- in this case, it is type unit. The Ocaml type unit is analogous to the C/Java type void. A function which returns no value (also known as a procedure) in Ocaml would return type unit. Then, after the equals sign, the top-level prints the value the expression computed. The type unit can only have one value- the unit value or (). A slightly different example makes this more plain:
# 3 + 5;; - : int = 8 #

Here again, double semicolons mark the end of an expression. This code computes the value of 3 + 5. Since it doesnt call print_string (or any other output routine) 2

Chapter 1. Intro to Ocaml there is no line of output. But the top-level interpreter still appends its line. Again, we just compute a value and dont dene a variable or function. But this time the type of the equation is int and not unit. The value, 8, is also different. Note that this example also demonstrates one of the important features of Ocamltype inference. You dont need to tell it that the expression 3 + 5 is of type int, it gures it out by itself (Ocaml infers the type of all expressions, variables, and functions- thus "Type Inference"). Ocaml is a statically typed language, in that all types are checked at compile time and not run time. But type inference relieves the programmer of the burden of actually specifying most types. I use the top-level interpreter a lot during code development. Its useful to have it running in another window so that I can cut and paste code fragments into it. This allows me to interactively develop code, getting immediate feedback on the behavior of my programs. I also use the Ocaml top-level interpreter as a calculator, and to dash off quickie programs to do one-off jobs.

ocamlopt: the Ocaml Native Executable Compiler


The next way Ocaml provides to execute Ocaml code is native compilation with ocamlopt. This works like C/C++ compilers do- running ocamlopt on Ocaml programs produces native binaries which can just be run on the target machine (or partially linked object les that can be linked together to form native binaries). There are two huge advantages to ocamlopt. The rst is performance- when you hear me saying that Ocaml can deliver performance competitive with C or C++, its ocamlopt Im talking about. Once Ive gotten my code (mostly) working in the top-level interpreter, I generally compile with ocamlopt for production. The other advantage of ocamlopt is that binaries compiled with it can be run on compatible systems without Ocaml needing to be installed. Ocaml binaries created with ocamlopt are self-contained the same way C/C++ binaries are. This (plus the performance advantages) makes them what you want to ship to a customer. There are, however, several disadvantages to binaries created with ocamlopt. The rst is the lack of exibility- ocamlopt requires the classic C/C++ edit/compile/run cycle. The second problem is that (as of this writing) the binaries produced by ocamlopt cannot load and use code dynamically (via shared or dynamically linked libraries). The third problem is lack of portability- Ocaml code compiled for, say, an x86 computer running Linux cannot run on a Power-PC computer running MacOS. Or even on an x86 computer running Windows. You get C/C++s performance and independence, but you also get C/C++s debugging problems and lack of executable portability. Ocaml programs to be compiled either with ocamlopt or with the virtual machine compiler ocamlc (see below) should have the .ml le extension- this is the equivalent of Cs .c extension. A .ml le is a series of Ocaml statements, which are executed one at a time in order in the compiled program. There is no main function as such, unlike in Java or C/C++. So, to compile our hello world program with ocamlopt, wed create a le, call it helloworld.ml, which would only contain one line (which should start to look familiar):
print_string "Hello, world!\n";;

Once we have this le, we can compile it with ocamlopt:


$ ocamlopt -o helloworld helloworld.ml $ ./helloworld Hello, World! $

Chapter 1. Intro to Ocaml The program ocamlopt takes many of the same command line options your generic C compiler does, and they mean the same thing. Here, the -o helloworld option means to name the resulting binary "helloworld" instead of "a.out". In addition to the binary executable le helloworld, ocamlopt also produces a .o, .cmx, and .cmi les (with base names the same as the base name of the .ml le- so compiling foo.ml would produce foo.o, foo.cmx, and foo.cmi les). These are used for compiling applications with more than one le, and are discussed later. One last comment about les compiled with ocamlopt. The top-level interpreter does not parse nor execute a statement until it sees the double semicolons- so the double semicolons are less optional. They are how you tell the top-level "go do this now". With ocamlopt, the double semicolons are truly optional, and can generally be removed without harm. But I like having them, even in les Im compiling with ocamlopt, for two reasons. First, it makes a clear syntactic statement that the current function is complete. Second, I have had an occasional syntax error that was made easier to nail down with the injection of double semicolons. But these are stylistic opinions I hold, which not everyone agrees with. The examples in this book all use the double semicolons.

ocamlc and ocamlrun: the Ocaml Virtual Machine


In between the performance of the native executables produced by ocamlopt and the exibility of the top-level interpreter is the Ocaml virtual machine. Ocaml code is compiled (with ocamlc) to a virtual machine object le, which can then be run by an interpreter (ocamlrun). This is similar to the way that Java or C# work. Compiling code to the virtual machine allows for the same object code to be run on multiple different architectures, but it avoids the cost of parsing and compiling the program at runtime that the top-level interpreter has. Also, it allows for the loading and executing of Ocaml code dynamically. However, it forces the same edit/compile/run cycle that ocamlopt does, and since (as of this writing) the interpreted code is neither just-in-time converted to native executable nor hot-spot optimized, its performance is not as good as native code is. Why Not Use the Java Virtual Machine? The question of why Ocaml doesnt use Javas virtual machine comes up with a fair regularity. The answer comes down to two parts. First, Ocamls virtual machine predates Java by a fair bit- the Java virtual machine didnt exist. Second, attempts to port Ocaml to the JVM have consistently run into problems with the speed of object allocation. Ocaml programs allocate a lot (trust me on this), and the objects tend to become garbage just as fast. Most Java VMs still have a large penalty for allocating objects, and this translates into disappointing Ocaml performance. The Ocaml runtime is tuned to support this fast allocation behavior efciently. As to why not use the C# Common Language Runtime, this product may already exist. Ive heard numerous reports mentioning a language from Microsoft called F#- which is described as being basically Ocaml.NET. I know very little about this product other than what Ive described- if youre interested, you are encouraged to contact Microsoft. Like with ocamlopt, Ocaml code to be executed should be placed in a .ml le. This le is then compiled with ocamlc, producing a virtual machine executable. This is then fed to ocamlrun to execute:
$ ocamlc -o helloworld helloworld.ml $ ocamlrun helloworld Hello, World!

Chapter 1. Intro to Ocaml


$

Simple Types and Expressions


Now that we have Ocaml code running (in one of three different ways), we turn our attention to actually doing something useful. Starting with simple types and expressions. The capabilities introduced here are little more powerful than a cheap hand calculator, but it gives us a basis to build real functionality on later. You know programming, allowing me to move at speed through this introduction and get on to the more interesting topics. But rest assured, you will see this stuff again.

Comments
Comments in Ocaml start with (* and end with *). Comments may be nested. There is no "comment to the end of the line" marker like // in C++ or # in Perl or Bourne Shell. Java-style documentation comments are supplied via Ocamldoc, described later.

Unit
Unit, as already introduced, is Ocamls version of Cs or Javas void. Its the "no value" value. Its what functions return when they dont return anything, and the type of a functions argument when it doesnt take an argument. The unit type can only hold one value, the unit value, or (). Note that there can be space between the parentheses, () is the same as ( ). But the common style is to not have a space.

Bool
Expressions of type bool have two values- true and false. You have your standard complement of boolean operations- not for boolean negation (thats the keyword not, spelled out, so that not true is false), boolean and by either the && operator or the keyword and, and boolean or by either the || operator or the keyword or. Boolean operations always short circuit- so that if the left hand side of a boolean and is false, or the left hand side of a boolean or is true, the right hand side is not even evaluated.

Char
Ocaml characters are 8-bit ASCII characters following the ISO 8859-1 standard (sorry, no Unicode). Character constants are either single characters or escape sequences, wrapped in apostrophes ('), like 'a'. Table 1-1. Character Escape Sequences Sequence \\ \" \' \n Character denoted backslash (\) double quote (") single quote/apostrophe (') line feed (LF) (new line) ASCII 92 34 39 10 5

Chapter 1. Intro to Ocaml Sequence \r \t \b \ddd \xhh Character denoted carriage return (CR) horizontal tab (TAB) backspace (BS) the character with the ASCII code ddd in decimal the character with the ASCII code hh in hexadecimal ASCII 13 9 8 ddd 0xhh

Note: Decimal constants in escape sequences must be exactly three digits long, and hexadecimal constants must be exactly two digits long. So '\013', '\x0d', '\008', '\000', and '\x00' are all valid, while '\65', '\8', '\xd', '\0', '\00', and '\x0' are all invalid. Hexadecimal constants can be either lower case or upper case, so '\x0d' and '\x0D' have the same value.

There are only two operations of interest on characters- char_of_int, which converts an integer to the character with that ASCII code, and int_of_char, which converts a character to the integer value of its ASCII code. These act like unary operators (like all unary functions do).
chars Are Not Short ints!: Some languages (C, C++) use chars as short integer values. Ocaml is not one of them. You cannot do arithmetic on chars without rst converting them to ints, doing the arithmetic on the ints, and converting the nal result back to a char.

The function print_char writes the character to stdout:


# print_char a;; a- : unit = () #

Note that print_char does not add an end-of-line character- so the a at the beginning of the response line is the output of the code, the rest of the line is the normal top-level type information.

String
As has already been introduced, Ocaml has a string type. Strings are delineated with double quotes- ". The same escape sequences for character constants can be used in strings. Note that unlike C, Ocaml strings are not null-terminated. So its perfectly OK to have a null character in the middle of a string. The ^ operator concatenates strings. As already mentioned, the function print_string can be used to print a string to stdout. More complicated I/O functions are going to wait for a bit, so the print_string and string concatenation operator are going to get a lot of use. So, an example of these in action is in order:
# "foo" ^ "bar";; - : string = "foobar" # print_string ("Goodbye, " ^ "cruel " ^ "world!" ^ "\n");; Goodbye, cruel world! - : unit = () #

Chapter 1. Intro to Ocaml

Int
Unlike many other languages, Ocaml only has one type of integer: int. These are 31bit (on 32-bit platforms) or 63-bit (on 64-bit platforms) signed integers. Integer constants are a little different for Ocaml than is normal for C, Java, etc. Decimal constants can just be given straight, like 73. Hexadecimal constants start with 0x or 0X as in C and Java, so 0x73 is decimal 115. Octal constants start with 0o or 0O (thats a number zero and the letter O). So octal 73 (decimal 59) would be 0o73. Decimal constants can start with the number zero- so the constant 073 is decimal 73, not decimal 59 (73 octal). The usual operations are available for ints: Table 1-2. Integer Operations Op
+ * / mod abs land lor lxor lnot lsl lsr asr pred succ ~-

Meaning addition subtraction (also unary negation) multiplication division modulo absolute value bitwise logical and bitwise logical or bitwise logical xor bitwise logical negation logical shift left logical shift right (unsigned shift) arithmetic shift right (signed shift) predecessor (pred x is x - 1) successor (succ x is x + 1) alternate unary negation

Note: The operators abs, lnot, pred, and succ are actually just functions, but they work like unary operators. But mod, land, lor, lxor, lsl, lsr, and asr are inx operators- you write 1 lsl 3, not lsl 1 3- this is Ocaml, not Lisp.

The values min_int and max_int evaluate (unsurprisingly) to the smallest and largest possible integers on the system (similar to C/C++s INTMIN and INTMAX in limits.h). You can use the top-level interpreter to inquire about the values for your system. Heres the output from my system (32-bit Intel):
# # #

min_int;;
: int = -1073741824

max_int;;
: int = 1073741823

The function print_int can be used to print an integer, in decimal, to stdout.


# print_int min_int;;

Chapter 1. Intro to Ocaml


-1073741824- : unit = () #

Again, no new line is added. In addition, Ocaml provides the functions string_of_int, which converts an integer to its decimal string representation, and int_of_string which converts a string into an int (rather like Cs atoi function). This is going to be used a lot to perform more complicated I/O in conjunction with print_string and string concatenation:
# print_string ("max_int div 2 = " ^ (string_of_int (max_int / 2)) ^ "!n");; max_int div 2 = 536870911! - : unit = () #

Why Only One Type of int? Many programming languages (C, C++, Java) provide a range of different integer types. The normal reason given is that so the programmer can use the most efcient one. There are three problems with this: the rst is that Ocaml stores all variables in full machine words (32 bit words on 32 bit platforms, and 64 bit words on 64 bit platforms). If the data size is smaller than a word (like bools and chars are), Ocaml still uses a full word to store them in. This isnt that inefcient- most of the time C and C++ use full words to store smaller data types in due to padding and alignment constraints. This kind of eliminates the efciency argument. And secondly, Ocaml does provide multiple different types of integersspecically with the NativeInt (full word integers of the native machine size), Int32 (32 bit ints on all platforms) and Int64 (64 bit ints on all platforms). Im just not dealing with them at this point.

Why Are ints One Bit Short? One of the strangest things about Ocaml is that ints are one bit shorter than you might think- 31 bits on a 32 bit system, for example. The reason for this is that the garbage collector needs some way to tell integers (which it can ignore) from pointers (which it cant). In Ocaml, all pointers are to word-aligned locations, so the low 2 or 3 bits will always be 0 for pointers. So Ocaml steals the low bit from integers and sets it to 1. Now, the garbage collector can easily differentiate integers from pointers by looking at the low bit of the word. And ints are one bit short. This does mean Ocaml has to issue an occasional "extra" instruction to deal with the tag bit- an occassional increment or decrement, or a shift left or right by one place. But this is less of a problem than it might at rst appear. First of all, these instructions are among the fastest instructions there are. Second, Ocaml is very good at omitting them if they arent needed (for example, integers being manipulated in registers often dont have tag bits). And third, other effects of the programming language tend to drown out the minor effect this instructions have- garbage collection, for example, can speed up some programs and slow down others, and will in general have a much larger effect (positive or negative). There are ways to move the tag bit out of the int, and store it elsewhere. But investigation by the designers of Ocaml has shown that in most cases these methods slow down the garbage collector, and the whole program, by more the omitting the occassional tag bit handling instruction would speed the program up. So ints are likely to stay one bit short. 8

Chapter 1. Intro to Ocaml

Float
Ocaml provides a single oating-point type- oat. This type is dened to be an IEEE754 double-precision oating-point number (the same as Cs or Javas double type). To differentiate oating-point constants from integer constants, Ocaml requires oating-point constants have either a decimal point or an exponential component. Other than that, Ocamls format for oating-point numbers is same as the standard C/Java format. So 3 is an integer, while 3., 3.14, 3E2, and -3.14e2 are valid oating-point numbers. The keywords infinity, neg_infinity, and nan stand for the obvious IEEE 754 special values (1.0 /. 0.0, -1.0 /. 0.0, and 0.0 /. 0.0, respectively). Ocaml provides the standard complement of operators on oating-point numbers.
Note: The operators Ocaml provides for oating-point are not the same operators Ocaml provides for integer operations- there is no operator overloading in Ocaml, even to the extent that C does operator overloading (where + means both integer and oating-point addition). So the + operator means only integer addition, the +. operator (note the .) is used for oating-point addition. Once you get used to this oddity, you will nd it not so annoying as you might rst suppose. The reason for this is a combination of type inference and speed. The type inference algorithms have problems with overloaded operators. Consider what would happen if, for example, + could mean either oating-point or integer addition- Ocaml could not tell what the types of x and y where in the code x + y. It could, I suppose, emit code that could handle either. But this would require a branch on every basic operation (is it an integer or a oating-point?), causing a very large performance hit to be taken by Ocaml programs for very little gain. Different operators for oating-point and integer operations is the price we pay for C-like performance without needing to explicitly type everything.

Table 1-3. Floating Point Operations Op


+. -. *. /. ** ~-.

Meaning addition subtraction (also unary negation) multiplication division exponentiation alternate unary negation

In addition, Ocaml provides a number of useful functions for operating on oatingpoint values. Table 1-4. Floating Point Functions Function
sqrt exp log log10 cos

Meaning square root exponentiation (ex) natural log (log base e) log base 10 cosine 9

Chapter 1. Intro to Ocaml Function


sin tan acos asin atan atan2 cosh sinh tanh ceil floor abs_float

Meaning sine tangent arccosine arcsine arctangent arctangent2- takes two parameters hyperbolic cosine hyperbolic sine hyperbolic tangent ceiling- least integer greater than or equal to the argument oor- greatest integer less than or equal to the argument absolute value

In addition, Ocaml provides the functions int_of_float and float_of_int to convert between oating-point numbers and integers. The trigonometric functions use radians and not degrees. Two things to note about the ceil and floor functionsrst, they have a return type of oat- the only way to convert a oat to an integer is to use int_of_float. Second, ceil returns the least integer greater than or equal to the argument- so ceil (-2.5) returns -2.0, not -3.0. As you could probably guess, Ocaml provides the print_float function to print oats to stdout (again, without appending a new line):
# print_float 3.14;; 3.14- : unit = () #

Likewise, Ocaml also provides string_of_float and float_of_string functions, which act like their integer brethren:
# print_string ("e = " ^ (string_of_float (exp 1.)) ^ "\n");; e = 2.71828182846 - : unit = () #

10

Chapter 1. Intro to Ocaml

Precision Loss With Floating Point Numbers All languages that use the hardware-accelerated IEEE 754 oating-point numbers- including C, C++, Java, and Ocaml- suffer a similar problem. These are not perfect real numbers, instead they are xed-precision approximations. This leads to a loss of precision. To see why this is a problem, assume for a moment that oating-point numbers are stored as 4-digit base-10 numbers. Now, the rational number 1. /. 3. could not be stored precisely. The closest we could come would be the number 0.3333. If we then multiplied this number by 3., we would get not the expected value of 1. but instead the surprising value of 0.9999. We have lost some precision- our answer is not as accurate as we might like it to be. IEEE oats are base-2 instead of base-10, meaning that in addition to 1. /. 3. not being precisely represented, numbers like 1. /. 5. and 1. /. 10. also are not precisely represented. But instead of a mere 4 decimal digits of precision, they provide 52 binary digits of precision (about 15 decimal digits of precision). This reduces the problem, but doesnt eliminate it. Now, the Ocaml output routines know this, and tend to want to print 1.0 instead of 0.9999. So if you asked what the value of (1. /. 10.) *. 10. was, you would still get the expected answer:
# (1. /. 10.) *. 10.;; - : float = 1. #

The error is still there, however. And over several operations can accumulate to be large enough that Ocaml doesnt round the answer correctly. For example:
# (1. /. 10.) +. (1. /. 10.) +. (1. /. 10.) +.

(1. /. 10.) +. (1. /. 10.) +. (1. /. 10.) +. (1. /. 10.) +. (1. /. 10.) +. (1. /. 10.) +. (1. /. 10.);;
- : float = 0.999999999999999889 #

You can get a PhD and spend an entire career dealing with the consequences of this "little problem". A full treatment of this subject is therefore beyond the scope of this book. My advice- for simple stuff, assume that oating-point numbers are approximations. For more complicated stuff, I recommend buying a good book on numerical analysis, using routines in high-quality libraries such as LAPACK, and hiring a good numerical analyst.

11

Chapter 1. Intro to Ocaml

Boxing In Ocaml, all types are either boxed or unboxed. With boxed types, you only have a pointer or a reference to the value, not the value itself. This is similar to the way call-by-reference works. Unboxed types are smaller than pointers, and therefore are kept inline. In Java, the lower-case types int, oat, etc. are unboxed, while the upper-case classes Integer, Float, etc. are boxed. But you dont need the dual representations, Ocaml can treat the unboxed types as if they were boxed. In terms of C/C++, everything is implemented as if they were void pointers- unboxed types are the old trick of holding ints in void pointers with typecasting. Boxed types are mainly data structures of some sort- lists, arrays, strings, objects, etc. Currently, int, char, and bool are the only unboxed types. Floats are boxed.

Comparison Operations
Ocaml provides several comparison operations. Comparison operations are the way you convert other types to bools- Ocaml does not automatically convert things to booleans. In this sense, Ocaml is like Java and not C. Comparison operators are the exception to the rule that operators only take one type of argument. The two operands can be of any type so long as they are the same typeno comparing apples to oranges! Table 1-5. Integer Operations Op
= <> < <= > >= == !=

Meaning structural equality (thats one equal sign, not two) structural inequality (structural) less than (structural) less than or equals (structural) greater than (structural) greater than or equals referential equality (thats two equal signs, not one) inequality

Referential and Structural Comparisons: Rather confusingly, Ocaml provides both referential and structural comparisons. The idea isnt that complicated. For the unboxed types (int, char, bool), there isnt any difference (except that referential is more efcient). For boxed types (everything else), the difference between the two comparison types matters. Referential comparisons compare the values of the pointers. Two objects are equal if and only if theyre stored at the same location. Structural comparisons compare the actual values pointed to- two objects are equal if they have the same value, even if theyre stored in different places. Structural comparison is "deep", in that if the objects being compared has references to other objects, the referenced objects are compared structurally as well.

12

Chapter 1. Intro to Ocaml

Use Structural Comparisons For Floats


One area where the difference between structural and referential comparison is particularly likely to bite is in comparing oating-point comparisons. Remember that oating-point values are boxed, so that you have a pointer to the value rather than the value itself. So when the compiler sees the value 1., it allocates a new location for the oatingpoint value, writes 1. to it, and returns the pointer. A second 1. gets allocated a second, different , location to hold the same value. This gives rise to the counter-intuitive result that the expression 1. == 1. always returns false- the two different values are stored in two different locations. What you want to use is the structural comparisons for oatingpoint values. This is not the case for the unboxed types- ints, chars, and bools, where referential and structural compares have identical results.

Grouping and Precedence


Like most other languages, Ocaml uses parentheses- ( and )- for grouping. The keywords begin and end can be used in place of parens for grouping- theyre semantically equivalent. I tend to use begin/end to group large blocks, and parens to group small expressions. Examples:
# # # #

2 * 1 + 3;;
: int = 5

2 * ( 1 + 3 );;
: int = 8

2 * begin 1 + 3 end;;
: int = 8

A wise programmer once said Multiplication and division happen before addition and subtraction, use parens around everything else. A complete listing of all operators and their precedence is available in the Ocaml language manual if youre interested, but I recommend this rule. An especially important place for parens is when arguments to a function are expressions. For example, the code print_int 3 + 5 is not correct. Ocaml wants to call the function print_int with an argument of 3, and then add 5 to the result. Unfortunately, + only adds two ints, and the return value from print_int is unitadding a unit to an int doesnt make sense. Instead, you should put parens around the expression, like:
# print_int (3 + 5);; 8- : unit = () #

If/Then/Else
Ocaml supplies a standard if/then/else construct in the obvious way. The general structure of if expressions are:
if boolean-expr then expr1 [ else expr2 ]

You dont need parens around the test condition (they dont hurt, however), but it does need to be a boolean expression. Like Java, and unlike C and C++, Ocaml does not automatically convert integers (or chars, or anything else) to bools, you have to explicitly use the comparison operators. The expression in the else clause, expr2, must evaluate to the same type as expr1. It is not OK, for example, for expr2 to be of type int when expr1 is of type oat. 13

Chapter 1. Intro to Ocaml The else clause is only optional if expr1 has type unit, otherwise it is necessary. The type of the entire expression is the type of expr1. Ocamls if statement can be dropped directly into expressions- in this way, its more like Cs ?: operator than a normal if construct. An example shows this in action:
# # # #

if 3 <> 4 then 1 else 2;;


: int = 1

3 + (if 7 = 2 then 1 else -1);;


: int = 2

7 * (if 2 < 4 then 3 else 2);;


: int = 21

Design Pattern: Explicit Compares to Convert to Boolean Ocaml does not auto-promote ints, or any other type, to bools. In this way, Ocaml is like Java and not C. The test expression of the if statement needs to be a bool. So you need to explicitly compare non-bool values in order to branch on them. An example makes this more clear. In C, you can just do:
if (x + 3) { ...

and rely on C to convert the integer value into a boolean. In Ocaml, you need to explicitly compare the integer value to something else in order to branch on it, like;
if (x + 3) != 0 then ...

Exercises
1. Write a program that prints the string "Hello, lunch!", followed by a new line. 2. Write a program that calculates and prints the value of as a oating point number. You can use the formula = 4 * atan(1) (hint: that formula is not legal Ocaml). 3. Ocaml does not provide the function print_bool. How might such a function work?

14

Chapter 2. Variables, Functions, and Tuples


You keep using that word. I do not think it means what you think it means. Inigo Montoya Now that the basic stuff is out of the way, we can start getting into more interesting stuff- variables, functions, and our rst real data structure, tuples. So, without any further ado, lets get to it.

Variables
Ocaml allows you to introduce a variable using a let expression. The syntax for a let expression is just:
let varname = expr ;;

The expression expr is evaluated, a new memory location is allocated and given the name varname, and the value is written to that memory location. An example would be:
let pi = 3.;;

The expression expr can be arbitrarily complicated- it is evaluated at runtime (as a constant constructor for C++/Java programmers), and so there are no limitations on what functions you can call, etc. For example, the following is perfectly valid:
let e = exp 1.;;

In this case, the function exp is called at run time, and the result stored into the newly created variable. Now the leading - the top-level interpreter returns makes more sense. It means that the calculated value is not bound to any name. When dening a variable, it is replaced by the name of the variable dened:
# let e = exp 1.;; val e : float = 2.71828182845904509 #

The val e means "you have dened a variable e". Once youve dened a variable, any use of its name is replaced by its value:
# e;; - : float = 2.71828182845904509 # log e;; - : float = 1. # let x = sin e;; val x : float = 0.410781290502908847 #

Variable names in Ocaml are more limited than in other languages. They must always start with a lower-case letter a-z, followed by zero or more letters of either case, numbers, the underscore (_) character, or the apostrophe () character. Note- and this is important- that variable names cannot start with an upper case letter, an underscore, or an apostrophe! So x, g01, fooBar, y, and do_this are all valid variable names. 15

Chapter 2. Variables, Functions, and Tuples So far, variables have been pretty obvious. But heres the catch: what Ocaml (misleadingly) calls variables are actually named constants. Unlike variables in most other programming languages, once you set the value of a variable, you cannot change it. Now, remember the wise words on the cover of the Hitchhikers Guide to the Galaxy and DONT PANIC. This is one of the biggest differences between Ocaml and other languages- and its often the most shocking. So let me reiterate: DONT PANIC. Ocaml is not broken or stupid or useless, and you havent just wasted the money spent on this book and several hours of your life. This isnt as big a problem as you are probably thinking- its just that Ocaml has a different way of doing things- a way which is equally powerful and expressive, and in many ways more powerful. If it helps, Ocamls denition of a variable is closer to the mathematicians denition than the programmers, and not just because of the syntax. When mathematicians say that x = 3, they are making a statement of truth, not dening an action to perform. The statements x = 3 and x = 4 cannot both be true. One thing you can do is dene new variables with the same names as old variables. After the new variable is dened, its value (and type) are used instead of the old variable. For example, the code:
let e = exp 1.;; print_float e;; print_char \n;; let e = 2.7;; print_float e;; print_char \n;;

will produce the output:


2.71828182846 2.7

One thing to note is that the new variable with the new value does not take effect until after the expression computing its value is evaluated- so you can use the value of the old variable to calculate the new value:
# let e = e +. 1. ;; val e : float = 3.71828182845904509 #

Redening variables can even change their types- since the new variable is (conceptually, at least) wholly new, its type need not be the same as before:
# e;; - : float = 3.71828182845904509 # let e = int_of_float e;; val e : int = 3 # let e = (e == 4);; val e: bool = false #

Here, the type of e changes- the rst e is of type oat, an a value 3.71828182845904509. The second e has type int and a value of 3. The third e has type bool and a value of false. So already were seeing variables apparently varying quite a lot- not only in value, but also in type. More tricks like this are forthcoming.

16

Chapter 2. Variables, Functions, and Tuples

Changing Types of a Variable is a Bad Idea


Simply because you can do it, doesnt make it a good idea. As a style guideline, changing the type of a variable when redening it should generally not be done. Its confusing.

So what happens to the old values of old variables which have been superseded by new variables with the same name? Well, Ocaml is a garbage-collected language (like Java, Perl, Lisp, etc.). This means that sooner or later, the garbage collector will come along and see that the old variables can no longer be accessed, and are therefore garbage. At that point, the memory for those variables will be freed.

Functions
The syntax for dening functions with one parameter is very similar to the syntax for introducing variables (this similarity is not a coincidence, as well see later):
let funcname paramname = expr ;;

Like variables, function names have to begin with a lower-case letter, and can be followed by zero or more letters of either case, numbers, the underscore character, or the apostrophe character. The return value of the function is simply whatever expr evaluates to (given the current arguments). Well handle functions with multiple statements at a later point. A simple example of dening a function:
let f x = 2*x + 3;;

That code denes a function named f, that takes one argument, x. The returned value in this case is just 2*x + 3. Remember that if statements are expressions as well, allowing us to write functions like:
let gf_mult_x x = if (x land 1) == 0 then x lsl 1 else (x lsl 1) lxor 0xb7;;

One thing to note is the lack of any type information with the function denition- this is because Ocaml has type inference. Basically, it looks at the function and say "the * operator multiplies to ints, so x has to be an int, and the type of the whole expression is int, so the function takes an int as a parameter and returns an int." As mentioned in chapter 1, to call this function we just give its name (f, in this case) and the argument:
f 2;; (* Call f with the value of let y = 7; f y;; (* Call f with the value of f ((f y) + 3); (* Call f with the is_even y;; (* call is_even with 2 *) y (aka 7) *) value of f(y) plus three *) the of y (aka 7) *)

This is called applying an argument to a function. To dene functions with more than one parameter, you add more parameter names after the rst one, separated by nothing except whitespace:
let funcname param1 param2 ... = expr ;;

Some examples:
let g x y = 2*x + 3*y + 4;; (* A function with two parameters *)

17

Chapter 2. Variables, Functions, and Tuples


let h x y z w m n p q = ((x*y) land (z lxor (w + m - n))) - (p/q);; (* a function with eight parameters *)

The rst name is is the name of the function- we just dened functions g and h. The rest of the names are the parameters- x, y, etc., are all parameters. To call functions with multiple parameters, you just continue listing the arguments with nothing but space in between. So you might call the functions g and h like:
g 1 2;; (* call g *) h 1 2 3 4 5 6 7 8;; (* call h *)

Watch Your Grouping!


When calling a function, a good idea when calling a function is to put parentheses around any argument more complicated than a simple name or constant. This is correct:
g (f 2) (4 + 3);;

Removing the parentheses makes the above expression both incorrect and confusing:
g f 2 4 + 3 ;;

Like variables, you can redene new functions with the same name as old functions, in exactly the same way. And, just like new variables can use the value of the old variables in calculating their values, so too can new functions call old functions with the same name.

Function Types
Now, lets see how the top-level interpreter responds to a function, as there is a lot worthy of comment:
# let f x = 2*x + 3;; val f : int -> int = <fun> #

The val f tells us the name of the function we just dened- in this case, f. The int -> int is the type of the function dened. This means it is a function that takes one parameter of type int, and has a return type of int. The -> notation is deliberately designed to mimic the notation mathematicians use to describe the type of functions. A mathematician might say that f : Z Z, or that a function f maps every integer onto another integer; Ocaml says val f : int -> int. The last <fun> simply says the value of the function is a function, i.e. a bunch of code to be executed. This is Ocamls way of letting us know that its a function, not a variable. Its important to remember that the last type is the return type- as our function is_even demonstrates:
# let is_even x = (x land 1) == 0;; val is_even : int -> bool = <fun> #

18

Chapter 2. Variables, Functions, and Tuples

Negative Constants as Parameters


One word of warning- remember that the - negation operator is both the negation operator and the subtraction operator. Ocaml has it default to being the subtraction operator when it could be either. This means that if we want to pass in a negative constant to a function, we need to encase it in parentheses. Otherwise:
# is_even -1;; This expression has type int -> bool but is here used with type int # is_even (-1);; - : bool = false # is_even ~-1;; - : bool = false #

The error message is Ocamls way of saying that it thinks youre trying to subtract one from is_even- a nonsensical proposition. The solution is to either use parentheses to force the grouping, and therefore the interpretation of - to be negation and not subtraction, or to use the alternate negation operator ~-.

Now, lets take a look at the case of functions with multiple parameters:
# let g x y = 2*x + 3*y + 4;; val g : int -> int -> int = <fun> #

Again, val g tells us that we just dened a function or variable, and <fun> means its a function, not a variable. But now the type is int -> int -> int. The easiest way to read this is "a function that has two parameters, both ints, and returns an int". More parameters just get their types added on with more ->s:
# let h x y z w m n p q = ((x*y) land (z lxor (w + m - n))) - (p/q);; val h : int -> int -> int -> int -> int -> int -> int -> int -> int = <fun> #

This mess of a type should be read as "h is function that has eight (count em) parameters, all integer, and returns an integer". While its usefull as an example, this function is very bad style. If you nd yourself writing an function with more than a small number (ve or six) parameters, you should probably rethink things. The parameter name shadows any previous variable of the same name, but only for the duration of the function. This works just like it does in C++, Java, etc, for example:
# let x = 3 (* define a global variable *);; val x : int = 3 # let f x = (* The parameter x shadows the global x- within this

* function, x means the parameter x, not the global * variable x. *) x + 7 ;;


val # f - : # x - : # f - : # f : int -> int = <fun>

6;;
int = 13

(* the global x *);;


int = 3

x (* call f with the value of the global x *);;


int = 10

19

Chapter 2. Variables, Functions, and Tuples

Not Returning a Value


Ocaml is like C and Java, in not having procedures (subroutines that do not return a value) as such. Instead, a function can return a unit value (which, if you recall from chapter 1, is Ocamls version of void) to indicate "no value". So, for example, you could write a function to print a message only if the argument is true like:
# let print_msg b =

if b then (* print_string returns unit *) print_string "Hello, world!\n" else () (* just return unit *) ;;
val print_msg : bool -> unit = <fun> #

Local Variables
You can dene local variables inside of a function with a variant of the let syntax introduced so far, called a let/in expression:
let varname = expr1 in expr2

A let/in expression introduces a new variable, with name varname, a value of expr1, but only in the expression expr2. Conceptually, this works a lot like creating a block in C or Java using curly braces ( { and } ), and declaring a new local variable. But it is somewhat more exible in where you can drop new variables in. Note that a new variable is allocated, and expr1 is re-evaluated, every time that expr2 is evaluated. But rst, an example of a let/in expression in use:
let f x = let y = sin x in y *. (1. -. y) ;;

Here, y is a local variable, with a new location and a new value calculated every time f is called:
# let f x =

let y = sin x in y *. (1. -. y) ;;


val # f - : # f - : # f : float -> float = <fun>

1.;;
float = 0.133397566534325301

2.;;
float = 0.0824756163938757253

Remember that let/in expressions are expressions. This means, among other things, that they can be nested: 20

Chapter 2. Variables, Functions, and Tuples


let hypot a let d = let e = sqrt (d ;; b = a *. a in b *. b in +. e)

They can also appear, properly parenthesized, in other expressions:


let f x = let a = sin x in (a *. a) +. (let b = cos x in b *. b) ;;

Just like their global counterparts, variables dened by let/in expressions can hide or mask other variables with the same name, including other variables dened in let/in variables. So we could go:
let f x = let a = let a = let b = let b = a +. b ;; sin x in a *. a in (* redefining a *) cos x in b *. b in

The variable dened by the let/in expression exists only for that expression. If the variable is shadowing another variable, the original variable "re-emerges" after the expression nishes being evaluated.
let b = 3; (* Define a global b *) let f x = let b = b * x in (* define a new b in terms of the global b *) b + 3 (* this uses our local b *) ;; let g y = y + b; (* This uses the global b again *)

Design Pattern: Use Local Variables to Simplify Expressions You can use local variables to factor common expressions to simplify equations. This is most obviously useful when the same subexpression is used multiple times. But its also useful when writing excessively complex equations to break parts of it out onto separate lines.

21

Chapter 2. Variables, Functions, and Tuples

Design Pattern: Use Local Variables to Force Evaluation When talking about the boolean operators, we mentioned that they were short-circuiting- which means that it is possible the second part of the expression would not get evaluated at all if its value wasnt needed. The way to force evaluation is to rst assign them to local variables. The expressions for the values assigned to local variables are always evaluated before the expressions they are used in are evaluated. So, if you wanted the bar y from this expression:
if (foo x) && (bar y) then ...

to always be evaluated (currently, if foo x returns false, then bar will not be called), you could:
let b1 = foo x in let b2 = bar y (* bar will always get called here *) in if b1 && b2 then ...

Multiple Local Variables


You can dene multiple local variables using and. The syntax is:
let varname1 = expr1 [and varname2 = expr2] in expr

For example:
let f x = let a = sin x and b = cos x in (a *. a) +. (b *. b) ;;

More variables can be dened simply by adding more and clauses. The syntax for this is just:
let varname1 = expr1 [and varname2 = expr2 [and varname3 = expr3

[...]]] in expr

For example, you might see code like:


let and and and and ... a b c d e = = = = = sin cos tan exp log x x x x x

22

Chapter 2. Variables, Functions, and Tuples With and, all variables are dened simultaneously. So if one (or more) of the variables being dened mask another variable, all of them use the old value of the variable.
# let a = 3;; val a : int = 3 # let a = 5

and b = a + 2 (* this uses the old a, not the new a! *) in a + b;; (* this uses the new a! *)
- : int = 10 #

Local Functions
As youve probably guessed, let/in expressions can introduce local functions as well. You just add one or more parameters to the variable name in the let/in expression. This allows you to introduce local functions in a natural way:
let vector_length x y z = let square x = x *. x in let t = (square x) +. (square y) +. (square z) in sqrt t ;;

Note that the parameters of local functions can shadow parameters to the functions they are contained in. Like above, where the x parameter to the square local function shadows the x parameter to the vector_length function that contains it. This implies that local functions can see the parameters of the functions that contain them- and this is exactly correct. Local functions in Ocaml have lexical scoping, like nested functions in Pascal and GCC, or inner classes in Java. So we might write:
let norm p x y z = let pow x y = (* x raised to the y power *) exp (y *. (log x)) in let f x = pow (abs_float x) p in pow ((f x) +. (f y) +. (f z)) (1. /. p) ;;

Here, the inner function f uses the parameter p from the outer function norm. Inner functions can, of course, contain inner functions themselves.

Multiple Local Functions


You can dene multiple inner functions using the same and syntax as with local variables. You can even mix and match local function and local variable declarations. The syntax, unsurprisingly, is:
let name1 [param [ param [ ... ]]] = expr1 [ let name2 [param [ param [ ... ]]] = expr2 [ let name3 [param [ param [ ... ]]] = expr3

[...]]] in expr

For those who read BNF notation, the BNF representation might be a little more clear: 23

Chapter 2. Variables, Functions, and Tuples


expr let-expr in expr expr expression let-expr let var-or-fn let-expr let_expr and var-of-fn var-or-fn name opt-params = expr opt-params opt-params opt-params name

An example of let/and in action:


let norm p x y z = let pow x y = exp and x = abs_float and y = abs_float and z = abs_float in let x = pow x p and y = pow y p and z = pow z p in pow (x +. y +. z) ;; (y *. (log x)) x y z

(1. /. p)

Design Pattern: Use Local Functions to Replace Duplicate Code One of the uses of local functions is to eliminate duplicate code. Rather than having a complicated equation cut and pasted multiple times, put the function in a local function and simply repeatedly call it. Because the local function can see parameters from the outer function, you generally dont have to pass very many arguments to inner functions.

The Relationship Between let and let/in


One important point I need to make here. The following code is not valid:
let f x = x * 2 in let g x = 1 + (f x);;

The correct way to do this is to move the local function inside the global function, like:
let g x = let f x = x * 2 in 1 + (f x) ;;

The reason is that the top level let construct is not an expression- it is one of the few statements in Ocaml. Remember that expressions can be statements, but statements are not expressions. This applies to both variables and functions. The construct
let varname [ args ] = expr1

can only be at the top level. The construct


let varname [ args ] = expr1 in expr2

is an expression- it can be at the top level or inside another expression. 24

Chapter 2. Variables, Functions, and Tuples

The Relationship Between Variables and Functions


Youve probably noticed a certain resemblence between how functions are dened and how variables are dened. It might not surprise you to learn that this resemblence is not a coincidence. What you are saying when you program let f x = x + 3;; in Ocaml is literally "declare a variable named f which holds a reference to a function (kind of like a C pointer to a function) which blah blah blah". When you call a function by applying arguments to it, like f 3, you are actually saying "call the function that the variable f holds a reference to with the argument 3". Needless to say, most of the time Ocaml optimizes the actual reference away, and just calls the function. But this does mean you can pull tricks like this:
let f x = x + 3;; let g = f;; let f x = x + 4;; g 7;; (* (* (* (* define a function g now "points" to mask the original We can still call *) the same function as f *) f with a new function. *) the original f through g *)

Note especially the second line. You might think wed need to give a parameter name, like let g x = f x;;, but we dont. We can just copy the pointer over. If the "original" idea of object oriented programming is object (which can contain both state aka data and behavior aka member functions), this the "original" idea of functional progamming- that functions are rst class objects, just like integers and tuples are. We will dig into this idea deeper in the next chapter.

Functions as Arguments
If functions are really just variables holding references to the actual code, than it should be possible to pass those references in to a function as an argument. This is, in fact, easy. To make an parameter a function, all you need to do is apply arguments to it, and the type inference will gure things out. For example, you might write a function to numerically approximate the derivative of another function, like this:
let deriv f x = ((f (x *. 1.001)) -. (f x)) /. (x *. 0.001);;

Here, f is obviously a function, with type float -> float. The type of the parameter is just the type of the function you need to pass in, surrounded with parenthesesthe -> symbol works like an operator, and needs to be grouped.
# let deriv f x = ((f (x *. 1.001)) -. (f x)) /. (x *. 0.001);; val deriv : (float -> float) -> float -> float = <fun> #

How you read this type is that deriv is a function that takes two parameters- the rst parameter is another function which takes a oat and returns a oat, and the second parameter is a oat, and the outer function returns a oat. The parentheses around float -> float group it into a single parameter to deriv. To pass a function in as a parameter, all you have to do is give its name as an argument. You can pass in global or local functions the same way. So, given the deriv function dened above, you might do:
# let g y = y *. y in

deriv g 7.;;
- : float = 14.0069999999994366 #

Notice that the g 7. is parsed as two separate arguments- the function g, and the oat 7.. Which is why, if we wanted to pass the result of calling g with the argument 7., wed have to put parentheses around it, like:
# let g y = y *. y in

deriv g (g 3.);;

25

Chapter 2. Variables, Functions, and Tuples


- : float = 18.0089999999969059 #

Unfortunately, this means expressions like sqrt exp 3. are broken (and agged as such by the type checker). Ocaml tries to pass the function exp, which has type float -> float, in as an argument to a function which expects only a oat.

A Complicated Example
Heres a complicated example of the ideas presented so far. Its an old programmers trick that, for any positive integer x, the expression (in Ocaml) x land (x 1) clears (sets to 0) the lowest bit of x. The integer 40 is represented in binary as 0b101000, while the integer 39 is 0b100111. Bitwise-and them together, and you get 0b100000- decimal 32. Note that if x is a power of two, then x land (x - 1) is 0. In this way the expression also works as a nice test to see if the integer is a power of two. Using this trick, you might write a function that, given a char, returns a char with only the highest bit set of the original char:
let highbit c = let i = int_of_char c in (* use a local variable to store the int value *) (* The expression to clear the low-order bit, but not if its a power * of two, is too complicated- cut it out into a local function. *) let f x = let y = x land (x - 1) in (* common sub-expression factored out *) if y == 0 then (* Its a power of 2- return the original value *) x else (* Its not a power of 2- return the value with the low * bit cleared. *) y in (* this binds to the let f x = ... *) let i = f i in (* pseudo-overwrite i *) let i = f i in (* multiple times *) let i = f i in let i = f i in let i = f i in let i = f i in let i = f i in (* We only have to clear at most 7 bits *) char_of_int i ;;

Tuples
Time to introduce a new data type - the tuple (pronounced like two-pull). A tuple is the simplest non-primitive data type. It is literally just two (or more) other types put together into one container. You might think of a tuple as a structure or a class containing just public member variables, except the variables dont have names, just positions. Tuples resemble dynamically allocated COMMON blocks from FORTRAN.

26

Chapter 2. Variables, Functions, and Tuples

Where the Heck Did You Get The Name "Tuple"? Its the generic sufx for large groups of object. Two things together are a pair, three a triple, four a quadruple. But once you get beyond four, a more general pattern emerges- quintuple (ve things), sextuple (six things), septuple (seven things), octuple (eight things), and so on. So mathematicians started talking about n-tuples as collections of n things, and programming language theorists stole it from them. The n- got dropped off, and that left us with tuples. You make a tuple by joining two (or more) expressions with commas. So 3, "foo" creates a tuple with an int as a rst element and a string as a second element. The expression 3.7, true, a creates a three-element oat-bool-char tuple. Tuple can contain other tuples as elements- the comma is an operator, so you have to group the second tuple with parentheses. So 3.7, ( true, a ) doesnt create a threeelement tuple like before, but a two-element tuple- the rst element is a oat, and the second element is another tuple containing a bool and a char. The type of a tuple is expressed as a Cartesian product- this is a fancy way of saying that its the types of the individual expressions combined with the asterisk, or , character. So the tuple created by the expression 3, true has a type of int bool. Tuples within tuples have their types grouped with parentheses, so 3.7, ( true, a ) has a type float * (bool * char):
# 1, true;; - : int * bool = (1, true) # (("foo" ^ "bar"), (log 3.), a), (false, (6 * 9));; - : (string * float * char) * (bool * int) = (("foobar", 1.09861228866810978, a), (false, 54)) #

Tuples are just types- they can be assigned to variables, and returned from functions, just ne. For example, you might re-implement the C ldiv(3) function using a tuple return type like:
# let ldiv numer denom =

let quot = numer/denom and rem = numer mod denom in quot, rem ;;
val ldiv : int -> int -> int * int = <fun> #

Note that the type of this function is read as "a function with two parameters, both ints, and returning a tuple of two ints". You can also assign variables tuples as values:
# let t = ldiv 49 11;; val t : int * int = (4, 5) #

But this isnt very useful, as it doesnt give you access to the members of the tuple. Fortunately, thats the topic of the very next section.

27

Chapter 2. Variables, Functions, and Tuples

Design Pattern: Use Tuples to Return Multiple Values From a Function In most other languages, a function which wants to return multiple values modies one or more of its parameters to return the extra values. So in C, you pass into the function a pointer to the location to store the extra parameter. In Java, you could pass in an array length one, or dene a special class that the function returns. In Ocaml, just return a tuple. The advantages of using a tuple are manifold. First, it clearly separates inputs to the function from outputs. And all outputs work the same way. And since tuples dont require specialized object or structure denitions (like Cs ldiv_t, which is the structure returned from ldiv()), they dont require a lot of extra code.

Tuples and Variables


You can use a tuple with n elements in it to set the values for n different variables simultaneously with a single let or let/in statement. All you have to do is list the variables on the left-hand side of the equals sign, separating them with commas. An example of how this works helps make this more clear:
let a, b, c = 1, true, 3.;;

Now, this particular code is no different than:


let a = 1 and b = true and c = 3.;;

except that this pattern gives us a way to "pick apart" a tuple and access its members. For example, we could gain access to the results of the ldiv function we dened above like:
let q, r = ldiv n d;;

This trick works with local variables (dened with let/in) just as well as with global variables. In fact, unless I say otherwise, you can assume that if something works with global variables, it also works with local variables. Likewise, if something works with global functions, it also works with local functions. Using parentheses around groupings of variables, you can do this recursively, to pick apart tuples within tuples, like:
let (a, b), c, (d, e) = (1, true), 3., (a, "foo");;

This code is equivalent to:


let a, b = 1, true and c = 3. and d, e = a, "foo";;

which is equivalent to:


let and and and and a b c d e = = = = = 1 true 3. a "foo";;

28

Chapter 2. Variables, Functions, and Tuples One thing to note- the number of variables on the left-hand side of the equals sign has to equal the number of members in the tuple on the right-hand side. So this is illegal, and will be agged as an error by the compiler:
let a, b = 1, a, true;;

As an example of using tuples for a real-world use, here is some code you might write to implement complex multiplication, where complex numbers are stored as float float tuples, the rst tuple being the real component and the second tuple the imaginary:
# let cmult a b =

let ar, ai = a and br, bi = b in ((ar *. br) -. (ai *. bi)), ((ai *. br) +. (ar *. bi)) ;;
val cmult : float * float -> float * float -> float * float = <fun> #

Design Pattern: Use Tuples to Set Multiple Variables Together You often have to assign multiple values based upon a shared condition. For instance, you might want to write:
let ival = if some_test then 1 else 3 in let str = if some_test then "foo" else "bar" in ...

In this case, you can set ival and str simultaneously using a tuple, like:
let ival, str = if some_test then 1, "foo" else 3, "bar" in ...

Ocaml is smart enough (at least, ocamlopt is smart enough) to gure out it doesnt actually need to allocate the tuple, just do the assignments. So if some_test is at all expensive, or especially if some_test is imperative or does I/O, then this pattern allows you to not call it twice.

Tuples as Parameters
The function cmult we just introduced raises an interesting question. The parameters a and b are, in some sense, useless- all we do is immediately pick them apart. Isnt there some way we could dispense with them completely, and just have them handed in picked apart? Why, yes, there is. The same comma separated list of variables idea works for parameters to functions as well as variables. You need to group them with parentheses, however. We can use this to simplify our cmult function thusly:
# let cmult (ar, ai) (br, bi) =

((ar *. br) -. (ai *. bi)), ((ai *. br) +. (ar *. bi)) ;;


val cmult : float * float -> float * float -> float * float = <fun> #

Here, we replace the variables a and b with their picked-apart representations ar, ai and br, bi, thus not having to dene a and b explicitly.

29

Chapter 2. Variables, Functions, and Tuples

Design Pattern: Return the New Data Structure Tuples are immutable. Once you allocate one, you cant change its values. This is a feature they have in common with a lot of data structures in Ocaml, so I want to discuss a design pattern used across the board- just allocate a new one. Consider the case where you are handed a tuple of two ints, and you want to increment the rst int, and decrement the second int. But you cant change the values in the tuple- so what you instead do is you allocate a whole new tuple, with the correct new values, and return it instead, like:
let incdec (a, b) = a + 1, b - 1;;

Allocating a new tuple isnt the performance hit you might think it is. Allocation in Ocaml is very, very fast. Its too complicated to go into at the moment (Appendix A includes a complete discussion on Ocamls garbage collection), but sufce it to say that allocating the new tuple takes a common case of only ve simple instructions in Ocaml- a load, a subtract, a store, a compare, and a (highly predictable) branch. Allocating a new data structure with the correct values has a number of advantages. First of all, its much easier for the compiler to analyze. Second the calling function can decide whether to continue using the old data structure or use the new one- or to use both. Note that since the tuple cannot be modied, it can be reused. Consider the case where you have a tuple whose rst element is an int, and whose second element is another tuple. If all you want to do is increment the int, you dont need to reallocate the second tuple, just reuse it.
let inc (a, b) = a+1, b

Reusing parts of a tuple isnt that big of an advantage- tuples are small, so youre not saving a lot. But this pattern will show up again when we start handling more complicated data structures, where the parts of the data structure we want to modify (and therefore must reallocate) are only a small part of the data structure as a whole

Another Extended Example- log2


At this point, Id like to pause a bit and present an extended example of the concepts given so far. The function is one that you might consider hard to write without being able to change variables- an integer log base 2 function. This is a function that returns the bit number of the highest bit set of an integer (bit 0 being the least signigant bit). For positive numbers, it returns the oor of the base 2 logarithm of x, also known as the largest int n such that 2n x. For negative numbers it returns the bit number of the sign bit, and for 0 it returns -1. This function can be implemented efciently with a variant of the binary search algorithm. First we check if the number is negative or zero, and handle those cases specially. Then, if ints are 64 bits, we check to see if the number is larger than 0xFFFFFFFF- i.e. if the highest bit set is in the upper 32 bits. If it is, are return value starts at 32 and we shift x right 32 places. If its not, or if ints are only 32 bits, our return value starts at 0. Then we check to see if x- which we now know to be in the range of 0 to 0xFFFFFFFF, is greater than 0xFFFF, i.e. if the highest bit set is in the upper 16 bits. If it is, our return value has 16 added to it, and x is shifted right 16 bits 30

Chapter 2. Variables, Functions, and Tuples (putting it in the range of 0 to 0xFFFF), otherwise things remain unchanged. Then we check to see if our number is larger than 0xFF, and so on. The Ocaml implementation of that routine shows off a number of interesting features of Ocaml. It demonstrates using a local function as sort of a macro to simplify redundant code. It demonstrates using tuples to return multiple values from a function, and to passing values to multiple variables from the same expression. It demonstrates using if in an expression. It demonstrates shadowing the same variables multiple times with new values. But most importantly, it shows that not being able to assign new values to a variable doesnt handicap you as much as you might think. Here it is:
let log2 x = let bsearch x c n = if (x >= (1 lsl n)) then (x lsr n), (c + n) else x, c in if (x < 0) then Sys.word_size - 2 else if (x == 0) then -1 else let x, c = if (Sys.word_size > 32) then bsearch x 0 32 else x, 0 in let x, c = bsearch x c 16 in let x, c = bsearch x c 8 in let x, c = bsearch x c 4 in let x, c = bsearch x c 2 in let x, c = bsearch x c 1 in c ;;

Ignoring Parts of Tuples


Sooner or later, itll happen. Youll have tuple you want a part of, but not other parts. One possibility would be to dene those parts of the tuples you dont want as unused "useless" variables. But Ocaml offers a better solution. The variable _- thats a single underscore character- has special meaning in Ocaml. It means "discard this value" when assigning variables. So in:
let a, _, c = 1, true, b;;

the true value is discarded. If you wanted the modulo from our ldiv function but not the quotient, you could simply go:
let _, m = ldiv x y;;

You could dene functions that take our complex number tuples and return the real and imaginary parts like:
let real_of (r, _) = r;; let imag_of (_, i) = i;;

31

Chapter 2. Variables, Functions, and Tuples Using _ doesnt actually dene a variable, so you can use it multiple times in the same let statement or parameter list with no problem. So this function takes the real part from one complex number, and the imaginary part from another, and combines them into a single complex number:
let combine (r, _) (_, i) = r, i;;

You can use _ as a variable all by its lonesome to throw away the return values of expressions you want evaluated, but dont care what the return value is. This is mainly useful for output functions like print_string. So, for example, you might see code like:
let foo x y = let _ = print_string "We reached foo!\n" in x + y

This prints out a quick debugging message before actually executing the body of the function proper. You can, of course, do this multiple times:
let foo x y = let _ = print_string "foo called with x = " in let _ = print_int x in let _ = print_string " and y = " in let _ = print_int y in let r = x + y in let _ = print_string " and returns " in let _ = print_int r in let _ = print_string "\n" in r

Notice that the debugging statements can be added and removed without affecting the body of the code (once the optimizer is done with it, there isnt a difference between let r = x + y in r and just x + y). But this allows you to fake having multiple statements and still be a single expression:
# let foo x y =

let let let let let let let let r ;;

_ _ _ _ r _ _ _

= = = = = = = =

print_string "foo called with x = " in print_int x in print_string " and y = " in print_int y in x + y in print_string " and returns " in print_int r in print_string "\n" in

val foo : int -> int -> int = <fun> # foo 3 7;; foo called with x = 3 and y = 7 and returns 10 - : int = 10 #

Well see the _ discard variable used a lot more as we dig into pattern matching in the next chapter.

32

Chapter 2. Variables, Functions, and Tuples

Universal Types
You might have noticed something odd about the type Ocaml gives for the real_of function dened above. Youd expect it to take a complex number (or at least a tuple of two oating-point values) and return the real part of the tuple. Instead, Ocaml says something different:
# let real_of (r,_) = r;; val real_of : a * b -> a = <fun> #

The a and b are so-called "universal" types, which are stand-ins for literally any type. The type of the function real_of should be read as "for any type a and any type b (a and b can be the same type or different types), this function takes an argument which is a tuple of a type a and a type b, and returns a type a." This is because the real_of function really does work for all two-element tuples. In C++, youd have to explicitly use a template to get the same generality, in Java, youd have to be casting things to Object. In Ocaml, generic types are the default and not the exception. Unless you do something specic to limit the type, Ocaml assumes a universal type. You might remember from chapter one the concept of boxing. Now, hopefully, the reason for that decision becomes clearer. A two-element tuple is always two words in size. If that tuple contains another tuple as its rst element, the rst word in the original tuple is simply a pointer to the other tuple. Likewise, if the rst element of the tuple is a oat, the rst word of the tuple is just the pointer to the oat. How Ocaml implements the real_of function is that it takes as a parameter a pointer to a two-word object (the two-element tuple) and simply returns the rst word. If its a boxed type, like a oat or another tuple, it returns a pointer to the object. If its an unboxed type, like an int or a char, those are stored in place of the pointer. This is rather like the old void * tricks of C. But the same object code works for all twoelement tuples.

Exercises
1. Write the string_of_bool function. 2. There is an old programmers trick to count the number of set bits in an integer, that works like this. First, for every pair of bits you add the even-numbered bits with the odd-numbered bits, to form a set of 2-bit sums. For a 31-bit integer x, this is simply (x land 0x55555555) + ((x lsr 1) land 0x55555555). These 2-bit sums of pairs of bits can then be turned into sums of four bits each by going (x land 0x33333333) + ((x lsr 2) land 0x33333333), the 4-bit sums then turned into 8-bit sums, and so on. This allows you to get a count of the number of set bits in 5 steps for 32-bit ints, or 6 steps for 64-bit ints. Write a function that counts the number of bits in an integer using this trick. The function should work on either 32-bit or 64-bit systems, checking Sys.word_size to see which the current system is. Dont forget that Ocaml ints are one bit short (31 bits on 32-bit systems, 63 bits on 64-bit systems)!

33

Chapter 2. Variables, Functions, and Tuples

34

Chapter 3. TODO: Think of a Title


TODO: Find a Quote William Shakespear

Explicit Typing
Generality as the default is a feature of Ocaml. And it is most denitely a feature. It is much easier to ignore unneeded generality in general, than it is to retrot code to be more general. But there are times when generality is not wanted. For example, the function name real_of implies, to me at least, a complex number. It doesnt make semantic sense if applied to a tuple of a string and int, despite the fact that the object code Ocaml produces still works just ne. If we named the function first_of, then Id be inclined to leave the function generic- all two element tuples have a rst element, so the concept is generic. But the concept of a real element and an imaginary element only makes sense for complexes- so itd be nice to say that the function real_of only works with complex numbers. Which is the point of the whole next section.

Type Declarations
Before we can say that the function real_of only takes complex numbers as its argument, we have to dene to Ocaml what a complex number is. Ocaml has a way to introduce named types similar to C/C++s typedef. The syntax is simple:
type typename = typeexpr ;;

where typeexpr is a type expression like the Ocaml top-level gives (the expression between the : and =). So, for example, we might declare our complex type like:
type complex_t = float * float;;

We can even declare the type of a binary operator on complex numbers like:
type complex_op_t = complex_t -> complex_t -> complex_t;;

The response of the top-level interpreter is a little different for type declarations than for variable declarations, as we can see:
# type complex_t = float * float;; type complex_t = float * float #

It basically echoes the type declaration back to us- not too useful, but at least it tells us it parsed it correctly. Type names have the same constraints that variable and function names do. They have to start with a lower-case letter, followed by zero or more letters of either case, numbers, the underscore, or the apostrophe.

Typing the Arguments of a Function


Now that weve given a name to our type, we can start specifying the type of parameters to our function. How we do this is to follow the parameter name with a colon (:) and the type name. We have to encase the parameter name, the colon, and the 35

Chapter 3. TODO: Think of a Title type in parentheses, however. This is due to how the colon binds, which well get to in a little bit. So we can rewrite our real_of function like:
# let real_of ((r, _): complex_t) = r;; val real_of : complex_t -> float = <fun> #

The type is now limited- we have specied that real_of doesnt work for any twoelement tuple, but only for complex numbers. Note that real_of still works for any tuple of two oats. All we did with the type declaration is to dene a shorthand, give a name, to a complex type (in this case float * float). All Ocaml does when it sees the type complex_t in a type expression it simply replaces it with float * float. So the above denition of the function is functionally no different than:
let real_of ((r, _): float * float) = r;;

The reason for the parentheses around typed parameters is that you type a function by appending a colon and the return type after the list of parameters. Consider, for example, our complex multiplication function (now with typed parameters):
# let cmult ((ar, ai) : complex_t) ((br, bi) : complex_t) =

((ar *. br) -. (ai *. bi)), ((ai *. br) +. (ar *. bi)) ;;


val cmult : complex_t -> complex_t -> float * float = <fun> #

While the parameters are typed correctly, the return type is still wrong. We x it by giving an explicit type for the return type:
# let cmult ((ar, ai) : complex_t) ((br, bi) : complex_t)

: complex_t = ((ar *. br) -. (ai *. bi)), ((ai *. br) +. (ar *. bi)) ;;


val cmult : complex_t -> complex_t -> complex_t = <fun> #

Type Denitions and Universal Types


There are times when you want to name a type that uses one or more universal types. For example, you might want to dene a type to be a tuple whose rst element is an int and whose second element can be any type. The way you do this is to preface the name of the type with a list of the universal types you need, for example:
type a foo = int * a;;

You can have multiple different universal types in a type expression by separating them with commas and encasing them in parentheses, like:
type (a, b) bar = int * a * b;;

This is one place where the echo of the type back from the top-level can get interesting. Remember that we can name our types as complicated as we like. Instead of the plain vanilla a above, we might have said:
type mytype foo = int * mytype;;

36

Chapter 3. TODO: Think of a Title The top-level, however, simplies all universal types into a, b, etc.:
# type (firsttype, second_type) bar =

int * firsttype * second_type;;


type (a, b) bar = int * a * b #

The names of the universal types dont matter to Ocaml. They may, however, have meaning to humans. Note that universal types allow us to express an important relationship between types- the of relationship. Object Oriented designers will talk a lot of is a and has a relations, as a way to differentiate inheritance relationships and member variable relationships. Theyll say things like a car is a vehicle while it has a stereo system. But one relationship they forgot when coining this terminology is the of relationship- we need to express concepts like "a list of ints". Using Explicit Types to Speed Up Comparisons Generally, using explicitly declared types is useless, and simply clutters the code, and doesnt help performance. There is one time, however, when adding explicit types does help performance- and that is when you can replace generic comparisons with comparisons of known types. Consider the function:
let is_lessthan x y = x < y;;

This function has a type of a -> a -> bool, which means it works on any type. It does this by calling a special function, which is coded in C, and which can compare any two values of the same type (the functions name is Pervasives.compare, not that the name of the function is relevant right now). Obviously, if x and y are always going to be ints, calling the generic compare function is going to be a lot slower than just comparing two integers. In this case, explicitly typing one of the two parameters to be int, like:
let is_lessthan (x : int) y = x < y;;

allows the compiler to replace the call to the generic compare with the much faster in-line integer compare. Note that you only have to explicitly type one variable. Ocaml only allows you to compare two things of the same type, so if one parameter is an int, the other one has to be as well. Note that this is only an issue if no other operation denes the type of the parameters already. For example, the function:
let f x y = (3 * x) < y;;

does not need explicit typing. As you are multiplying x by the integer 3, x has to be an integer as well, so y has to be an integer, so no explicit typing is needed.

Recursive Functions
Stated most simply, recursive functions are functions that call themselves. They are a natural way of writting various algorithms and mathematical forumlas, and (as we shall see in Chapter 4) heavily used in Ocaml. 37

Chapter 3. TODO: Think of a Title

Writting Recursive Functions


There is a problem with writting recursive functions with the standard let syntax introduced so far- the function itself is not visible until after its dened. If we tried to write the naive implementation of calculating Fibonacci numbers, we get a compiler error:
# let fib x =

if x > 2 then (fib (x-1)) + (fib (x-2)) else 1 ;;


Unbound value fib #

Even worse, if the function has already been dened and we are introducing a new version, instead of calling the new version, the old version is called instead:
# let fib x = (* old version of fib *)

let _ = print_string "In old version of fib!\n" in x + 1 ;;


val fib : int -> int = <fun> # let fib x = (* new version of fib *)

let _ = print_string "In new version of fib!\n" in if x > 2 then (fib (x-1)) + (fib (x-2)) else 1 ;;
val fib : int -> int = <fun> # fib 3;; In new version of fib! In old version of fib! In old version of fib! - : int = 5 #

The new version of fib is calling the old version of fib, and not itself, as it is intended to. The solution to this dilemna is the let rec construct. The syntax for this is:
let rec funcname param1 [ param2 [ ... ]] = expr1 [in expr ]

Note that recursive functions can be local functions as well. The rec makes the function name visible within the function were dening, meaning we can call ourselves. So now, our naive implementation of the Fibonnacci sequence works as we expect it to:
# let rec fib x =

if x > 1 then (fib (x-1)) + (fib (x-2)) else 1 ;;


val fib : int -> int = <fun> # fib 4;; - : int = 5 #

38

Chapter 3. TODO: Think of a Title

A Example of Recursion: Towers of Hannoi

A Towers of Hannoi game (photo curtesy of Kurt Andersen) If "Hello, World!" is the standard introductory program for every language, then solving the Towers of Hannoi is the standard indtroduction to recursion. Its a child game from Asia which is played on a board with three pegs. On the pegs is a stack of different sized disks. The object of the game is, starting with the pegs all on one peg (call it peg 1), to move all of the disks to another peg (call it peg 3), following these two rules:

You can only move one disk at a time, taking it off one peg and putting it on another (no moving whole stacks, no hiding disks in your pocket). You can not put a larger disk on top of a smaller disk.

The general recursive solution to move n disks from peg a to peg c (using peg b as a temporary storage location) goes something like this: if n = 1, simply move the disk. Otherwise, recursively move n - 1 disks from peg a to peg b (using peg c as a temporary storage location)- this moves them out of the way. Then move the nth disk from peg a to peg c, putting it in the right place. Finally recursively move the n - 1 disks back from their temporary storage location on peg b to where they belong on peg c (using peg a as a temporary storage location). From this description, we can derive the following code:
# let move n a b =

(* move disk n from peg a to peg b *) print_string ("Move disk " ^ (string_of_int n) ^ " from peg " ^ (string_of_int a) ^ " to peg " ^ (string_of_int b) ^ ".\n") ;;
val move : int -> int -> int -> unit = <fun> # let rec toh n a b c =

(* move n disks from peg a to if n > 1 then (* move the top n-1 disks let _ = toh (n - 1) a c b (* move the nth disk *) let _ = move n a c in (* move the top n-1 disks let _ = toh (n - 1) b a c () else

peg c using peg b as a temporary *) out of the way *) in

to where they belong *) in

39

Chapter 3. TODO: Think of a Title


(* just move the disk *) move n a c ;;
val toh : int -> # toh 3 1 2 3;; Move disk 1 from Move disk 2 from Move disk 1 from Move disk 3 from Move disk 1 from Move disk 2 from Move disk 1 from - : unit = () # int -> int -> int -> unit = <fun> peg peg peg peg peg peg peg 1 1 3 1 2 2 1 to to to to to to to peg peg peg peg peg peg peg 3. 2. 2. 3. 1. 3. 3.

An Example of Recursion: Root Finding


Towers of Hannoi, as standard as it is for introducing recursion, isnt very usefull- so lets do something a little bit more interesting. Given a continuous function f of type float -> float, and a range [a, b] (both oats) where the sign of f(a) is different than the sign of f(b) (i.e., either f(a) 0 and f(b) < 0, or f(a) < 0 and f(b) 0), there is at least one x such that a x b and f(x) = 0. This value is called a root of the function f. We can nd this value by a variation on the binary search algorithm. We nd c = (a + b)/2, the midpoint between a and b, and evaluate f(c). If f(c) has the same sign as f(a), we know the the root then lies somewhere between c and b. Otherwise, our root lies between a and c. At which point we can recursively search the subrange for the root. A picture is worth a thousand words:

Here we have a continuous function f(x) crossing the x axis. Our range [a, b] is marked out- with f(a) > 0 and f(b) < 0. Thus we are gaurenteed that f(x) crosses the x axis (has a root) somewhere between a and b. The line marked c is our halfway point. Since f(c) < 0 (in this example), we know the root is between a and c. The problem is when does the algorithm stop? All recursive functions need to have some sort of stopping criteria (otherwise they either turn into innite loops, or throw an exception when they run out of stack space). In this algorithm I take advantage of 40

Chapter 3. TODO: Think of a Title the fact that oating point values only have a nite precision. Unlike the mathematical concept of reals, sooner or later a and b will be so close together that there will not be another oating point value in between them. Thus, when we calculate c, its value will be either rounded down to be the same value as a, or rounded up to be b. At which point weve gotten as accurate as we can get given the limited precision of oating point numbers, and we can stop. And now, the code:
let root f a b = let rec step a b = let c = (a +. b) /. 2. in if (c = a) || (c = b) then (* Weve hit the limit of precision, we can stop now *) (* Return a or b, whichever is closer to 0. *) let x = abs_float (f a) and y = abs_float (f b) in if x <= y then a else b else if ((f a) <= 0) == ((f c) <= 0) then step c b else step a c in step a b ;;

One interesting thing to watch out for: Consider the line if (c = a) || (c = b) then. If we change this to if (c == a) || (c == b) then the function never stops looping. This is because in calculating c, it gets allocated a new location to be stored in- even if it has the same value as a or b, and == compare wether the two values are stored in the same place, not if they have the same value. Remember that 1. == 1. returns false.

Separate Compilation
We didnt touch much on separate compilation in chapter one, because nothing we introduced was worthwhile to put in a different le. But with the introduction of functions, variables, and types, separate compilation now becomes a worthwhile subject to discuss. Obviously, separate compilation is only relevant for the two ways of running Ocaml code that involve manually compiling- compiling to run on the virtual machine (ocamlc), and compiling to a native executable (ocamlopt). Separate les are called modules in Ocaml. Module names always start with an upper case letter followed by zero or more letters of either case, numbers, the underscore (_) character. Notice that the names of modules start with an upper case character, while the type, variable, and function names all start with lower-case letters. Foo is a valid module name, while foo is a type, variable, or function name. Every module has two parts, the interface and the implementation. The implementation of a module Foo is held in the le foo.ml. Notice the change in capitalization of the rst letter. While this is not strictly necessary (recent versions of Ocaml deal with either capitalization of the rst letter), it is standard. The implementation contains the code that actually implements the module- a series of type declarations and global let expressions, similar to the code examples weve been showing so far. The 41

Chapter 3. TODO: Think of a Title


foo.ml is equivalent to a .c le in C. The interface to the same module is held in the le foo.mli, and is roughly equivalent to a .h le in C.

The Implementation
The implementation of a module Foo is the actual code and type denitions to implement the module, and is held in the le foo.ml. Pretty much all of the examples weve given are examples of stuff that can go into the implementation of a module. So, for example, the implementation of a Cplex module, the contents of cplex.ml (there is already a Complex module in the standard library, were avoiding a name collision), might be:
type complex_t = float * float;; let make r i = r,i;; let i = (0., 1.);; let real_of (r, _) = r;; let imag_of (_, i) = i;; let add (r1, i1) (r2, i2) = (r1 +. r2, i1 +. i2);; let sub (r1, i1) (r2, i2) = (r1 -. r2, i1 -. i2);; let mul (r1, i1) (r2, i2) = (r1 *. r2) -. (i1 *. i2), (r1 *. i2) +. (r2 *. i1) ;; let inv (r, i) = let u = (r *. r) +. (i *. i) in (r /. u), -. (i /. u) ;; let div p q = mul p (inv q);; let to_string (r, i) = (string_of_float r) ^ " + " ^ (string_of_float i) ^ "i";;

There is no main procedure per se in Ocaml. Rather, the values assigned to global variables are evaluated in the order they are given when the program starts executing, and, since variables can have arbitrarily complicated expressions to evaluate, this can be used instead. This includes, by the way, the discard variable (_). So if you wanted the module Foo to print a greeting on start-up, you could simply go:
let _ = print_string "Hello, world!\n";;

Note that any expression which is not assigned to a variable in a let statement is assumed to have been assigned to the discard statement. So now we see how our original Hello World program works. Note that variables have their values calculated and are visible only after theyre dened. So if you have one large, complicated main program loop, it needs to be last. Otherwise the rest of the variables will never be gotten to.

42

Chapter 3. TODO: Think of a Title

The Interface
The interface to a module Foo is held in the le foo.mli. The interface is simply the names and types of those types, global variables, and global functions which are visible from outside the module. For a type, variable, or function to be static (for all you C programmers) or private (for everyone else) to the module, they simply arent listed in the interface. The syntax of the interface of a module is different from the syntax of its implementation. So, for example, the interface to the Cplex module we implemented above, which would be the contents of the cplex.mli le, might be:
type complex_t val make : float -> float -> complex_t val i: complex_t val real_of : complex_t -> float val imag_of : complex_t -> float val add : complex_t -> complex_t -> complex_t val sub : complex_t -> complex_t -> complex_t val mul : complex_t -> complex_t -> complex_t val inv : complex_t -> complex_t val div : complex_t -> complex_t -> complex_t val to_string : complex_t -> string

The very rst line of this le is interesting. It announces that we have a type named complex_t. Nothing else is said about this type- it is an abstract type declaration. Remember that in Ocaml, everything is a reference. So the calling functions dont need to know anything about the actual structure of a complex_t to handle one, and we arent telling them anything. You can export the structure of a type (if you wish) in a modules interface by adding an equals sign and the actual type of the structure, like:
type complex_t = float * float

This would allow code outside the module to access the members of a complex number. In general this is a bad idea- by exporting the structure of a complex_t users become dependent upon the structure not changing. If, at a later point, you wanted to change the structure of a complex_t (from a tuple to a structure, say, or from a real + imaginary representation to an angle + length representation), you would have to modify not only the Cplex module, but also all code that depends upon knowing the internal structure of a complex_t. I will make one other comment in support of using abstract types. The Ocaml nativemode compiler, ocamlopt, does have the ability to in-line functions across module boundaries. So accessor functions like real_of and imag_of, and simple constructors like make, will likely be in-lined automatically. So there is no performance advantage to having the actual structure of a complex_t exposed. You can have as many types declared in an interface as you wish, and you may have both abstract and concrete denitions. The rest of the lines in the interface are value declarations. These are either global variables (for example, i, with is the unit imaginary value), or global functions. The basic syntax of a value declaration is:
val valname : typeexpr

where valname is the name of the variable or function, and typeexpr is the type. This syntax probably looks vaguely familiar to you- and it is. Its the rst part of the line (everything up to the =) which the Ocaml top-level prints: 43

Chapter 3. TODO: Think of a Title


# let add ((r1, i1) : complex_t) ((r2, i2) : complex_t) : complex_t =

(r1 +. r2), (i1 +. i2) ;;


val add : complex_t -> complex_t -> complex_t = <fun> #

There is an even easier way to generate interface les. Both the bytecode compiler (ocamlc) and the native-code compiler (ocamlopt) take -i as an argument, which makes them output the signature of a given implementation. So, for example, running ocamlc -i cplex.mli gives:
type complex_t = float * float val make : a -> b -> a * b val i : float * float val real_of : a * b -> a val imag_of : a * b -> b val add : float * float -> float val sub : float * float -> float val mul : float * float -> float val inv : float * float -> float val div : float * float -> float val to_string : float * float ->

* float * float * float * float * float string

-> float * float -> float * float -> float * float -> float * float

Which isnt perfect, but its a place to start (and often is good enough). An explicit interface is not needed if none of the members of the module will (or can) be referenced from other modules. This is common for modules which contain the main program loops. You might notice that in the interface denition I dont use double semicolons to separate the statements. They are legal and can be used, but almost never are, so I follow convention and dont use them either.

Compiling with Separate Modules


You need to compile the interface le for Ocaml as well as the implementation, using either ocamlc -c (for bytecode) or ocamlopt -c (for native). The interface compiles into a .cmi le, which is basically a precompiled header. The implementation compiles into a .cmo by ocamlc, or both a .cmx and a .o by ocamlopt. The .o le is the actual relocatable object le produced by ocamlopt, while the .cmx le is the extra linking information Ocaml needs to perform whole-program analysis (including inlining functions across module boundaries). Both are needed for linking later. If an interface le exists, it must be compiled before the corresponding implementation is compiled. If an interface le does not exist, ocamlc and ocamlopt will create a default .cmi le (one that exports everything, just as if you have used the -i option to create the .mli le). For example, assume that in addition to helloworld.ml we also had hellosailor.mlwhich looks exactly like helloworld.ml except that it instead prints out "Hello, Sailor!" 1 We could compile both of these les into a single executable by the following sequence of commands:
$ ocamlopt -c helloworld.ml $ ocamlopt -c hellosailor.ml $ ocamlopt -o hello helloworld.cmx hellosailor.cmx $ ./hello Hello, World! Hello, Sailor! $

44

Chapter 3. TODO: Think of a Title The order that .cmx les are given on the command line is important- that controls the order the statements in those les are executed. For example, if we changed the order of helloworld.cmx and hellosailor.cmx in our above example, the order the two statements are printed out will change as well:
$ ocamlopt -o hello2 hellosailor.cmx helloworld.cmx $ ./hello2 Hello, Sailor! Hello, World! $

This is also important because a function cannot be called, remember, until it is dened. So if helloworld.ml dened a function that hellosailor.ml called, then helloworld.cmx needs to be before hellosailor.cmx in the list of les to link. Ocaml is written, unsurprisingly, primarily in Ocaml. But this means that ocamlopt, while it generates native code, is actually a bytecode-interpreted program. Normally this isnt a problem, but for large projects compile time does become an issue. In this case, use ocamlopt.opt, which is the same program compiled to a native binary (there is also a ocamlc.opt, which is a native binary that produces bytecode).

Accessing other Modules


Now that you have separate modules, and can compile them and link them, how do you access functions and variables in other modules? There are two ways. The rst, and preferred, method is to simply preface the name of the function you want to access with the name of the module (with the rst letter capitalized, remember) and a period (.). So, to access the mult function in the Cplex module, it would be simply Cplex.mult. This is preferred, because this puts the multiplication routine for complexes in a different name space for other routines with the same name, for example Bigint.mult, Vector.mult, etc. The next way to access separate modules is the open command. This is much more like Javas import, or Cs #include. To open a module Foo, you just say open Foo;;. This makes all the variables, functions, and types visible in the current module without prexing every name with the module name. But it doesnt add Foos interface to the current modules interface. The problem with this is name-space clashes- if you did:
open Cplex;; open Bigint;; open Vector;;

and all three modules had a mult function, Vectors mult would shadow the other two. You could still access them with Cplex.mult etc., but this is a good way to accidentally introduce bugs. When you compile, you supply the libraries you need explicitly. In the top-level interpreter, things are a little different. The open command works like expected for the standard libraries (which are already linked into the top-level interpreter). But non-standard libraries need to be explicitly loaded. There are two ways you can do that. First, the command #load "foo.cmo" loads and links in a bytecode module (one compiled with ocamlc). Remember, the top-level is using bytecode to execute everything. This includes evaluating any variable values:
# #load "helloworld.cmo";; Hello, World! # #load "cplex.cmo";; # open Cplex;; # mul;;

45

Chapter 3. TODO: Think of a Title


- : Cplex.complex_t -> Cplex.complex_t -> Cplex.complex_t = <fun> # let x = make 1. 2.;; val x : Cplex.complex_t = <abstr> # imag_of (add x i);; - : float = 3. #

Note that the <abstr> is just Ocamls way of telling you that the type was declared as abstract, and you cannot see the internals. The other way to access other les in the top-level interpreter is the #use command. Entering #use "filename.ext" in to the top-level interpreter acts exactly as if you had just typed the contents of filename.ext in yourself. Note that this is expected to be uncompiled Ocaml code, so itll generally be filename.ml, but its not required to be.
# #use "hellosailor.ml";; Hello, Sailor! - : unit = () #

Exercises
1. Write a module to create, add, subtract, multiply, invert, and divide quaternions. Quaternions are a number system (useful in graphics, physics, and other places) like complex numbers, but instead of assuming that -1 has a lone square root, it has three square roots- i, j, and k, which follow these rules:

ij = -ji = k jk = -kj =i ki = -ik = j i2 = j2 = k2 = -1

A tuple of four numbers (a, b, c, d) can then represent the quaternion a + bi + cj + dk in the same way the tuple of two numbers (a, b) can represent the complex number a + bi. Addition, subtraction, and multiplication are easy to derive using the distributive and associative properties (watch the negations on multiplication!). The inverse of the quaternion q = a + bi + cj + dk is q-1 = (a/u) - (b/u)i (c/u)j - (d/u)k, where u = a2 + b2 + c2 + d2 . For any two quaternions p and q, p/q = p*(q-1). 2. In one example, we used the classic calculus formula to numerically approximate the derivative of an arbitrary function at a given location:
let deriv f x = ((f (x *. 1.001)) -. (f x)) /. (x *. 0.001);;

This is just a variant on the classic formula in all calculus textbooks that df/dx = (f(x + h) - f(x))/h, except we set h = 0.001 *. x. As we saw, this is actually not a very good approximation. A much better approximation would be to calculate that function with two different hs, h1 and h2, where h1 = -h2. This gives us two different approximations y1 and y2, where yi = ((f(x + hi) - f(x))/hi. The two points (h1, y1) and (h2, y2) form a line y = m*h + b. If we evaluate this line with h = 0, we get a much better approximation of the real derivative of the function. 46

Chapter 3. TODO: Think of a Title As youve probably already guessed, this exercise is to write this improved deriv function. Ill give you that m = (y2 - y1)/(h2 - h1), and that b = y1 m*h1. 3. In our example of recursion where we implemented root nding by binary search, the root function isnt as efcient as it might be. Specically, local function step evaluates (f a) <= 0, even when this value has already been evaluated before in previous calls to step. Fix the function so that it only calls f once per step and not twice (hint: step needs an extra parameter). 4. Write a function that, given any continuous function with a type float -> float and a range of values, nds the minimum of the function in that range, using the following algorithm (hint: use a local function as the recursive function). Given a function f, the range [a, b], and the magic constant (which Ill dene in a moment), we set c = (b - a) + a, and d = (1 - )(b - a) + a, and calculate f(c) and f(d). Now, if f(c) f(d) we know that the minimum is in the range [a,d], and that if f(c) > f(d), its in the range [c,b] (draw yourself a picture- its obvious). We can then continue to search the new subrange recursively, until the range is "small enough" and we can stop. Consider the function from the root nding example:

Once again, [a, b] is our range. Now, instead of one interior point (the mid point), we have two- c and d. Since f(d) f(c), we know our root is in the range [c, b]. If we pick the right , a magic thing can happen. Lets assume that f(c) f(d), and that our new range is therefor [a, d]. We pick new points, call them c = (d - a) + a and d = (1 - )(d - a) + a. But we already know f(c) in that range- if we picked the right , d would = c, and we wouldnt have to calculate f(d), wed already know it. This would mean wed call f only once per step. Given the example above, our next step would look like this:

47

Chapter 3. TODO: Think of a Title

But notice that our new c is our old d, and we calculated the new f(c) last time as the old f(d). All we really need to do is calculate the new f(d). So, we know that c = (b - a) + a, and that d = (1 - )(d - a) + a, and that d = (1 - )(b - a) + a, substituting all of that in, we can do the following algebra:
(b (b (b =

- a) - a) - a) (1 -

+ a = (1 - )((1 - )(b - a) + a - a) + a = (1 - )((1 - )(b - a) + a - a) = (1 - )(1 - )(b - a) )2

or, (1 - )2 - = 0. Plugging this formula into our root nder example discovers that a = 0.381966011250105153 ts the bill. Note that, with this , if our new range is [c,b], d = c, and we still only have to call f once per step.

Notes
1. This is the traditional greeting printed out for rst-time programs for the Stanford Articial Intelligence Laboratory- SAIL, whose members are sometimes referred to as SAILors. Any similarity between this greeting and the greeting given by ladies of low repute to maritime workers is, of course, completely coincidental.

48

Chapter 4. Variant Types and Pattern Matching


TODO: Find a good quote for here. William Shakespear Variant types are Ocamls answer to Cs enums, unions, and NULL pointers, all rolled up in one powerful idea. They go hand in hand with Ocamls answer to switch/case statements- pattern matching. We also introduce structures (much like a C struct) in this chapter, and introduce the myriad of ways functions and pattern matching interact.

Simple Variant Types


Using variant types as an C-like enum is simple enough. All you need to do is declare a type with various symbolic names. The symbolic names have the same rules as module names do- the rst letter has to be uppercase, followed by zero or more letters of either case, numbers, underscores, or apostrophes, combined with the vertical bar |. The syntax looks like:
type name = [ | ] Tagname [ | Tagname ] [ | Tagname ]

[ ... ]
;;

So you might declare a type represents the suits of a deck of cards by just:
# type suit =

Spade | Heart | Diamond | Club ;;


type suit = Spade | Heart | Diamond | Club #

Note that you can have an optional | to start a variant type, so you will generally see the above denition as:
type suit = | Spade | Heart | Diamond | Club ;;

After this declaration, the literals Spade, Heart, Diamond, and Club are called "variant tags", and can be used with a type of suit. For example:
# Heart;; - : suit = Heart # let is_trump x = if (x == Spade) then

true else false ;;


val is_trump : suit -> bool = <fun> #

49

Chapter 4. Variant Types and Pattern Matching

Variant types are not integers! One major difference between Ocamls variant types and C enums is that variant types are not integers. They are special, symbolic values. You can easily enough write functions to convert them to integers and vice versa, but this conversion does not take place automatically.

Redening a Variant Tag


One word of warning with variant tag names- using the same name in multiple differnt variant types is a bad idea. The most recently seen denition is the only one visible- the rest are masked. For example:
# type color = Red | Green | Orange;; type color = Red | Green | Orange # type fruit = Apple | Kumquat | Orange;; type fruit = Apple | Kumquat | Orange # let is_orange (x: color) = x == Orange;; This expression has type fruit but is here used with type color #

Ocaml is telling you that youre trying to compare a color to a fruit, and that this isnt allowed. This is because the original denition of Orange as a color has been masked by the denition of Orange as a fruit. Notice that this is still and error despite the fact that you might think that the two denitions of Orange should have the same "numeric value".

Simple Pattern Matching


Consider the function:
# let suit_number s =

if s else else else ;;

== if if (*

Spades then 0 s == Hearts then 1 s == Diamonds then 2 its clubs *) 3

val suit_number : suit -> int = <fun> #

This is clunky and hard to write (especially if we had more than four suits). Ocaml does provide a mechanism to write this function in a better way- pattern matching. Pattern matching is much like many languages switch or case statements (but with extra capabilities, as well see in a bit). Pattern matching (match/with expressions) allow you to just list the cases and the values they evaluate to. The basic syntax of match/with expressions are:
match expr0 with [ | ] value1 -> expr1 [ | value2 -> expr2 [ | value3 -> expr3

[ ... ] ] ]

The above syntax works exactly like:


let tempval = expr0 if tempval = value1 [ else if tempval = [ else if tempval = in then expr1 value2 then expr2 value3 then expr3

50

Chapter 4. Variant Types and Pattern Matching


[ ... ] ] ]

with two exceptions- one, value1, value2, etc., all have to be constant values, they can not be general expressions (like they can with if statements). Second, you dont need the dangling else if you deal with all the possible cases. So we could rewrite the suit_number function like:
# let suit_number s =

match s with | Spades -> 0 | Hearts -> 1 | Diamonds -> 2 | Clubs -> 3 ;;
val suit_number : suit -> int = <fun> #

Here the values Spades, Hearts, etc., are called "patterns", and this entire construct is called pattern matching. One thing to remember is that a match/with expression is still and expression. You can write code like:
let cardnum s c = let suitnum = match s with | Spades -> 0 | Hearts -> 1 | Diamonds -> 2 | Clubs -> 3 in (13 * suitnum) + c - 1 ;;

and
let cardnum s c = 13 * ( match s with | Spades -> 0 | Hearts -> 1 | Diamonds -> 2 | Clubs -> 3 ) + c - 1 ;;

One important thing to note is that Ocaml can detect when you dont cover all the cases:
# let suitno s =

match | | | ;;

s with Spades -> 0 Hearts -> 1 Diamonds -> 2

Warning: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Clubs val suitno : suit -> int = <fun> #

The function still compiles, but calling it with an argument of clubs results in it throwing an exception (Ill cover exceptions, throwing and catching them, in the next chapter). I would strongly, strongly suggest that you treat this warning as an error- every 51

Chapter 4. Variant Types and Pattern Matching case should be explicitly dealt with (using an assertion- which Ill deal with in the next chapter as well, to cover those patterns you really dont have a good way to handle).

Fall-Through Pattern Matching


Ocaml pattern matching is different from C/C++ switch statements in that the patterns do not fall through to the next pattern. You dont need a break statement at the end of each case, and Duffs Device 1 is simply not a possibility. But this makes sharing code between cases different. Consider the following code:
type color = Red | Black;; let suit_color s = match s with | Spades -> Black | Clubs -> Black | Hearts -> Red | Diamonds -> Red ;;

Itd be nice if we could avoid duplicating the expressions (this is especially nice in cases where the expressions being duplicated are more complicated than a simple Black or Red). Itd be nice to be able to use pattern matching to express code like:
let suit_color s = if (s == Spades) || (s == Clubs) then Black else Red ;;

I hope by now you trust me enough to know that I wouldnt have taken you on this paragraph long ride simply to say "you cant do that". Of course there is a way to share a common expression between multiple different patterns. If you leave off the -> and the given expression, the pattern uses the same expression as the next pattern. Leaving them off of multiple patterns has all the patterns fall through to a shared expression. The syntax for this is:
match expression with [ | ] pattern [ | pattern ] [ | pattern ]

[ ... ] -> expression [ [ [ [


| pattern | pattern ] | pattern ]

... ]

-> expression ]

[ ...

So we can rewrite the suit_color function like this:


let suit_color s = match s with | Spades | Clubs -> Black | Hearts | Diamonds -> Red ;;

52

Chapter 4. Variant Types and Pattern Matching

Tagged Data
Variant types can also be used to tag data, and thus have one variable hold more than one type. In this way, variant types work like the old trick with C union of structureswhere the rst element of each structure in the union is an enum telling you which structure the union is. Another way to look at it is that tagged data types is Ocamls way of implementing a simplistic form of run time type identication (where the tag is the type information).

Dening Tagged Types


You dene a tagged type by adding the keyword of and the type stored with the tag to the type denition:
type name = [ | ] Tagname [ of type-expr ] [ | Tagname [ of type-expr ] ] [ | Tagname [ of type-expr ] ]

[ ... ]
;;

So, for example, you might dene a number (that can hold either an integer or a oating point value) like:
type number = | Int of int | Float of float ;;

Notice that since they start with capital letters, the tags Int and Float dont conict with the types (and keywords) int and float. The type expression can be any type, including that of a tuple- so, for example, we might have declared numbers to include complex numbers like:
type number = | Int of int | Float of float | Complex of float * float ;;

When the tag also contains data, the tag name is often called a constructor, as it acts like a single argument function to create a member of the type:
# type number =

| Int of int | Float of float | Complex of float * float ;;


type number = Int of int | Float of float | Complex of float * float # Int 3;; - : number = Int 3 # Float 3.1415;; - : number = Float 3.1415 # Complex (1., 2.) - : number = Complex (1., 2.) #

53

Chapter 4. Variant Types and Pattern Matching The parentheses around the arguments to Complex are necessary- otherwise, the expression is interpreted like (Complex 1.), 2., which produces an error:
# Complex 1., 2.;; The constructor Complex expects 2 argument(s), but is here applied to 1 argument(s) # Int 1 + 3;; This expression has type number but is here used with type int #

You will often see parentheses around the arguments to a constructor even when they arent technically needed, to make sure the correct bindings happen. You can mix tagged values with normal variant types, like:
type number = | Zero (* the constant zero - no data associated with it*) | Int of int | Float of float | Complex of complex ;;

54

Chapter 4. Variant Types and Pattern Matching

Design Pattern: Use Tagged Types for Run Time Type Identication One of the common complaints made against Ocaml is the lack of run time type identication. How, you ask, can you write a function that does one thing with a string and another with an integer? How can you even write a function that can take either a string or an integer? The solution (if universal types arent what you need) is to use a tagged type. The number solution above is a perfect example of how this works. One way to look at tagged types is that the tags add type information to the data. There are a couple of advantages to doing run time type identication this way. The rst is that adding type information to a variable takes up extra memory. The tag Int 3 takes up two words of memory, while the integer 3 only takes up one. Type information is only added to those variables which actually need it. This gives Ocaml an advantage both in memory utilization and in performance (albeit a minor one). The second (and much more important) advantage is correctness. Weve all hit the problem- were storing multiple different types somewhere, maybe in some data structure. Originally, the data structure holds only ints or oats. Everywhere you pull elements out of the data structure, you have a similiar tree of ifs to hand the different types. And then it happensyou need to store strings in the data structure as well. Now you have to go back through the code and nd all the places where you are pulling data out of that data structure and add a new case to the if tree to handle strings. And, inevitably, you miss one (I always seem to miss one). Its not xing the problem thats so hard, its just nding the problem. Using tagged types, the Ocaml compiler can do the skut work for you- looking through all the code for all the places where youre handling the type, and warning you of all the places where you dont handle the new case. Generally, you dont need to do run time type identication in Ocaml as much as you do in other languages. One common pattern in other languages is to use short, known-length lists with different types in them to implement what in Ocaml would be a tuple. Ocaml provides you with a plethora of different data structures- in addition to the tuple (introduced in chapter 2) and the variant type (being introduced now), Ocaml also provides the structure (introduced later in this chapter), the list, the array, and the object as built-in data types. All of them have their strengths and weaknesses. Rather than asking how to contort a given data structure to do everything, you should be asking yourself what the right data structure for the problem is.

Matching Tagged Types


The real power of tagged types and pattern matching comes when you combine the two. One thing pattern matching can do is it can dene new variables holding the values from the patterns youve just matched. So a pattern like Int i, this matches any number created with the Int constructor, and creates a new variable named i to hold the value passed to the contructor. This gives you a convient way to get the values back out of a tagged type again. As an example, consider the function string_of_number (which works like string_of_int but with our number class):
# let string_of_number n =

match n with | Zero -> "0" | Int i -> string_of_int i

55

Chapter 4. Variant Types and Pattern Matching


| Float f -> string_of_float f | Complex (r, i) -> "(" ^ (string_of_float r) ^ ", " ^ (string_of_float i) ^ ")" ;;
val string_of_number : number -> string = <fun> # string_of_number (Int 3);; - : string = "3" # string_of_number (Float 3.1415);; - : string = "3.1415" # string_of_number (Complex (1., 2.)) - : string = "(1., 2.)" #

Note that when you have multiple patterns evaluating to the same expression, the patterns must all dene the same variables with the same types. The following, for example, would cause an error:
match num with | Int i | Float f -> (* do something *) ...

The problem is that if num is a Float, the variable i wouldnt be dened, and if num was an Int, the variable f wouldnt be dened- so you couldnt use either. The types of the variables also have to be the same everywhere- this isnt allowed either:
match num with | Int x | Float x -> ...

Here, the type of x is indeterminate- it might be an int, or it might be a float. Use Tagged Types for Nullable Variables In Ocaml, everything except for the small handfull of unboxed types are boxed- that is, instead of having the object itself, you have a reference or a pointer to the object. But Ocaml references are more like C++ references than they are C/C++/Pascal pointers, or Java references, in that they can not be null (nil for you Pascal programmers) but instead must always point to some valid object. The way around this is to use a tagged type to add a null value, for example:
type nullable_int = | Int of int | Null ;;

Reusing the same name for the null values of different types is a bad idea (as shown before), so Ocaml programs tend to come up with different names for them- Null, Nul, Nil, End, Stop, Leaf, Empty, etc. are all commonly used as names for null values.

56

Chapter 4. Variant Types and Pattern Matching

Recursive Types
A tagged type can hold any other type (can have any type expression to the right of the of keyword), including itself . The type name is visible within its own denition, allowing you to write types and code like:
type expr = | Variable (* the value of the variable *) | Value of float | Add of expr * expr | Sub of expr * expr | Mul of expr * expr | Div of expr * expr ;; let rec eval e x = (* evaluate the expression e with match e with | Variable -> x | Value f -> f | Add (l, r) -> (eval l x) +. | Sub (l, r) -> (eval l x) -. | Mul (l, r) -> (eval l x) *. | Div (l, r) -> (eval l x) /. ;;

the variable value of x *)

(eval (eval (eval (eval

r r r r

x) x) x) x)

let rec deriv e x = (* Calculate the derivitive of the expression e at the point x *) match e with | Variable -> 1. (* if f(x) = x, then f(x) = 1 *) | Value f -> 0. (* if f(x) = c, then f(x) = 0 *) | Add (l, r) -> (* if f(x) = g(x) + h(x), f(x) = g(x) + h(x) *) (deriv l x) +. (deriv r x) | Sub (l, r) -> (* if f(x) = g(x) - h(x), f(x) = g(x) - h(x) *) (deriv l x) -. (deriv r x) | Mul (l, r) -> (* if f(x) = g(x) * h(x), f(x) = g(x)*h(x) + g(x)*h(x) *) ((deriv l x) *. (eval r x)) +. ((eval l x) *. (deriv r x)) | Div (l, r) -> (* if f(x) = g(x) / h(x), f(x) = (g(x)*h(x) - g(x)*h(x))/(h(x) * h(x)) *) let t = eval r x in (((deriv l x) *. t) -. ((eval l x) *. (deriv r x))) /. (t *. t) ;;

Universal Types and Tagged Types


You can use universal types in tagged data structures just as easily as you can with tuples. Like with tuples, the type name has to be prefaced with the names of the universal types you want to use. For example, you might implement an unbalanced tree like:
type a tree = | Branch of a * a tree * a tree | Leaf ;;

Here we have just dened a tree data type that can hold any other type (including other trees). 57

Chapter 4. Variant Types and Pattern Matching Pattern matching works with universal types just like it does with normal types, except now the new variables dened in the patterns are universal types. We might implement an add function for our tree type like:
# let rec add tree newval =

match tree with | Branch(curr, left, right) -> if curr = newval then Branch(newval, left, right) else if curr < newval then Branch(curr, left, (add right newval)) else Branch(curr, (add left newval), right) | Leaf -> Branch(newval, Leaf, Leaf) ;;
val add : a tree -> a -> a tree = <fun> #

Notice that our add function implements the "Return a New Copy" pattern. Instead of modifying the old tree, it reallocates new nodes in the tree as necessary. We recurse down the tree looking for the place to insert the new element, and the allocate new nodes as we return back up the tree. Assuming our tree is more or less balanced, we replace O(log N) nodes in the tree with new nodes- the rest of the nodes are shared between the new tree and the old tree.

The option Type


One of the most common uses for universal tagged types is to implement the option type, like:
type a option = | Some of a | None ;;

This gives you a nullable universal type. Rather than dening types like the nullable_int example a while ago, you could instead just use int option instead. This is such a usefull and common construct that you dont need to actually dene the option type, Ocaml automatically denes it for you.

Discard Variables in Pattern Matching


By calling the variables dened in pattern matching "variables", I imply that the discard variable can be used in pattern matching as well. This is absolutely correct. Anywhere that can be a variable can be the discard variable (_). So we might dene our real_of function from the previous chapter to work on numbers like this:
let real_of n = match n with | Zero -> 0. | Int i -> float_of_int i | Float f -> f | Complex(r, _) -> r ;;

Note that you can use as many discard variables in patterns (even in the same pattern) as you like- all the discard variable means is "ignore this". So we might write an is_complex function (which returns true if a number is a complex number, and false otherwise) like this:
let is_complex n =

58

Chapter 4. Variant Types and Pattern Matching


match n with | Zero | Int _ (* We dont care what the ints value is *) | Float _ (* nor the floats *) -> false | Complex(_, _) -> true ;;

Default Patterns
A pattern can consist of nothing except a single variable name. This pattern matchs anything that doesnt match some prior pattern. This allows default patterns to be constructed. For example, we might have written is_complex like this instead:
let is_complex n = match n with | Complex(_, _) -> true | x -> false (* matchs everything that isnt already matched *) ;;

This is even more usefull when we replace the unused x variable name with the discard variable:
let is_complex n = match n with | Complex(_, _) -> true | _ -> false ;;

So Ocamls | _ -> is the equivelent to C/C++s default case. An important thing to notice is that pattern matching happens (conceptually) in order. This means that for Complex numbers, the rst pattern overrides the second. If we instead inverted the order of the patterns, wed get an incorrect behavior:
# let is_complex n =

match n with | _ -> false | Complex(_, _) -> true ;;


Warning: this match case is unused. val is_complex : number -> bool = <fun> # is_complex (Complex(1., 2.));; - : bool = false #

The order in which the patterns are listed is important.

59

Chapter 4. Variant Types and Pattern Matching

Nested Matching
There is a "dangling-else" problem (also known as a shift-reduce conict to compiler writers) with pattern matching. To do pattern matching within a pattern match you need to wrap the inner pattern match with either parentheses or with begin/end. Otherwise Ocaml doesnt know when the inner matching ends, and the outer matching resumes. For example, this code is incorrect:
match (foo) with (* outer match *) | Some_pattern(x) -> match x with (* inner match *) | Another_pattern -> 0 | Different_pattern -> 1 | _ -> 2 | Yet_another_pattern -> 3 (* this goes with the inner match, * not the outer match! *)

The Yet_another_pattern pattern gets bound by Ocaml with the inner match, not (as indentation seems to suggest) with the outer matchremember, in Ocaml, indentation is (mostly) ignored! C/C++ have the exact same problem with else clauses get bound to the wrong if statement. One solution is to simply wrap the inner pattern matching in either parentheses, or better yet, begin/end, like:
match (foo) with (* outer match *) | Some_pattern(x) -> begin match x with (* inner match *) | Another_pattern -> 0 | Different_pattern -> 1 | _ -> 2 end | Yet_another_pattern -> 3

Another possibility is to simply coallesce the two pattern matchings into one, like:
match (foo) with | Some_pattern(Another_pattern) -> 0 | Some_pattern(Different_pattern) -> 1 | Some_pattern(_) -> 2 (* notice the use of the discard * variable! *) | Yet_another_pattern -> 3

Other possibilities include wrapping the inner pattern matching in a let/in construct, an if/then construct, or make it a local function.

Pattern Matching on Other Types


Up until this point, Ive been using variant types to patten match on. But you can pattern match on just about any data type- including tuples and fundamental data types (ints, booleans, etc). We could have just as easily written our Fibonacci number function from Chapter 2 like:
let rec fib match x | 0 | 1 | i x = with -> 1 -> (fib (i-1)) + (fib (i-2))

60

Chapter 4. Variant Types and Pattern Matching


;;

Notice the use of constants in this example. We can also pattern match on tuples, using the comma operator in the pattern. So we might code up a length function on our complex type (which is a tuple of two oats, if you recall) like this:
let complex_length (x: complex_t) = match x with | (v, 0.) (* matchs complex numbers with no imaginary part *) | (0., v) (* matchs complex numbers with no real part *) -> v | (r, i) (* all other complex numbers match this *) -> sqrt ((r *. r) +. (i *. i)) ;;

Design Pattern: Use Tuples to Match Multiple Things One of the "limitations" of pattern matching is that you can only match a single value. What happens if you want to match multiple different things in the same pattern? The solution is to put the multiple different things into a tuple, and then pattern match on the tuple. As an example of this, lets consider the add function for a simplied number type, that only contains the original Int and Float constructors. We might write the add function like this:
let add a b = (* a and b are numbers *) match a, b with (* note: were matching on the tuple a,b! *) | (Int i), (Int j) (* both numbers are integers *) -> Int (i + j) | (Int i), (Float j) -> Float((float_of_int i) +. j) | (Float i), (Int j) -> Float(i +. (float_of_int j)) | (Float i), (Float j) -> Float(i +. j) ;;

Exceptions
Exceptions have become a standard feature in modern programming languages. They are a way to seperate out the error handling of a peice of code from the code itself- and a way to ensure that errors were handled somehow (even if it is just to print out an error message an exit). Naturally, Ocaml support raising (aka throwing) and catching exceptions. Exceptions in Ocaml work a lot like they do in Java, or Cs setjmp()/longjmp() functions- ow of control "jumps" directly from the raise to the nearest catch that handles the exception, where error handling takes place. Because Ocaml is a garbage collected language, complicated (and expensive) stack unwinding does not need to occur- Ocaml just drops everything on the oor and lets the garbage collector pick things up. This makes exceptions in Ocaml signigantly faster than exceptions in C++.

61

Chapter 4. Variant Types and Pattern Matching

Dening Exceptions
Exception are declared like variant types are- with one exception (pun not intended). While variant types can have multiple different tags, exceptions can only have one. The basic syntax for declaring an exception is:
exception Name [ of type ] ;;

Like tag names, exception names have to begin with a capital letter, and otherwise they follow all the same rules as other names. Some examples of declaring exceptions:
exception We_died;; exception File_not_found of string;; exception Parse_Error of int * int * string;; The first exception doesnt have any data associated with it- its a pure tag. The second and third have data associated with them, in one case a string and the other a tuple. Note that, like type definitions, new exceptions can only be declared at the top level, they can not be declared locally.

Note that exceptions dont have an inheritance heirarchy- all exceptions are created equal. There is no distinction, as in Java, between "run-time" and "normal" exceptions.

Raising Exceptions
To throw an exception, you use the raise operator. This is just raise followed by the constructor for the exception (just like allocating a new variant type). So you might get code like:
if x = 1 then raise We_died (* throw a We_died exception *) else if x = 2 then raise File_not_found(filename) (* throw a File_not_found exception * with filename (a string) as its * data. *) else raise Parse_error(column, lineno, filename) (* throw a Parse_error *)

Raising an exception is a expression, and thus can be done just about anywhere. Unlike in Java, functions that throw exceptions do not need to document what exceptions they raise. Its good style to actually document what exceptions a function raise, however.

Catching Exceptions
To catch a raised exception, you use the try/with construct, which looks like this:
try expr with[ | ] pattern
[ | pattern [ ... ] ] -> expr2 [ | pattern [ | pattern [ ... ] ] -> expr2]

62

Chapter 4. Variant Types and Pattern Matching


[...]

This should look familiar- the with syntax for which exceptions to catch is exactly the same as the syntax for matching a variant type is (much like the syntax for declaring and raising an exception is the same or similiar to the syntax for declaring or allocating a variant type). The exact same rules apply- you can declare local variables in the pattern matching of catching an exception, you can use the discard variable (_) to ignore parts of exceptions, and can even use the discard variable to catch all exceptions. So you might have do like:
try do_something filename with | We_died -> print_string "We died!\n" | File_not_found(fname) -> print_string ("File not found: " ^ fname ^ "\n") | Parse_error(_, _, _) (* ignore the data passed back *) -> print_strng "Parse error.\n" | _ -> print_string "Some other exception happened!\n" ;;

Another thing to note is that the entire try/with construct is itself an expression! This means you can drop try/with constructs in formulas, if/then statements, function bodies, values given to variables, etc.

63

Chapter 4. Variant Types and Pattern Matching

Design (Anti-)Pattern: Using Exceptions for Fast Exits Generally, exceptions should only be used for "exceptional" circumstances- cases that are (or should be) unexpected, and hard to deal with. But there is, in Ocaml, one design pattern which is common enough that I feel a need to point it out in which exceptions are used in "non-exceptional" circumstances- and that is as a fast way to exit out of a deep recursion. This works because Ocaml exceptions are lightning fast. The measurements Ive made say that raising and catching an exception is only 2-5 times more expensive than allocating a tuple (your mileage may vary). So the idea here is that instead of returning a tuple of error return code plus the real return value, and returning out of a deep recursion, throw an exception which is always caught to exit out of several levels in one fell swoop. The upside is obviously somewhat simpler coding, and better performance. The downside is that it corrupts the meaning of exceptional, and encouraging spaghetti code. I freely admit to using this (anti-)pattern, but I feel guilty about it. An example of this in action might be to detect overow in calculating Fibonacci numbers- if, at any point, we add two numbers and get a negative number, we know weve overowed. At which point, our fib function could return -1, like:
exception Overflow;; let fib x = let rec inner x = match x with | 0 -> 0 | 1 -> 1 | _ -> let rval = (inner (x-1)) + (inner (x-2)) in if rval < 0 then raise Overflow else rval in try inner x with | Overflow -> -1 ;;

Assertions
Ocaml has some support for design by contract, in the form of built-in assertions. The keyword assert acts like a function of type bool -> (), in that it evaluates a boolean value and return () if the value is true, and raises the exception Assert_failure. There are a couple of differences, though. First off, assertions can be disabled by compiling the code (with either ocamlc or ocamlopt) by adding the -noassert ag. This is the equivelent to compiling C code with the preprocessor symbol NDEBUG dened- the code is simply removed. This includes, by the way, the evaluation of the boolean value- so the asserted expression should not have side-effects. Second, the compiler adds in (automatically) the le name, line number, and column number as arguments to the exception. The code might look something 64

Chapter 4. Variant Types and Pattern Matching like this:


exception Assert_failure of string * int * int;; let assert b = if b then () else (* filename, lineno, and colno are supplied by the compiler! *) raise Assert_failure(filename, lineno, colno) ;;

The compiler doesnt remove code in one particular case- assert false always throws an exception, even in code compiled with -noassert. Note that the false can not be obfuscated: this code wont throw an exception if the -noassert ag is passed to the compiler:
let temp = false in assert temp

One other thing to note is that there is nothing special about the exception raised- it can be caught just like any other exception. Although you normally dont want to do that, it is occassionally useful for things like unit testing. Design Pattern: Use assert false for Impossible Patterns One of the most common uses of assert false is for "impossible" matches. For example, we might write a function to convert the number of a suit back into the suit itself like:
let suit_of_number n = match n with | 0 -> Spades | 1 -> Hearts | 2 -> Diamonds | 3 -> Clubs | _ -> (* this should never happen! *) assert false ;;

Here were assuming that the number being passed in came from the earlier example suitno, so it should always be in the range of 0 to 3. Numbers outside this range indicate a aw in the programming somewhere.

Invalid Arguments
Another common exception predened by Ocaml is the Invalid_argument exception, and its helper function invalid_arg. These are dened as:
exception Invalid_argument of string;; let invalid_arg str = raise Invalid_argument(str);;

The intention of this exception is to provide a common way for functions to report domain errors- i.e. invalid parameters. Note that, unlike assertions, the only way to remove invalid_arg calls from the code is with an editor. 65

Chapter 4. Variant Types and Pattern Matching

Invalid_argument vs. Assert_failed When should you use asserts and when should you use invalid_arg? Both exceptions, in some sense, signal a bug in the code. Some code, somewhere, is doing something it shouldnt. There is no clean answer to this, but my general rule of thumb is that if the buggy code can only be in the same module, use an assertion, while if the buggy code can be in a different module, use an invalid_arg. So, for example, the consistency of a data structure, whose type is exported as abstract, so that all creations and modications must be done within the given module, should be checked with assertions. If somehow the data structure became inconsistant then some code, somewhere in the current module, must be wrong. The theory here is that once the module is completely tested and debugged, the sanity checking is superuous and can be removed. The integer argument passed in to an exported function should not be checked with assertionsit should be explicitly checked and invalid_arg called if incorrect. Input from outside the program should never, ever, be checked with assertions!

An Extended Example: a Height Balanced Tree


One of the fundamental data structures is the binary tree- and variations on binary trees are used quite a bit in Ocaml programming. For those who havent seen a tree data structure since college, we provide a quick review of trees and big-O notation. If you feel condent of your knowledge here, you can skip the overview and go straight for the code.

An Overview of Binary Trees


A binary tree is a container structure, like lists and arrays- it holds other peices of data. The data the tree holds are called its elements. There are lots of different variations on the basic tree pattern, but the tree were implementing here is structured the following way. There are two cases of importance. The rst is the empty tree, i.e. the tree holds no elements. The second is the non-empty tree. In this case, the tree has a root element and two subtrees, a left subtree and a right subtree. The subtrees are themselves binary trees, and can be either empty or non-empty. The binary tree we are implementing is ordered- in that a total ordering can be done over all elements in the tree. We gaurentee that the root element of a tree is "larger than" all the elements in its left subtree, and "smaller than" all the elements in its right subtree. Thus, a walk down the tree represents a search pattern on the sorted list of the elements. If the tree is balanced, this pattern is that of a binary search. Thus, the number of elements we have to inspect, and the number of nodes we have to visit, to nd any element in a tree that holds N elements and is balanced, is (on average) k * log2(N), for some numeric constant k.

An Overview of Big-O Notation


So lets say that k, the constant multiple of how many nodes we have to search to nd any given element, is 2.0. Upon coding up our balanced tree search, we might nd that it takes 3.4 microseconds per node visited, with a constant overhead of 1.7 seconds. The total time itd take to nd a node in a balanced tree holding N elements would then be 6.8 * log2(N) + 1.7 microseconds. The precise constants involved here are dependent upon a wide range of factors- the precise machine the code is run on, how the code was compiled, etc. 66

Chapter 4. Variant Types and Pattern Matching What we can say generically is that nding a node in a tree with 65,536 elements in it will take about twice as much time as nding a node in a tree with only 256 elements. We say that the algorithm is O(log N). All algorithms with the same basic formula for the amount of time they take- a*(log N) + b are all of the same "order"we discard the constant factors a and b. So if one algorithm took 0.01*(log N) + 1 microseconds to complete, and another algorithm took 100*(log N) + 1,000,000 microseconds to complete, theyd both be O(log N) algorithms. Other examples- prepending an element to a list takes O(1), or constant time. It doesnt matter how many elements are in the list, it always costs about the same. Searching that same list takes O(N). Generally you have to search about N/2 elements to nd the one youre looking for- but we ignore the 1/2 constant factor. It takes about twice as long to search a list of 512 elements as it does a list of only 256 elements. "Fast" sort algorithms like quicksort or heapsort take O(N * log N) time, "slow" sort algorithms like insertion sort and bubblesort take O(N2 ) time.

Balancing Trees
Trees are nice data structures, because if theyre balanced they are fast (O(log N)) to insert, delete, or nd elements in. The problem is that they dont necessarily stay balanced unless you balance them. Every time you insert or remove an element from a tree, you might need to adjust the tree to keep it balanced. The core operation in balancing a tree is the rotate, which comes in left or right varieties:

A rotation then makes one branch "taller" while it makes the other branch "shorter". Almost all tree balancing algorithms can be seen as just applying rotations, and the differences between them are just when and how they detect that rotations should be applied to keep all branchs of the tree at about the same height. One of the simpler schemes for keeping trees balanced is height balancing. The height of a node is 0 if its a leaf node, or one more than the maximum of the heights of its two subtrees. For speed, we keep the height of the node as a member of the node itself, so we dont have to keep recalculating it. The height is then a measure of the maximum number of nodes that need to be traversed before nding an empty subtree. Its the maximum number of levels in the tree. The height then tells us when we need to rotate. We dont want the height of one subtree to be more than 1 greater than the height of the other subtree. If one subtree has a height of 7 (for example), and the other a height of 8, thats alright. But if one subtree has a height of 7, and the other has a height of 9, we should perform a rotate and make both subtrees have a height of 8. The gaurentee we can then make is that the longest path from the root of the tree to an empty subtree is no more than twice as long as the shortest path. The reason for this is obvious. If a tree has a height of, say, 10, it has to have at least one subtree with a height of 9 (the height of the tree, remember, is always one greater than the height of the taller subtree). The other subtree therefor cant have a height of less than 8. The height 9 subtree has a height 8 subtree, which has a height 7 subtree, and so on, until you get to a height 1 subtree which has empty trees as both of its subtrees. So the maximum number of nodes on that path is 10 nodes. This is obviously the longest path possible- if a longer path from the root to an empty subtree were possible, the height of the tree would be more than 10. 67

Chapter 4. Variant Types and Pattern Matching Now, lets look at the other subtree. It has a height of 8, and while it denately has one subtree with a height of 7, it could also have a subtree with a height of 6. The subtree with a height of 6 could have a subtree with a height of 4, which could have a subtree with a height of 2, which could have an empty subtree. The 10-8-6-4-2-empty path takes 5 nodes, and its obviously the shortest possible path. For a path to an empty node to be shorter, wed have to violate the constraint that no subtree is more than 1 level taller than its sibling subtree.

The Interface
We start with dening the interface. Well be implementing an association with our tree, also known, confusingly, as a map- but since the Ocaml standard library already has a module called Map (which uses advanced features I havent introduced yet), well instead call our module Assoc. An association is parameterized with two typesthe key type and the value type. Every element in the association has both a key and a value. So the following code should go into the le assoc.mli to dene the implementation:
type (key, data) t val val val val val val val val make: (key -> key -> int) -> (key, data) t find: key -> (key, data) t -> data option mem: key -> (key, data) t -> bool add: key -> data -> (key, data) t -> (key, data) t remove: key -> (key, data) t -> (key, data) t iter: (key -> data -> unit) -> (key, data) t -> unit fold: (key -> data -> a -> a) -> (key, data) t -> a -> a map: (a -> b) -> (key, a) t -> (key, b) t

Some notes on the implementation. First, notice we dene an association to be an abstract type- only functions within assoc.ml le will be able to see "inside" of it. We call it the simple name t, because most likely external functions will actually be calling it Assoc.t, which is much more descriptive. The make functions gives us a way to make an initially empty association (one with no elements), to which we can add things later. It takes one argument- a function, which takes two arguments both of whatever type we are using as a key, and returns an int. This is a standard comparison or ordering function, a common pattern used all over in Ocaml code, so a word about it is in order. We use it whenever we want to build up an ordered structure, like were currently doing. We also use it to sort lists and arrays, and to search them as well. The function should return less than 0 if x is "before" y, 0 if x is "equal to" or unordered with respect to y, and greater than zero if x is "after" y. The easy way to remember how this function works is that if its two arguments are integers, it works like x - y. Another way to look at it is that for any given comparison operator op, (compare x y) op 0 is the same as x op y. So (compare x y) < 0 is the same as x < 0, and (compare x y) != 0 is the same as x != y. The reason we use a passed in comparison function rather than just using comparison operators, is that specialized comparison functions are generally faster than calling the generic compare. Next up is our find function. Given any key, it nds the data element associated with that key. Note that it returns a data option as a type- it returns None if it cant nd the required key, and Some x if the key is associated with the data x. The function mem returns true if the given key has some data value associated with it in the given association, or false if it doesnt.

68

Chapter 4. Variant Types and Pattern Matching Next comes the function add, which adds a new key/value association to the datastructure. Note that this function doesnt modify the old structure- instead it returns a new structure with all the elements of the old structure and the new association. As we shall see, the new structure shares the bulk of its members with the old structurewhich is safe, as the old structure cant be modied anyways. If the key being added already exists, the new association replaces the old in the new structure. The function remove is the opposite of add- it returns a new structure with all the associations of the old less one association- the association of the given key. Next we start moving into more interesting functions. These functions iterate over all the associations of the structure doing something to all of them. These sorts of functions are often called "comprehensions" (Im not really sure why)- or, in the case of a tree, "tree comprehensions". You might also hear of "list comprehensions", "array comprehensions", etc. They are all basically variants of the Visitor pattern, if you know your design patterns. The rst of which is the most simple. Given a function which takes both a key type and a data type of whatever the structure holds and returns a unit value (Ocamls version of void, remember), it calls the function on every association held by a structure. This is usefull for printing off the associations, for example. The next comprehension is fold. This function accumulates a value over the entire structure. The rst argument is the accumulation function- it takes an key and the associated data from the structure and the current accumulation and returns the new accumulation. Note that the type of the value you are accumulating does not need to be the same type as either the key or data types of the structure! It then takes the structure to accumulate over, and the initial value, and returns the nal value. So if we call the fold function and pass in the arguments the function f, a structure which holds the associations k1/d1, k2/d2, ..., kn/dn, and the initial value a, the fold function returns the value: (f kn dn (... (f k2 d2 (f k1 d1 a))...)). The last function well implement is map. Now, this functions coverts the data elements of an association into a new type, but keeping the old keys.

An Example Use of an Association


Sometimes, the best way to learn how (and why) an interface is put together that way, is to look at an example of how it is used. So here is a user of Associations to make a sparse vector library. The code isnt very heavily annotated, however. For those of you who dont know or have forgotten your linear algebra, a vector is basically just an array or list of numbers. In many cases, however, most of those numbers will be 0., so we can have a great savings in memory utilization if we only store the non-zero elements. So we build our vector from an association of ints to oats. If the association has a oat associated with a given int, that means that the int is the index of a member of the vector which is non-zero, and the oat is the value it is set to. So our minimal interface here is the ability to make a vector, get the current value of any given index (the oating point number associated with it if its set to nonzero, or zero otherwise), set a given index to a given value, unset the given index (set the value to 0., and free any memory associated with the element). Then, as an example of using the comprehensions, I supply a print routine (which prints the vectors non-zero members out to stdout using Assoc.iter), a length routine (the length of a vector is the square root of the sum of the squares of all its memberswe use Assoc.fold for this), and a scale routine (if you multiply a vector by a real, that means you multiply all the members of that vector by the given real- we use Assoc.map to do this).
type vector_t = (int, float) Assoc.t;; let intcompare (x: int) y =

69

Chapter 4. Variant Types and Pattern Matching


if x < y then -1 else if x > y then 1 else 0 ;; let make () : vector_t = Assoc.make intcompare;; let get (v: vector_t) i = match (Assoc.find v i) with | None -> 0. | Some d -> d ;; let set (v: vector_t) i d : vector_t = Assoc.add i d v;;

let unset (v: vector_t) i : vector_t = Assoc.remove i v;; let print (v: vector_t) = let locprint k d = print_string ("(" ^ (string_of_int k) ^ "," ^ (string_of_float d) ^ ") ") in let _ = print_string "[" in let _ = Assoc.iter locprint v in let _ = print_string "]\n" in () ;; let length (v: vector_t) = let loclength _ d s = s +. (d *. d) in sqrt (Assoc.fold loclength v 0.) ;; let scale (v: vector_t) a : vector_t = Assoc.map (fun d -> d *. a) v ;;

The Implementation of Assoc


And now for the moment youve all been waiting for- the actual implementation of the Assoc module. This code should go into the le assoc.ml. First up, we actually dene our types:
type (key, data) node_t = | Branch of int * key * data * (key, data) node_t * (key, data) node_t | Leaf ;; type (key, data) t = (key -> key -> int) * (key, data) node_t ;;

We split the basic type into two parts- the top part is just a place to stash our comparison function, the real data is held in the nodes. Now, we do the make function. This is easy, all it does is make a tuple of the comparison function being passed in and the Leaf constructor representing an empty tree: 70

Chapter 4. Variant Types and Pattern Matching


let make cmpfn = cmpfn, Leaf;;

We dont need to explicitly set any types, thats done for us by the interface. And now, the find function. The algorithm here is simple. We look at each node. If its a Leaf, weve bottomed out and the key were looking for doesnt exist in the association, so we can just return None. If its not a leaf, we compare key of the root element using the comparison function that got passed into make. If the comparison returns 0, were done- weve found the key were looking for and we can just return the associated data passed to the Some constructor. If the comparison returns less than zero, we know the key is somewhere in the left subtree, so we recursively search the left subtree. If the comparison returns greater than zero, we know to recursively search the right subtree. Sooner or later, well either nd the key were looking for, or bottom out and hit a Leaf node, in either case well return back out of the function. Two things to note is that the core recursion happens in an inner function, and we use the fact that our top level structure is a tuple.
let find k (cmpfn, root) = let rec innerfind node = match node with | Leaf -> None (* Didnt find the key *) | Branch(_, k, d, left, right) -> let rval = cmpfn k k in if rval == 0 then Some d (* We found the key we were looking for *) else if rval < 0 then innerfind left else innerfind right in innerfind root ;;

The function mem works just like find except that we return true or false instead of Some d or None:
let mem k (cmpfn, root) = let rec innermem node = match node with | Leaf -> false | Branch(_, k, d, left, right) -> let rval = cmpfn k k in if rval == 0 then true else if rval < 0 then innermem left else innermem right in innermem root ;;

Now we take a slight detour. The next several functions will be aided by a judicious selection of helper functions- little functions that just do something simple. The two I need are height, which returns the height of a node, and imax, which returns the maximum value of two integers:
let height node = match node with | Leaf -> 0 | Branch(h, _, _, _, _) -> h

71

Chapter 4. Variant Types and Pattern Matching


;; let imax (a: int) b = if a >= b then a else b ;;

This next function is where most of the real work gets done. Our general pattern for the functions that modify the tree (add and remove) is that we recurse down the tree looking for the point to do our modication. Once the modication is made, we return back up the tree creating new nodes as we go- the new nodes hold the same key and data as before, but replaces one subtree with a new value. So we create a function that, given a key, a data, and the two children, creates a new node. While were creating the new node, we do any rotations that are needed to keep the height balanced. In either case, we return the root node we just created.
let makenode key data left right = let lh = height left and rh = height right in if (lh > (rh + 1)) then (* left subtree is too tall, rotate right to rebalance * the tree. We pattern match on the left node to pick * it apart- it should never be a leaf! *) match left with | Branch(_, lk, ld, ll, lr) -> let h = 1 + (imax (height lr) rh) in let newnode = Branch(h, key, data, lr, right) in let newlh = 1 + (imax (height ll) h) in Branch(newlh, lk, ld, ll, newnode) | Leaf -> (* this should never happen *) assert false else if (rh > (lh + 1)) then (* right subtree is too tall, rotate left to rebalance * the tree *) match right with | Branch(_, rk, rd, rl, rr) -> let h = 1 + (imax lh (height rl)) in let newnode = Branch(h, key, data, left, rl) in let newrh = 1 + (imax h (height rr)) in Branch(newrh, rk, rd, newnode, rr) | Leaf -> (* this should never happen *) assert false else (* The tree is more or less balanced- no rotations * are needed *) let newheight = 1 + (imax lh rh) in Branch(newheight, key, data, left, right) ;;

Inserting a new node becomes easy once we have the above makenode function. Inserting a node into an empty tree is simple, we just replace the leaf node with a branch node containing the new key, the new data, leaf nodes as both of its subtrees, and a height of 1. If the tree is not empty, then we have three cases. We could be replacing the data in the root node of the tree. This happens when the new key and the root node key compare equal (the comparison function returns 0). In this case, we 72

Chapter 4. Variant Types and Pattern Matching just allocate a new root node, with the same height and same subtrees, and the new key and new data. Otherwise, we recursively insert the new key and new data into either the left or right subtrees, depending upon wether the new key is larger than or smaller than the root node key. Inserting the new key and data into the subtree will return a new subtree, so we have to allocate a new root node, with the same key and data, and one new subtree. In this case we might also have to balance the tree. So this is where we call makenode. Note that in all cases we end up allocating a new root node for the tree. So we always just return the new root node.
let add key data (cmpfn, root) = let rec inneradd node = match node with | Branch(h, k, d, l, r) -> let cval = cmpfn key k in if cval = 0 then Branch(h, key, data, l, r) else if cval < 0 then makenode k d (inneradd l) r else makenode k d l (inneradd r) | Leaf -> Branch(1, key, data, Leaf, Leaf) in cmpfn, (inneradd root) ;;

The next function is remove. It works a lot like add does with one exception. When we remove a key, value pair from a tree that has two non-empty subtrees, we need to successfully join the two subtrees into a common tree. We do this with recursion- we pick the higher of the two subtrees, lets assume its the left subtree- call it tree a, and the right subtree tree b. The root node of tree then becomes the root node of the joined trees. Tree as left subtree remains the same, but the right subtree becomes the result of joining as right subtree with b. If tree b were higher, bs left would get joined with a. Thus we sort of "zipper" the two trees together. We do one other optimization- if the key were trying to delete doesnt exist, we throw a special exception to bounce us out of the deep recursion. If we throw the exception, we dont allocate anythingjust return the original tuple.
exception Key_doesnt_exist;; let remove key tree = let cmpfn, root = tree in (* tree is a tuple, remember- I can do this *) let rec join a b = match a, b with | Leaf, _ -> b | _, Leaf -> a | Branch(ah, ak, ad, al, ar), Branch(bh, bk, bd, bl, br) -> if (ah >= bh) then makenode ak ad al (join ar b) else makenode bk bd (join a bl) br in let rec inner_remove node = match node with | Leaf -> raise Key_doesnt_exist;; | Branch(_, k, d, l, r) -> let cval = cmpfn key k in if cval = 0 then join l r (* Remove the current key, data pair *) else if cval < 0 then makenode k d (inner_remove l) r else

73

Chapter 4. Variant Types and Pattern Matching


makenode k d l (inner_remove r) in inner_remove root ;;

Now we come to the comprehensions. Visiting every node in the tree is easy if done recursively. The structure here is common for all the comprehensions- to traverse an empty tree is a freebie, there is no tree to traverse. If the tree is not empty, it has a root element and a left and right subtrees (which may or may not be empty). To traverse the tree, we rst recursively traverse the left subtree. Then we visit the root node, and then we recursively traverse the right subtree. The function iter is the most obvious example of how this works, as "visiting" a node means just calling the given function on the nodes key and data. So it works like this:
let iter f (_, root) = let rec inner_iter node = match node with | Leaf -> () (* Nothing to do, just return unit *) | Branch(_, k, d, l, r) -> (* traverse the left subtree *) let _ = inner_iter l in (* visit the root node *) let _ = f k d in (* traverse the right subtree *) inner_iter r in inner_iter root ;;

The function fold works just like iter, except we have an accumulation value we pass around. This isnt that big of a problem, as we returned the unit value from the recursive function in iter- we can instead return the resulting accumulation value in fold:
let fold f (_, root) init = let rec fold_inner accu node = match node with | Leaf -> accu (* empty tree *) | Branch(_, k, d, l, r) -> (* First, we recursively calculate the accumulation * over the left subtree. *) let accu = fold_inner accu l in (* Add the current node into the accumulation *) let accu = f k d accu in (* Recursively calculate the accumulation over the * right subtree. *) fold_inner accu r in fold_inner init root ;;

The map function is a bit different. We could implement it with a quick call to fold, where the value we are accumulating is the new tree, and we add a value by calling add. If we were changing the keys as well, this would probably be the best way to do it. But the problem here is that adding an element is an O(log N) operation, and we would have to perform it N times, meaning the overall cost of implementing map that way is O(N log N). The key insight is that were keeping both the keys and the 74

Chapter 4. Variant Types and Pattern Matching ordering of the keys in the result. This means the resulting tree can have the exact same "shape" as the original tree. First we map the left subtree, then we map the right subtree, and then we create a root node with the same key and the same height (the new subtrees will have the same height as the old subtrees), but with the new data and the new subtrees, to become the root node of our new tree:
let map f (cmpfn, root) = let map_inner node = match node with | Leaf -> (* An empty tree maps onto the empty tree *) Leaf | Branch(h, k, d, l, r) -> Branch(h, k, (f d), (map_inner l), (map_inner r)) in cmpfn, (map_inner root) ;;

Records
The elements of a tuple are identied only by position. This makes it easy to make them without having a declared type. Ocaml also has records, which are much more like C/C++s struct concept. In OO terms, records are objects that contain only public named member variables. They are tuples where the elements are identied by name and not position. Ocaml calls the members of a structure elds, and so shall. Before you can use a record, you have to declare its type. The type of a tuple is delineated by curly braces- { and }. Within the curly braces is a list of elds- each eld is given a unique name, and a type- the type follows the name with a colon (:). Fields are seperated by semicolons (;). Field names follow the same rules as variable and function names- most importantly, they start with a lower case letter. An example structure, declaring a 3D point, might look like:
# type point3d = { x : float; y : float; z : float };; type point3d = { x : float; y : float; z : float; } #

One important point at this juncture- for reasons that will become apparent later, the eld names of all structures are in the same namespace. This means that if one structure type has a eld named "x", no other visible structure can have a eld with the same name. This causes a rather severe namespace pollution problem- the more structures you have visible, the more likely a name collision will be. Because of this, I tend to limit the visibility of my structure denitions to a single module, and export their types only as abstract types (leave the actual denition out of the .mli le). Note that structure eld names are in a different name space than variables and functions are- you can have a variable with the same name as a structure eld with no problem. Allocating an instance of a structure is similiar. You use the same curly brackets { and } to delineate the allocation. Within the curly brackets are a series of expressions, each a eld name of the structure, followed by an equals sign (=), followed by an expression of the proper type which is the elds value. For example, to declare a point3d structure, we would:
# let p1 = { x = 1.; y = 2.; z = 3. };; val p1 : point3d = {x = 1.; y = 2.; z = 3.} #

75

Chapter 4. Variant Types and Pattern Matching Since the elds are named, it doesnt matter which order you declare them in:
# let p2 = { z = 3.; x = 1.; y = 2. };; val p2 : point3d = {x = 1.; y = 2.; z = 3.} #

But note that every eld of the structure has to be given a value, and no eld can be given a value twice:
# let p3 = { y = 2.; z Some record field labels # let p4 = { x = 1.; y The record field label x #

= 3. };;
are undefined: x

= 2.; z = 3.; x = 1.};;


is defined several times

Fields of a structure can be accessed using a ., in a manner similiar to structure eld access in C/C++, Java, etc. For all of you C/C++ programmers out there, remember that everything is a reference/pointer in Ocaml. So . in Ocaml is more strictly analogous to the -> operator in C/C++, in that it contains an implicit dereference. For all of you Java programmers, the . in Ocaml mostly acts like the . operator in Java. So to access the y eld of the variable p1, we would just write p1.y. We could dene a euclidean length function for out point3d type like:
# let len3d pt =

sqrt ((pt.x *. pt.x) +. (pt.y *. pt.y) +. (pt.z *. pt.z)) ;;


val len3d : point3d -> float = <fun> # len3d p1;; - : float = 3.74165738677394133 #

By default, structures, like tuples and variables, are immutable. Once the elds of a structure have been given values, they cant be changed. Like tuples, the solution is to just allocate a new structure with all the same values except for the one(s) you want to change. Ocaml provides a convient way of expressing this pattern, the with keyword. Instead of specifying all the values individually, you start the allocation with the variable (or an expression in parentheses) that the default values come from, followed by the with keyword and those elds that get non-default values. Some examples make this more clear. Here, p5 is p1 but with an x value of 5.:
# let p5 = { p1 with x = 5. } ;; val p5 : point3d = {x = 5.; y = 2.; z = 3.} #

You can override multiple elds by seperating them with semicolons (;), as if it were a normal declaration:
# let p6 = { p1 with x = 5.; z = 7. };; val p6 : point3d = {x = 5.; y = 2.; z = 7.} #

76

Chapter 4. Variant Types and Pattern Matching Expressions can replace the simple variables in a with statement, so long as theyre enclosed in parentheses for grouping. For example, assume we had an add3d function like:
let add3d p1 p2 = { x = p1.x +. p2.x; y = p1.y +. p2.y; z = p1.z +. p2.z } ;;

We could use the output of this function for the default values for another variable like:
# let p7 =

{ (add3d p5 p6) with x = 1. } ;;


val p7 : point3d = {x = 1.; y = 4.; z = 10.} #

This is one of the advantages structures have over tuples, the ability to "future-proof" code. Allocating structures using with allows for new members to be added to the structure in the future without necessarily breaking the code. The advantage of tuples over structures is that you dont have to declare the type of a tuple beforehand. You can pattern match on structures, just like you can on tuples. As you have probably guessed, the patterns you match on look amazing like how you allocate a structure, except that you need either constants or variable names instead of arbitrary expressions. The one important difference is that you do not need to specify all elds in a pattern- unspecied elds are simply discarded. So you might write an is_2d function (that returns true if at least one eld of a point3d is 0.) like:
let is_2d pt = match pt with | { x = 0. } | { y = 0. } | { z = 0. } -> true | _ -> false ;;

Exercises
1. Write the axpy function for our Assoc based vector library. This is function that takes a oating point value a and two vectors x and y and calculates z = (a*x) + y. For those who have forgotten your linear algebra, this just means the ith member of z is just the ith member of x times a, plus the ith member of y, or: zi = (xi * a) + yi. Note that if the ith element of both x and y are 0 (unset), then the ith element of z should also be 0 (unset)- but if either x or y has the element being non-zero (set), then z should have the value set. Note that the resulting function will be O(N log N). 2. Write a sparse matrix library using the Assoc module (hint: you can have an association of associations). Include functions to make an empty matrix, set a value in the matrix, get a value in the matrix, unset a value in the matrix, print the matrix, scale the matrix (all elements in the matrix get multiplied by a constant), and transpose the matrix.

77

Chapter 4. Variant Types and Pattern Matching 3. Write a set module, to contain an arbitrary set of objects, holding them in a height balanced tree. Note that, like Map, the standard library also has a Set module, so call the module MySet instead. The MySet module should implement the following interface:
type a t (* abstract set type *) val val val val val val val make : (a -> a -> int) -> a t (* make an empty set *) find: (a -> int) -> a t -> a contains: (a -> int) -> a t -> bool add: a -> a t -> a t remove: (a -> int) -> a t -> a t iter: (a -> unit) -> a t -> unit fold: (a -> b -> b) -> a t -> b -> b

Two things to note: rst off, where in the Assoc module we passed in a key, in the set module we pass in a function that returns less than 0 if the element is larger than the one were looking for, greater than 0 if the given element is smaller, or 0 if the given element is the one were looking for. Also, we left off map: why? 4. Rewrite the quaternion library from Chapter 3, except use a structure instead of a tuple. What are the disadvantages and advantages of each?

Notes
1. If you dont know what Duffs Device is, dont worry about it. You shouldnt use it- even in C/C++. Its lack is one of many advantages Ocaml has over C/C++.

78

Chapter 5. Lists and Recursion


Dont tug on that. You never know what it might be attached to. Dr. Buckaroo Banzai

Linked Lists
Another data structure Ocaml provides are lists. In addition to demonstrating more of the power of pattern matching, lists also introduce the fundamental looping construct of Ocaml- recursion.

Introducing Ocaml Lists


Lists are delineated using square brackets- [ and ]. Members of the list are seperated by semicolons (;). So, for example:
# [1;2;3];; - : int list = [1; 2; 3] #

The rst thing to notice is that lists use the same universal type notation as do variant types. We can then speak of an int list (a list of ints), a a list (a list of type a), or even horridly complicated types like a ((int, float) Assoc.t * string * int) list (a list of tuples containing an association from ints to oats, a string, and an int). An important point here: a list can only hold one type, ever! You can not, for example, add a oating point value to a list of ints. If the list is a xed length, consider using a tuple instead- or combine tuples and lists. So, for example, if you need an int followed by several oats, you should use a int * (float list) tuple- a tuple with two elements, an int and a list of oats. Use this instead of trying to make the rst element of the oat list an int. If you really need to mix types, use a variant type. So you might have something like:
type my_variant_type = | Int of int | Float of float | Char of char ;; let my_variant_list = [ Int(3); Float(3.14); Char(p) ];;

The type of my_variant_list would then be my_variant_type list. Another thing to note is that the members are seperated by semicolons, not commas! Commas are still used to create tuples- so you can create lists of tuples:
# # #

[1,2,3];;
: (int * int * int) list = [(1, 2, 3)]

[1,2,3;4,5,6];;
: (int * int * int) list = [(1, 2, 3); (4, 5, 6)]

Note that the rst list is a list of one element, a tuple with three ints. The second list is a list of two elements, each a tuple of three ints. Two square brackets with nothing except whitespace between them represents the empty list:
# [ ];; - : a list = []

79

Chapter 5. Lists and Recursion


#

Note that the empty list has a type of a list- since it doesnt hold anything, the type of the things it doesnt hold can be any type. The previous sentence does, in fact, parse. Also, any amount of whitespace includes no whitespace what so ever- in fact, no whitespace between the brackets is the norm. The :: (thats two colons) operator can be used to prepend a new element to a list. The expression to the left of the :: operator is the element to prepend to the list. The expression to the right is the list to prepend:
# let orig_list = [4;5;6];; val orig_list : int list = [4; 5; 6] # 3 :: orig_list;; - : int list = [3; 4; 5; 6] #

Since the resulting type from the :: operator is itself a list, it can be applied multiple times to add several elements to a list. As an operator, :: binds to the rightthat means that a :: b :: c :: lst is parsed like a :: (b :: (c :: lst)). For example:
# 1 :: 2 :: 3 :: orig_list;; - : int list = [1; 2; 3; 4; 5; 6] #

We start with the list [4;5;6]. Then we prepend 3 to the list, getting [3;4;5;6]. Then we prepend 2 to the list, and then 1. In fact, you dont have to start with a list containing any elements at all:
# 1 :: 2 :: 3 :: [];; - : int list = [1; 2; 3] #

This last example demonstrates that there is no difference between [1;2;3] and
1::2::3::[] except the amount of typing.

The @ operator can be used to concatenate two lists:


# [1;2;3] @ [4;5;6];; - : int list = [1; 2; 3; 4; 5; 6] #

The expressions on both sides of the @ operator must be lists of the same type (no concatenating a list of ints to a list of oats!). Using the @ operator, we can append an element onto a list by simply concatenating the original list onto a list containing only the element we want to append, like:
# [1;2;3;4;5] @ [6];; - : int list = [1; 2; 3; 4; 5; 6] #

Pattern Matching on Lists


Pattern matching is the primary way lists are read back (pattern matching is usefull for more than just variant types). Most functions that manipulate lists do so with pattern matching.

80

Chapter 5. Lists and Recursion The rst pattern to understand is the empty list pattern- []. When used in a pattern to be matched, this expression only matches the empty list. So, for example, we could write a function that returns true if a list is empty, or false if not, like:
# let is_empty lst =

match lst with | [] -> true | _ -> false ;;


val is_empty : a list -> bool = <fun> # is_empty [];;
- : bool = true # is_empty [1;2;3];; - : bool = false # is_empty [1.;2.;3.];; - : bool = false #

The rst point to make is that the _ discard pattern can be used to match entire lists as well- we use it here to make a "default" case. The second thing to notice is the type of the is_empty function- a list -> bool. This function works on lists of all types. You dont have to do anything special to get a high degree of generality from list functions- this type is common for list functions that just operate on the structure of a list and dont inspect the elements of the list. We demostrate this by calling the function with both a list of ints and a list of oats. You can also use the :: operator in forming patterns. The :: operator in patterns works just like it does everywhere else- the left hand side is a single element, and the right hand side is the rest of the list. So, a very common pattern to get the rst element of a list would look like:
# let first lst =

match lst with | h :: t -> h | [] -> invalid_arg "first" ;;


val first : a list -> a = <fun> # first [1;2;3];; - : int = 1 #

Here the pattern h :: t assigns h the rst element of the list (the Head of the list), and t the rest of the list (the Tail of the list). For all you Lisp programmers out there, h is the cdr of the list, and t is the car of the list. As an editorial note, I considered using the variable names first and rest here, as I think those names are more descriptive that h and t, or head and tail or cdr and car. But h and t are the standard names used by Ocaml programmers in this pattern, so I decided to keep to convention. But I will happily use first and rest in code that doesnt have as strong of conventional names as this code does. Since we dont need t, we could simply replace it with the discard variable, like:
let first lst = match lst with | h :: _ -> h |[] -> invalid_arg "first" ;;

You can use the discard variable to discard parts of lists as you desire. To do a rest function, that returns the rest of the list, we could do: 81

Chapter 5. Lists and Recursion


# let rest lst =

match lst with | _ :: t -> t | [] -> invalid_arg "rest" ;;


val rest : a list -> a list = <fun> # rest [1;2;3];; - : int list = [2; 3] #

You can use the :: operator multiple times in a pattern to "go deeper" into a list. To return a tuple of the rst three elements of a list, you might do:
#

let first_three lst = match lst with | first :: second :: third :: rest -> (first, second, third) | _ -> invalid_arg "first_three"

;;
val first_three : a list -> a * a * a = <fun> # first_three [1;2;3;4;5];; - : int * int * int = (1, 2, 3) #

Here, first, second and third match the rst, second, and third elements of a list (respectively). The variable rest isnt needed, and could probably be replaced by the discard variable, but I wanted to point out that in a chain of :: in a pattern, like this, the far right hand variable always gets the rest of the list. You could remove the rst three elements from the list like (notice the use of multiple discard variables):
# let remove_three lst =

match lst with | _ :: _ :: _ :: rest -> rest | _ -> invalid_arg "remove_three" ;;


val remove_three : a list -> a list = <fun> # remove_three [1;2;3;4;5;6];; - : int list = [4; 5; 6] #

You can also use the [] pattern on the right hand side of a :: operator (or a chain of them) to match xed length arrays. For example, if you wanted a function that returned true if the array was exactly one element long, you could write:
let is_unit_length lst = match lst with | _ :: [] -> true | _ -> false ;;

You could write a function that turned an array of exactly three elements into a three element tuple like:
let three_list_to_tuple lst = match lst with | a :: b :: c :: [] -> (a, b, c) | _ -> invalid_arg "three_list_to_tuple" ;;

82

Chapter 5. Lists and Recursion

You can also use [ and ] around patterns to match constant length lists, just like you can to declare constant lists. Our function to convert a three element list to a three element tuple could have been written:
let three_list_to_tuple lst = match lst with | [a; b; c] -> (a, b, c) | _ -> invalid_arg "three_list_to_tuple" ;;

Here, a is the rst element, b is the second element, and c is the third. You can not use the @ operator in pattern matching. This makes sense, if you think about it- where should ocaml split a list given a pattern like start @ end? We know that the :: operator in a pattern is only splitting off one element at a time, we dont know any such thing with the @ operator.

Efciency of List Operations


The rst thing to know about the efciency of list operations is how lists are constructed. Each element in the list is allocated a list node- a two-word structure analogous to the type a list = a * (a list);;. The rst word of the list node is the reference to the value of that node (either a reference to a boxed type, or the value of an unboxed type), the second word is the pointer to the rest of the list. The special NULL pointer signies the end of the list. You cant implement this directly in Ocaml, but its basically the same as singly linked lists youve seen in other languages. So the empty list is just the NULL pointer. A list with one element in it- say [ 6 ], has one two-word structure allocated for it. The rst word of the structure holds the value 6, and the second word the NULL pointer. A three element list takes three of the two-word structures. One important consequence of lists being singly linked is that you can not got backwards in them- only forwards. Lists are one way. The second thing to know about Ocaml lists is that they are immutable. Once you create a list node and assign its values, you cant change them. Note that this creates opportunities for optimizations. For example, consider the following code:
let orig_list = [6;7;8];; let list1 = 1 :: 2 :: 3 :: orig_list;; let list2 = 4 :: 5 :: orig_list;;

In this example, the elements of orig_list are shared between orig_list, list1, and list2. This can lead to great efciencies in memory utilization over the classic mutable linked lists- here we have three lists with a total length of 14 between them (orig_list has a length of 3, list1 a length of 6, and list2 a length of 5). But there are only eight list nodes between them. This ability to share the latter parts of lists explains why Ocaml lists are singly linked and unidirection. The rst element of orig_list is also the fourth element of list1 and the third element of list2. What is the previous element of this list- is there no previous element, as in orig_list? Or is the previous element the third element of list1? Or the second element of list2? This also explains why lists are immutablechanging orig_list in this case would also change list1 and list2 as collateral damage. The combination of singly linked and immutable allows Ocaml to make the :: operator very cheap. All it needs to do is allocate a two-word block of memory (about ve 83

Chapter 5. Lists and Recursion simple instructions in the common case, thanks to Ocaml using generational copying garbage collection), and then write the two words out. The fact that lists are singly linked means that to access the Nth element of a list requires the code to walk the rst N elements of a list. So accessing elements at or near the front of the list is cheap, but accessing elements towards the rear of the list is expensive. As a general rule, each :: operator in the pattern, or each element in the xed length list, is one step. So matching the pattern h :: t or [ a ] is one step. Matching the pattern a :: b :: c :: t or [ a ; b ; c ] takes three steps (and is about three times more expensive). The disadvantage of having lists being singly linked and immutable is that the @ operator is signigantly less efcient. In an imperitive language, where we could modify the list, all wed need to do is assign a new value to the pointer word of the last element. We cant do that in Ocaml. So what Ocaml is forced to do is allocate a whole new list, with the same elements as the original rst list, so that it can assign the last pointer to be the second list. In other words:
let new_list = [1;2;3] @ some_other_list;;

is the same as:


let new_list = 1 :: 2 :: 3 :: some_other_list;;

For three elements, this isnt such a big deal. But consider:
let new_list = a_really_long_list @ some_other_list;;

If a_really_long_list were, say, a million elements long, wed have to allocate a whole new list of one million list nodes- which would be very expensive both in terms of memory (we just allocated another two million words, or eight to sixteen megabytes of memory) and in terms of computation. This is important enough I want to call it out explicitly:

Efciency Concerns with @ Operator


Beware of using the @ operator to append lists. It has to allocate a whole new list to replace the left hand list- an O(N) operation. Its not quite the case that all uses of the @ are wrong, just use the operator with caution (and strongly consider using another data structure).

At this point youre probably thinking something along the lines of "Well, that was dumb! Why didnt they use old fasioned lists you can change, and avoid the costs of duplicating lists for the @ operator?" The thing to know is that you can almost always avoid the @ operator (Ill show you some tricks on how to avoid it later on in this chapter). So its not as bad a limitation as you might think. The second thing to know is that having the lists be mutable doesnt mean you dont have to copy them- in fact, copying must often be done in order to enforce immutability or encapsulation. The problem is like this: you have an object that has a list internal to it. You want to be able to return the list as the result of some function reporting on internal state. But if the list is mutable, code external to the object can change the list, thus changing the internal state of the object. This violates encapsulation- now how the object works internally is important to code outside of the object, because how it responds to the list being modied in a way the object doesnt see is important. Violating ecapsulation creates bugs and fragile, hard to change code. Overall, Id say that immutable, singly linked lists are a wash for performance over mutable, doubly linked lists. What you lose in the straight-aways you gain back in the curves. If you have to occassionally copy a list "unnecessarily" in some places, you dont have to copy the list in others. But what Ocaml gains from this trade off is a vast increase in the correctness of the code- its much easier to verify code correct. 84

Chapter 5. Lists and Recursion

Mixing Pattern Matching


You can mix and match pattern matching on different things to your hearts content. For example, you can match multiple lists simultaneously using a tuple of lists, like:
let first_pair lst1 lst2 = match (lst1, lst2) with | (a :: _), (b :: _) -> (a, b) | _ -> invalid_arg "first_pair" ;;

You can also mix and match pattern matching on variant types with lists, like:
type number_t = | Int of int | Float of float ;; (* Given a list of number_ts, returns the first element as a float *) let first_number lst = match lst with | Int(i) :: _ -> float_of_int i | Float(f) :: _ -> f | [] -> invalid_arg "first_number" ;;

Note that pattern matching, include the comma operator for matching tuples and the :: operator for matching list elements, bind from left to right, even if you mix them. This is just like how + and - work. This means you regularly need to use parentheses to force the right ordering. For example, the pattern a_first :: a_rest, b_first :: b_rest without any parentheses binds like ((a_first :: a_rest), b_first) :: b_rest. The type of the pattern is ((a list) * b) list. You have a list of tuples, the rst element of each tuple being a list. This is almost certainly wrong- was was probably meant was a tuple of two lists, i.e. a type of (a list) * (b list). To match this type, you need to use parentheses to force the correct binding- the correct pattern is (a_first :: a_rest), (b_first :: b_rest).

Circular Lists
You can use the let rec construct and the :: operator to construct circular lists- lists where the last element has the rst element as its next element:
# let rec infinite_list = 1 :: 2 :: infinite_list;;
val infinite_list : int list = [1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; ...] #

Note that although this list prints out like its a very long list, it still only takes up two list nodes of memory. Also note that on very long lists, Ocaml tends to omit the back portion of the list, replacing it with an ellipses (...). It does this to all long lists, not just innite lists. 85

Chapter 5. Lists and Recursion Ive never had a need to do anything like this in real code, but the capability is there. Its interesting in that it shows that let rec works for variables as well as for functions.

Recursion as Looping
Introducing lists shows an interesting lack in the language constructs Ive given you up until this point. Consider: how would you write a function that, given a list and an index, returns that element at that index? For xed, small indicies, you could just pattern match out that far- but this quickly becomes unwieldy (consider what the function to return the 20th element, or the 100th element, of a list might look like!). At this point, the lack of any looping constructs become obvious- I havent introduced any. No for loops, no while loops, nothing. Its time to introduce Ocamls notion of looping. The punch line here is that Ocaml doesnt have looping constructs (well, it does have a for loop construct, that Ill introduce in a later chapter- but dont bother ipping ahead, its severely constrained and limited in what it can do). Instead, Ocaml uses recursion to perform looping. The pattern is very basic- handle the current element, and recursively call yourself to handle the rest.

Example: Walking a List


So lets consider our nth function- the one that can return the element at any index in a list. We could write it just like:
# let rec nth lst idx =

if idx < 1 then invalid_arg "Invalid index passed to nth" else match lst with | h :: t -> if idx == 1 then h else nth t (idx - 1) | [] -> invalid_arg "Too short a list passed to nth" ;;
val nth : a list -> int -> a = <fun> # nth [1;2;3;4;5;6;7;8] 5;; - : int = 5 #

An index of less than one is only possible if the caller passes in a 0 or negative indexin that case we just throw an exception. Otherwise, if the list isnt empty, we use the :: operator in a pattern to be matched to pull off the rst element. Here we want both the rst element of the list, and the rest of list, depending upon wether this is the element we want or not. If the index is 1, i.e. we want the rst element, we just return the rst element we just pulled off the non-empty list. If the index is greater than 1, we recursively call ourselves to get the i-1th element from the rest of the list. If we every hit an empty list, we were passed too short a list, and throw an exception. The biggest advantage of using recursion for looping is that we can have variables that get new values every loop- by having arguments to the recursive function. One way to look at the idx argument to the nth function is it is a count of how many steps we have left to go- a variable that gets decremented each loop. We are completely sidestepping the problem that you cant assign new values to variables this way. 86

Chapter 5. Lists and Recursion Instead, we turn them into parameters, allowing us to give them new values every iteration.

Example: Fibanocci Numbers


Another example of how to do this is Fibanocci numbers. A few chapters ago we introduced the naive implementation:
let fib n = if n > 1 then (fib (n-1)) + (fib (n-2)) else 1 ;;

The disadvantage with this implementation is that we spend a lot of time computing the same values over and over. If we want to compute the value of fib 10 for example, we compute the value of fib 8 twice- once for fib 10, and once for fib 9. The value of fib 7 is computed three times- once for each of the two times we compute fib 8 and once by fib 9. We end up computing fib 1 a grand total of 55 times, and we end up calling fib a grand total of 177 times. If we try to compute fib 11, we end up computing fib 1 89 times- and having the function called a total of 287 times. Computing fib 11 is over 62% more expensive than computing fib 10 is. This snowballing expense means that it computationally infeasible to compute even reasonably small numbers, like fib 50, on even the fastest computers this way. The solution is to stop recalculating the same values over and over. Instead of recalculating them, we should calculate them once, cache the value somewhere, and use the cached value instead of recalculating, we could compute the ith Fibonacci number in only i steps. Furthermore, the ith Fibonacci number is only (directly) used twice- in the computation of the i+1st and 1+2nd Fibonacci numbers. So we only really need to cache at most two previous numbers if we work up and not down. In another language, we might write code something like this (note that this is not valid Ocaml code!):
function fib (int n) { int a = 1; int b = 1; while (n > 1) do int c = a + b; a = b; b = c; n = n - 1; done return b; }

In Ocaml, we use recursion to implement this while loop. First, the code, and then the commentary:
let fib n = let rec loop a b n = if n > 1 then let c = a + b in loop b c (n-1) else b in loop 1 1 n

87

Chapter 5. Lists and Recursion


;;

Each call of the function loop executes the while loop one time. The variable c in the pseudo-code is unique for every loop- it is only needed to hold the new value of b, so we can update a and b at the same time. We replicate it here simply so you can see where it went. The variables whose values get passed from one loop instance to the next are a, b, and n, so those are the arguments to our recursive function. We use the exact same test to determine if we should recursively call ourselves, or wether we should exit, as we did to determine how long the while loop should continue. This new Ocaml code can nd fib n in about n steps, so determining that fib 100 is 277,887,173 takes almost no time at all.

Example: Reversing a List


Another common operation we need to do is reverse a list. Given a list of strings, say ["foo"; "bar"; "baz"], we want to create another list with the same elements but in the reverse order- in this case, ["baz"; "bar"; "foo"]. The algorithm to
do this is simple. We start with the source list and an empty result list. While the source list is not empty, we remove the first element from the source list using pattern matching, and prepend it using the :: operator to the result list. he code to do this looks like: # let list_reverse lst =

let rec loop source_list result_list = match source_list with | [] -> result_list | first :: rest -> loop rest (first :: result_list) in loop lst [] ;;
val list_reverse : a list -> a list = <fun> # list_reverse [ "foo"; "bar"; "bang" ];; - : string list = ["bang"; "bar"; "foo"] #

As a side note, internal functions are only visible within their enclosing function or context. So only list_reverse can see this loop function, no one else can. So if a function is just a recursive looping function, I have the habit of naming it some variant on "loop", and making it a local function. The multiple different loop functions dont conict with each other, because there is no context where you can ever see more than one of them. Note that this function already exists in the standard library with the name List.revthe rev function in the List module. Reimplementing functions in the standard library is generally not a wise thing to do. So, from here on out, I will use the standard library. There are a lot more interesting list functions in the List standard library, you are encouraged to check them out.

Converting Loops Into Recursion


The translation from loops to recursive functions is almost mechanical. Any while loop, no matter how complicated, can be turned into a recursive function. If the loop is of the pattern:
function var1 var2 ... varn foo(param1, param2, ... paramn) { = initval1; = initval2; = initvaln;

88

Chapter 5. Lists and Recursion


while (test) loopvar1 loopvar2 ... loopvarn do = val1; = val2; = valn;

var1 = newval1; var2 = newval2; ... varn = newvaln; done return vari; }

This can get turned into the recursive Ocaml code:


let foo param1 param2 ... paramn = let rec loop var1 var2 ... varn = if test then let loopvar1 = val1 in let loopvar2 = val2 in ... let loopvarn = valn in loop newval1 newval2 ... newvaln else vari in loop initval1 initval2 ... initvaln ;;

Do/while loops work similiarly- the only question is where you put the test. So a do/while loop that might look like this in pseudo-code:
function var1 var2 ... varn do foo(param1, param2, ... paramn) { = initval1; = initval2; = initvaln; loopvar1 = val1; loopvar2 = val2; ... loopvarn = valn; var1 = newval1; var2 = newval2; ... varn = newvaln; while (test); return vari; }

This can get turned into the recursive Ocaml code:


let foo param1 param2 ... paramn = let rec loop var1 var2 ... varn = let loopvar1 = val1 in let loopvar2 = val2 in ... let loopvarn = valn in if test then loop newval1 newval2 ... newvaln else newvali

89

Chapter 5. Lists and Recursion


in loop initval1 initval2 ... initvaln ;;

If you need multiple values set from a loop, return a tuple. For example: for any two numbers u and v, there are two other numbers a and b such that (a*u) + (b*v) = gcd(u, v). We can nd a and b (and, while were at it, gcd(u,v)) by using an extended Euclids algorithm 1. This algorithm could be psuedo-coded like:
function ext_euclid(int u, int v) { int a1 = 1; int a2 = 0; int b1 = 0; int b2 = 1; while (v != 0) do int q = u/v; int w = y mod v; int a3 = a1 - q * a2; int b3 = b1 - q * b2; u = v; v = r; a1 = a2; a2 = a3; b1 = b2; b2 = b3; done return (u, a1, b1); }

In Ocaml, wed write the same code like:


let ext_euclid u v = let rec loop u v a1 a2 b1 if v != 0 then let q = u/v and r = u mod v in let a3 = a1 - q * and b3 = b1 - q * in loop v r a2 a3 b2 else u, a1, b1 in loop u v 1 0 0 1 ;; b2 =

a2 b2 b3

The C/C++/Java concept of a for loop is a just some syntactic sugar over while loops. For example, the C for loop:
for (i = 1; i <= 5; ++i) { printf("%d\n", i); }

is syntactically the same as this implementation using a while loop:


i = 1; while (i <= 5) { printf("%d\n", i); ++i; }

90

Chapter 5. Lists and Recursion

Using our standard pattern for turning while loops into recursive functions, we might write the following Ocaml code:
# let rec loop i =

if i <= 5 then let _ = print_string ((string_of_int i) ^ "\n") in loop (i+1) else () in loop 1;;
1 2 3 4 5 - : unit = () #

Ocaml doesnt need fancy loop control features, like break or continue. You dont need a continue, because at any time you make the recursive call, which is effectively the same thing (indeed, you can decide what values the parameters get the next iteration- allowing you to both continue with the next iteration, restart the current iteration, back up and redo the last iteration, or even jump to any arbitrary iteration). Likewise, you can decide to not make the recursive call at any point and just return a value- doing effectively the same thing as a break.

Nested Loops
Using recursion as looping presents on obvious problem- you cant have more than one loop per function. But with local functions, this is less of an issue. Consider the case where you are representing a matrix as a float list list (a list of lists of oats). Matrix addition is then just peicewise addition- the jth element of the ith list of the rst matrix is added to the jth element of the ith list of the second matrix. We could implement this the following way, with two seperate functions at the same "level":
let matrix_add a b = let rec inner_loop a_lst b_lst = (* Add two columns of the matrix *) match a_lst, b_lst with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (a_first +. b_first) :: (inner_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in let rec outer_loop a_mat b_mat = (* add two matricies, column by column *) match a_mat, b_mat with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (inner_loop a_first b_first) :: (outer_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in outer_loop a b ;;

91

Chapter 5. Lists and Recursion Notice how outer_loop calls inner_loop using its return value. I prefer this style of nested loops if the inner loop doesnt need to reference the context of the outer loop, but can run "independently" (as it does here). Another way to do the same thing is to have the inner loop function as a local function within the outer loop:
let matrix_add a b = let rec outer_loop a_mat b_mat = (* add two matricies, column by column *) let rec inner_loop a_lst b_lst = (* Add two columns of the matrix *) match a_lst, b_lst with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (a_first +. b_first) :: (inner_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in match a_mat, b_mat with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (inner_loop a_first b_first) :: (outer_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in outer_loop a b ;;

This style is better when the inner loop needs to reference the context of the outerloop.

Mutually Recursive Functions


The question arises natural- how do you make two (or more) functions that are mutually recursive- that call each other and themselves? The solution is to use the let/and construct. The classic example of mutual recursion seems to be a really bad denition of even and odd. Basically, if a number is 0 it is even and not odd. Otherwise, a number x is even if x-1 is odd, and odd if x-1 is even. This gives rise to the code:
let rec even x = if x == 0 then true else odd (x-1) and odd x = if x == 0 then false else even (x-1) ;;

If you looked at this code and went "what an awful way to dene even and odd" Im right there with you. But it does give a simple example of mutual recursion. One point Id like to make, because I got confused by it, is that the rec binds with the let, and not with the function name. I keep wanting to say something like:
let rec f x = ... and rec g x = ...

but the second rec is a syntax error. If you add the rec, all the functions you dene with that let are recursive. This is generally not a problem, there is no performance or memory penalty for making a function recursive that doesnt need to be. Its only a problem if you are overriding the name of another function you want to call (a rare case, hopefully).

92

Chapter 5. Lists and Recursion

Tail Recursion
Youre probably thinking to yourself at this point "Cute trick, that using recursion for looping, but Id bet that old fasioned looping would still be faster!" After all, for each iteration you have an (unnecessary) cost of calling the function and setting up the stack frame. And at the end of all of these recursive calls you have a long string of returns as you unwind the stack. And what of the memory utilization? If you have an especially long running iteration, for example on doing millions or billions of iterations, wouldnt using recursion as our looping run us out of memory, while an old style loop would run in constant memory? The answer here is no. Recursion as looping can be as efcient as old-style looping, and you can loop billions, even trillions, of times and still only use a constant amount of memory. This is because Ocaml does tail call optimization. The idea behind tail call optimization is simple. Lets say that function a calls function b, and that function b calls function c. And that function b returns whatever function c returns without modifying it in any way or doing anything else (this is called a tail call). What Ocaml does in this case is that instead of calling c like normal, it sets up cs stack frame, then frees bs stack frame, and then jumps to c. This way the return address for c goes directly back to a, do not pass b, do not collect $200. For two functions this isnt that big of a deal- but what if c tail calls d, which tails calls e, etc., all the way down to function z. Each time, the previous stack frame gets freed, so there is only ever one stack frame- when z is executing, all the stack frames for functions b through y have already been freed. And when z returns, z jumps over all the functions b through y to return directly to a. Even better, if functions b through z are all the same function (which happens if were using recursion as looping), Ocaml will go a step further. Rather than freeing the old stack frame and allocating a new stack frame, Ocaml will simply reuse the stack frame- write the new values for the parameters to the stack frame (the compiler can do this- you cant) and jump back to the start of the function. Note that this is exactly how the while loop is implemented- write the new values into the variables and jump back to the start of the loop. The rules for whether a function call is a tail call or not are as follows: 1. The value returned from the tail call must be returned without any modication- this includes passing the value to any other functions, modifying the value in an expression, and including the value in an allocated data structure (including tuples, variant types, or lists). 2. The tail call can not be contained in a try statement (you can not catch exceptions from the tail call). 3. The function being called must have ve or fewer arguments. This is due to technical requirements inside the compiler- functions with more than ve arguments are called in a different way that can not be tail call optimized. This is an important concept, so Im going to start giving a number of examples- of functions that are not tail recursive, and how to x them so they are tail recursive.

Accumulation Parameters
The rst example we write is a function to calculate the length of a list. A naive approach would be to say that the empty list is 0 elements long, and a non-empty list is 1 plus the length of the tail of the list long. This gives us the code:
let rec len lst = match lst with | [] -> 0 | _ :: t -> 1 + (len t)

93

Chapter 5. Lists and Recursion


;;

This violates the rst rule- adding one to the result of len modies the result, so the call to len here is not a tail call. The solution is to use an accumulation parameter. We rewrite the function so the recursion is in an inner function that takes a parameter- a parameter that is incremented for every element of the list, and returned at the end of the iteration as the value, like this:
let len lst = let rec loop accum lst = match lst with | [] -> accum | _ :: t -> loop (accum+1) t in loop 0 lst ;;

Note that this function already exists in the standard library, and is called List.length. The List module contains a number of usefull list-handling functions. Another example of this pattern: assume we have a vector which is a list of oating point numbers. The euclidean length of the vector is the square root of the sum of the squares of all the elements. The naive implementation of this function might be:
let euclid_len vector = let rec loop lst = match lst with | [] -> 0. (* 0-element vectors always have a length of 0 *) | h :: t -> (h *. h) +. (loop t) in sqrt (loop vector) ;;

Since were adding a value to the result, the recursive call to loop is not a tail call. We can make it a tail call by accumulating the sum of the squares into a parameter:
let euclid_len vector = let rec loop accum lst = match lst with | [] -> accum | h :: t -> loop ((h *. h) +. accum) t in sqrt (loop 0. vector) ;;

Notice that the rst call to loop (from euclid_len) is not a tail call either- since we pass the result into the function sqrt and thus violate the rst rule again. This can be solved by moving the call to sqrt into the loop function, like:
let euclid_len vector = let rec loop accum lst = match lst with | [] -> sqrt accum | h :: t -> loop ((h *. h) +. accum) t in loop 0. vector ;;

94

Chapter 5. Lists and Recursion Note that this isnt likely to be a major performance advantage, as the non-tail-call was only performed once. Changing loop to be tail recursive makes a big difference, however (consider the case where you have vectors thousands of elements long). But this is an example of another pattern- that if you are accumulating values, choosing where to do the nal processing can make a difference in wether a call is a tail call or not. Another point to note with this example. Its not the function which determines wether a call is a tail call or not, its the call. The same function can be called with a tail call in some places, and called with a non-tail-call in other places. Ocaml will optimize those calls that are tail calls, and resort to normal calling for the rest.

Increment a List of Ints


Now, lets consider the function that takes a list of ints, and returns the list with all the elements incremented. Given the list of ints [1; 3; 7], this function would return the list of ints [2,; 4; 8]- the original list of ints with one added to every value. Now, since we cant change the old list, this means we need to allocate a new list- a list that will be the same length, with the elements all being the incremented elements from the original list. A naive implementation of this function would be:
let rec inclist lst = match lst with | [] -> [] | h :: t -> (h+1) :: (inclist t) ;;

Given the list [1; 3; 7], we use recursion to build up the expression 2 :: 4 :: 8 :: [], the list we want to return. Unfortunately, this function is not tail recursiveby prepending an element onto the list the recursion returns, we violate rule number one- weve modify the return value. Our rst attempt to x this runs us into real trouble. Rather than prepending elements, we append the new elements to the list. So, starting with the list [1; 3; 7], we build the lists [], [2], [2; 4], and [2; 4; 8]. We append to the list using the @ operator:
let inclist lst = let rec loop accum lst = match lst with | [] -> accum | h :: t -> loop (accum @ [ h+1 ] ) t in loop [] lst ;;

Remember my dire warnings about using the @ operator? Youre learning why. Every time we append a new element on to the end of the accumulating list, we have to reallocate a whole new copy of that list. For our three element list [1; 3; 7] we end up allocating six list nodes in total. For a four element list, 10 nodes. For an n element list, we need to allocate n*(n+1)/2 nodes. Weve turned an O(n) algorithm into an O(n2 ) algorithm that is signigantly slower! Itd be much better if we could use the :: list prepend operator instead. The solution is to accumulate the result of the list backwards, and then at the end reverse the list. This is a bit tricky. Say we start with the list source list [1; 3; 7; 15] and an empty accumulator list. While the source list is not empty, we take the rst element off the source list (using pattern matching), increment it, and prepend it to the accumulator list (using the :: operator). So after the rst recursive call, our source list is [3; 7; 15] and our accumulator list is [2]. After the second call, our 95

Chapter 5. Lists and Recursion source list is [7; 15] and our accumulator is [4; 2]. By the time were done, and have emptied out our source list, our accumulator list will be [16; 8; 4; 2]- all the values we want, but in reverse order. So we reverse the list with a call to List.rev. This gives us the code:
let inclist lst = let rec loop accum lst = match lst with | [] -> List.rev accum | h :: t -> loop ((h + 1) :: accum) t in loop [] lst ;;

This is a very common trick in Ocaml, and it often appears in very complex ways. Another example helps make this clear. Remember our matrix add function from above:
let matrix_add a b = let rec inner_loop a_lst b_lst = (* Add two columns of the matrix *) match a_lst, b_lst with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (a_first +. b_first) :: (inner_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in let rec outer_loop a_mat b_mat = (* add two matricies, column by column *) match a_mat, b_mat with | [], [] -> [] | (a_first :: a_rest), (b_first :: b_rest) -> (inner_loop a_first b_first) :: (outer_loop a_rest b_rest) | _ -> invalid_arg "matrix_add" in outer_loop a b ;;

Neither loop function is tail recursive, as in both cases were prepending elements to the head of the list were getting back from the recursive call. We can rewrite this function using a reversed accumulation list like:
let matrix_add a b = let rec inner_loop a_lst b_lst accum = (* Add two columns of the matrix *) match a_lst, b_lst with | [], [] -> List.rev accum | (a_first :: a_rest), (b_first :: b_rest) -> inner_loop a_rest b_rest ((a_first +. b_first) :: accum) | _ -> invalid_arg "matrix_add" in let rec outer_loop a_mat b_mat accum = (* add two matricies, column by column *) match a_mat, b_mat with | [], [] -> List.rev accum | (a_first :: a_rest), (b_first :: b_rest) -> let res_col = inner_loop a_first b_first [] in outer_loop a_rest b_rest (res_col :: accum) | _ -> invalid_arg "matrix_add" in outer_loop a b [] ;;

96

Chapter 5. Lists and Recursion Now both functions are tail recursive.

Working Around Exceptions


The read_line function has a type of unit -> string, and reads one line from the standard input and returns it as a string (sans the newline character). So, for example:
# read_line ();;

foo!
- : string = "foo!" #

When read_line encounters the end of le marker (control-D under Unix and MacOS X, control-Z under Windows), it throws an End_of_file exception:
# read_line ();;

^D
Exception: End_of_file. #

Notice that the end of le is consumed by the read_line, and doesnt kick us out of the top level interpreter. So, we want to write a function that reads lines in from the standard input until it hits an end of le marker, and stores all the lines in one big list (in order) for later processing. A rst, naive, implementation of this function might be:
let rec read_all_lines (_: unit) = try let line = read_line () in line :: (read_all_lines ()) with | End_of_file -> [] ;;

Now, the rst problem with this function is that its modifying the return value. Applying the same pattern as before, we rst build the list backwards and reverse it:
let read_all_lines (_: unit) = let rec loop accum = try let line = read_line () in loop (line :: accum) with | End_of_file -> List.rev accum in loop [] ;;

This function still isnt tail recursive however, because now were violating rule twothe call can not be within a try block. The solution is to seperate the decision to do the recurive call or not from catching the exception. We do this by introducing a new variable, hit_eof, which is set to true if we caught an exception and false otherwise:
let read_all_lines (_:unit) = let rec loop accum = let line, hit_eof = try (read_line ()), false

97

Chapter 5. Lists and Recursion


with | End_of_file -> "", true in if hit_eof then List.rev accum else loop (line :: accum) in loop [] ;;

Several things to notice about this example. First of all, remember that try/with expressions are expressions- the one here is an expression of type string * bool, which we use to set line and hit_eof simultaneously. But the most important thing to notice is that weve moved the recursive call out from the try block- its now a tail recursive call.

Overcomming the Parameter Count Limit


This is the one rule you are least likely to bump into in transforming function calls into tail calls. Functions with more than ve arguments should be rare in the rst place. Making the key recursive function into an inner function can allow you to drop the number of parameters- if the loop isnt changing them, leave them in the outer functions stack frame (where they dont count against the ve argument limit). If worst comes to worst, bind several arguments together up into a tuple and just pass in the tuple

Some Counterexamples
Consider the Assoc.find function, from when we developed the association data structure in the last chapter. For review, it looked like this:
let find k (cmpfn, root) = let rec innerfind node = match node with | Leaf -> None (* Didnt find the key *) | Branch(_, k, d, left, right) -> let rval = cmpfn k k in if rval == 0 then Some d (* We found the key we were looking for *) else if rval < 0 then innerfind left else innerfind right in innerfind root ;;

Given a key to look up, and an association structure, it returns Some(data) if the key has an association to data in the structure, or None if it doesnt. How would you turn this function into a tail recursive function? The answer is: you dont have too, its already a tail recursive function. We dont violate any of the three rules. This shows that you were writting tail recursive functions before you even knew what they were. In some other language youd be looking at this function wondering how to turn it into a loop- in Ocaml its already done. Now consider the Assoc.add function:
let add key data (cmpfn, root) = let rec inneradd node = match node with

98

Chapter 5. Lists and Recursion


| Branch(h, k, d, l, r) -> let cval = cmpfn key k in if cval = 0 then Branch(h, key, data, l, r) else if cval < 0 then makenode k d (inneradd l) r else makenode k d l (inneradd r) | Leaf -> Branch(1, key, data, Leaf, Leaf) in cmpfn, (inneradd root) ;;

Is this function tail recursive? No- were making a node with the return value by using it as a parameter to makenode (the call to makenode is a tail call, by the way). But before you start thinking of clever and complicated ways to make the function tail-recursive, consider: if you have an association with a billion (230) elements, the deepest branch will be at most 60 levels deep. The number of times the function will call itself simply isnt that many. And function calls are not that expensive (especially in Ocaml). In this case, the function isnt tail recursive, but Id say that this isnt a problem. Especially considering that any solution you could come up with wouldnt be as obvious, easy to debug, or maintain. The lesson here is to think before optimizing the code.

Two Examples: Sorting


BubbleSort
BubbleSort is the simplest of all possible sorting algorithms. We run through the entire list comparing adjacent elements. If we nd two adjacent elements in the wrong order, we swap them. We keep doing this, running through the list swapping elements, until we run the entire length of the list and dont nd any two elements out of order, at which point we know were done, and the list is sorted. We apply the standard patterns to make the code tail recursive, have the user pass in the comparison function (like we did for Assoc.make) and get:
let bubblesort cmpfn lst = let rec loop1 lst accum notdone = match lst with | [] -> notdone, (List.rev accum) | h :: [] -> notdone, (List.rev (h :: accum)) | a :: b :: t -> if (cmpfn b a) < 0 then loop1 (a :: t) (b :: accum) true else loop1 (b :: t) (a :: accum) notdone in let rec loop2 lst = let notdone, lst = loop1 lst [] false in if notdone then loop2 lst else lst in loop2 lst ;;

99

Chapter 5. Lists and Recursion This is an example of nesting loops. The outer loop, here called loop2 (Im not very imaginative with the names of my looping functions), simply keeps calling the inner loop until the inner loop says it didnt swap any elements. The inner loop (named loop1) is where the real work goes on. Its a single pass through the list. If the remaining list is zero or one elements long, were done walking through the list, there are no more elements left to possibly swap. All we need to do is reverse the accumulated result and return. If there are at least two elements left in the list, the smaller of the two gets prepended onto the accumulation list while the other gets pushed back on to the front of the rest of the list. Note that the rst element pushed on to the accumulation list will end up closer to the front of the resultant list once we reverse the list, so if the objects are out of order, and we end up pushing the second element of the source list onto the accumulation list rst, we end up swapping the order of the two elements in the result. This tells us if we swapped two elements or not, information we need to pass back to the outer loop. The notdone variable is an excellent example of a loop modied variable- we either have it keep its value, or set it to true, depending upon wether we know we had a swap or not. Because we accumulate the result backwards, both loop functions are tail recursive (verify this).

QuickSort
The QuickSort algorithm is a standby of computer programmers (although I myself prefer HeapSort, but thats a different argument), because it is the simplest of the fast sorting algorithms. To sort a list, we pick one element called the "pivot". We then split the list into two lists- a list of all elements less than or equal to the pivot, and a list of all elements greater than the pivot. We hope that the two lists will be more or less equal in size. Because then we quicksort both sub-lists. If were lucky in how we pick our pivots, QuickSort can sort an array length n in O(n log n) time. In this example, we dont do anything particularly bright with picking a pivot- we just grab the rst element in the list and hope for the best. We have the user pass in a comparison function (just like we did for Assoc.make). So now, without further ado, the code:
let quicksort cmpfn lst = let rec qsort lst accum = match lst with | [] (* empty lists, *) -> accum (* dont need to be sorted *) | pivot :: t -> let rec splitlist lelst gtlst lst = match lst with | [] -> lelst, gtlst | h :: t -> if (cmpfn h pivot) <= 0 then splitlist (h :: lelst) gtlst t else splitlist lelst (h :: gtlst) t in let lelst, gtlst = splitlist [] [] t in qsort lelst (pivot :: (qsort gtlst accum)) in qsort lst [] ;;

The qsort function really isnt a looping function, its a classic recursive function. If the list is empty, were done. If there is one or more elements in the list, we use the rst element as our pivot. We then do use a classic looping recursion to split the 100

Chapter 5. Lists and Recursion list into two lists- the list of elements less than or equal to our pivot, and the list of elements greater than our pivot. We then sort both sublists seperately. Notice the use of the accumulation parameter in qsort. This is one case where we can naturally build our list up backwards, just by sorting the second half of the list rst. Then we just keep prepending the sorted elements to the accumulating list. We sort the back half of the list rst, and as its sorted it gets prepended onto the accumulation list. Then we prepend the pivot element, and then we sort the front half of the list.

Handrolled Stack Management


Generally, Ocaml handles stack management for you automatically. You call a function, even a recursive function, and Ocaml sets up the stack frame, lls it with the right values, and frees it when the function returns. Occassionally, however, you need to hand manage your stacks- generally when you want to stop a recursive computation in midstep, and then resume it again later. When you need to do this, Ocamls automatic stack management just doesnt cut it. Consider the following problem: we want an iterator for our Assoc module- a magic data structure which allows us to step through all elements of an association as if they were a list. We might dene an API for this capability by adding the following lines to the assoc.mli le:
type (key, data) iterator_t (* abstract *) val iterator : (key, data) t -> (key, data) iterator_t val first : (key, data) iterator_t -> (key * data) option val rest : (key, data) iterator_t -> (key, data) iterator_t

The function iterator creates an iterator object, which we can then pass to the first and rest functions. The first function returns a tuple of the current key and data association, wrapped in a Some constructor, or None if there are no more elements left. The rest function returns a new iteration object- iterating all the elements of the association less the previous rst element. Its the step function, to move on to the next element. It throws an Invalid_argument exception if you pass it an iterator with no more elements in it (i.e. first returns None). If you remember our example in the last chapter of implementing sparse vectors as associations of ints to oats, using these new capabilities we could implement an add function thusly:
let add x y = let rec loop res x_iter y_iter match (first x_iter), (first y_iter) with | None, None -> res | Some(i, v), None -> loop (set res i v) (rest x_iter) y_iter | None, Some(i, v) -> loop (set res i v) x_iter (rest y_iter) | Some(i, v), Some(j, w) -> if i < j then loop (set res i v) (rest x_iter) y_iter else if i > j then loop (set res j w) x_iter (rest y_iter) else loop (set res i (v +. w)) (rest x_iter) (rest y_iter) in loop (make ()) (iterator x) (iterator y) ;;

101

Chapter 5. Lists and Recursion Imagine we had a magic_return function2. We could then create an iterator functioncall it iterloop. When we create an iterator, iterloop runs until it hits the rst call to magic_return. The value passed in at that call to magic_return is the value returned from the rst call to first on that iterator. When rest is called, iterloop picks up where it left off, until the next magic_return is hit. When iterloop is done, the iterator is empty. If we had this magic function, writting iterloop would be easy. Itd be a minor tweak on the iter function we already wrote:
let rec iterloop node = match node with | Leaf -> () | Branch(_, key, data, left, right) -> let _ = iterloop left in let _ = magic_return (key, data) in iterloop right ;;

Its really too bad we dont have this magic magic_return function- itd make our life a lot easier. But we dont. So we have to do things the hard way. To implement this function, we have to make and manage our own stack. When we wrote the tree comprehension functions, we depended upon the computer to manage our stack for us. Each recursive call of the function pushed a new stack frame onto the stack, each return popped a frame a stack frame off the stack. First, we have to think of how to implement the stack. A list is the perfect data structure for this. To push a frame on the stack we prepend the frame to the list using the :: operator. To pop an element off the stack, we just remove the head element of the list. If we wanted to, we could build a stack library with code like:
type a stack = a list;; let push x stack = (* push x onto the stack, returning the new stack *) x :: stack ;; let pop stack = (* pop the top element off the stack, return the new * stack and the top element in a tuple. *) match stack with | [] -> assert false | h :: t -> (h, t) ;;

We wont actually use a seperate stack module for this code, however. The operations are simple enough to hand inline. Next is what to store on the stack. The program stack contains both local variables and location in the function. This inclines us to think of using a variant type to hold on the stack. In this case, we have four places where we might need to (re)start execution of a call to iterloop. The rst is on entering the function. The second is when we return from iterating on the left subtree. The third is after we return from calling magic_return- The fourth is after we return from iterating the right subtree. Here is our original iterloop function, with the four restart points annotated:
let rec iterloop node = (* First restart point *) match node with | Leaf -> () | Branch(_, key, data, left, right) ->

102

Chapter 5. Lists and Recursion


let _ = iterloop left in (* Second restart point *) let _ = magic_return (key, data) in (* third restart point *) iterloop right (* fourth restart point *) ;;

We only have one local variable everywhere- node, the node were working on. This needs to get stored in the variant type. This gives us the following type:
type (key, data) iterframe_t = | First of (key, data) node_t (* on function entry *) | Second of (key, data) node_t (* after left rec call *) | Third of (key, data) node_t (* after the magic_return * from iter *) | Fourth of (key, data) node_t (* after the right rec call *) ;;

We need a function to fake executing our iterloop function- we call it, with a complete lack of imagination, execute. Its a local function- since it doesnt have a denition in the .mli le, functions in other modules cant see it. But all functions in the current module can. It continues executing one or another instance of iterloop until it hits either a magic_return point, or all calls to iterloop have exited (i.e. the stack frame is empty). 1. An empty stack means were done executing all copies of iterloop, and we just return the empty stack. 2. If we are at the First stop position, weve just entered the function. First, we check to see if the node we were called with is a leaf node. If it is, iternode just returns. We do a return by popping our frame off the top of the stack, and restarting to execute the function that called us. This is just a tail call to execute on the rest of the stack. 3. If we are in the First stop position, and the node we were passed isnt a leaf node, we recurse down the left hand subtree. But before we do that, we want to make sure we return from the right place. When we return, we want to pick up executing after the call- we want our stack to be in the second position. So we pop our stack frame off and push on a Second frame. We then push on a First frame and execute it. 4. If were in the Second stop position, weve just returned from recursively traversing the left subtree. The next think iterloop does is do a magic_return. This means we can stop where we are. The rest function will need to "reach in" to change our state before restarting us. 5. If were in the Third stop position, rest has just reached in and moved us past a magic_return. What iterloop does at this point is recurse down the right hand subtree. This works exactly like recursing down the left hand subtree, except for which subtree were recursing down (obviously), and the fact that we want to return to the Fourth stop point, and not the Second. 6. Lastly, if were in the Fourth stop position, weve just returned from traversing the right subtree. All we need to do here is exit. We just pop our stack frame off the stack, and start executing the function that called us, with a recursive call to execute. This gives us the following code for execute:
let rec execute stack =

103

Chapter 5. Lists and Recursion


match stack with | [] -> (* empty stack *) [] | First(node) :: stack_rest -> begin (* Note: remember that to do a pattern match within * a pattern match, we need to wrap the inner match * with a begin/end! *) match node with | Leaf -> execute stack_rest | Node(_, key, data, left, right) -> (* pop the current frame *) let stack = stack_rest in (* push the new current frame *) let stack = Second(node) :: stack in (* push the frame for the new call *) let stack = First(left) :: stack in execute stack end | Second(_) :: _ -> stack | Third(node) :: stack_rest -> begin match node with | Leaf -> (* this should never happen *) assert false | Node(_, key, data, left, right) -> (* pop the current frame *) let stack = stack_rest in (* push the new current frame *) let stack = Fourth(node) :: stack in (* push the frame for the new call *) let stack = First(left) :: stack in execute stack end | Fourth(node) :: stack_rest-> execute stack_rest ;;

All we need to store in our iterator is the current execution stack. So our denition of an iterator_t is easy:
type (key, data) iterator_t = (key, data) iterstack_t list;;

Given execute, creating an iterator is easy. We create a stack frame with only one frame- the original call to iterloop with the root node of the tree. We then execute this function until it hits a magic_return or exits. The stack upon returning from execute is our initial value of the iterator:
let iterator (_, root) = execute [ First(root) ] ;;

Note that creating a stack with one frame is the same as creating a list with one element. Writting first isnt much more difcult. If the stack were passed in is empty, the iteration is done, and we can just return None. If not, the top frame on the stack should be at the second stop point- at the magic_return point. We just take the key and data elements from its node for our Some return value: 104

Chapter 5. Lists and Recursion


let first stack = match stack with | [] -> None | Second(Node(_, key, data, _, _)) :: stack_rest -> Some(key, data) | _ -> (* No other states should be possible! *) assert false ;;

The function rest works very similarly. If the stack is empty, we throw an Invalid_arg exception. If the stack isnt empty, we have to change the top stack from from the second point its at, to the third point. This moves it past the magic_return and allows us to resume execution without immediately stopping at the same place.
let rest stack = match stack with | [] -> invalid_arg "Assoc.rest" | Second(node) :: stack_rest -> execute (Third(node) :: stack_rest) | _ -> (* No other states should be possible! *) assert false ;;

I have delibertly not optimized these routines, to make it more clear how the hand rolled stack management maps to recursive calls. We could eliminate the fourth stop point by doing our own tail call opimization- we just never push it onto the stack. So when the right subtree traversal returns, it jumps right past to the next call in the stack that has something other than just returning to do. Whenever we push any restart point other than the Second restart point, all were going to do is pop it right back off. So all we need to do is store the Second stop points on the stack- we can eliminate all the others. We never push a Leaf node onto the stack. When we push a Node onto the stack, we recursively push the root node of its left subtree onto the stack as well- on down the tree until we hit a Leaf node:
type (key, data) iterator_t = (key, data) node_t;; let rec push_left node stack = match node with | Leaf -> stack | Node(_, _, _, left, _) -> push_left left (node :: stack) ;;

To create a iterator, we just push the root node onto an empty stack:
let iterator (_, root) = push_left root [];;

The rst element of an iterator is just the key and data from the node on the top of the stack (or None if the stack is empty):
let first stack = match stack with

105

Chapter 5. Lists and Recursion


| [] -> None | Node(_, key, data, _, _) :: _ -> Some(key, data) | Leaf :: _ -> (* this should never happen *) assert false ;;

The rest function removes the top element from the stack, and then pushes the right subtree of the element it just removed back onto the stack:
let rest stack = match stack with | [] -> invalid_arg "Assoc.rest" | Node(_, _, _, _, right) :: tail -> push_left right tail | Leaf :: _ -> (* this should never happen *) assert false ;;

A FIFO Queue implementation


Stacks are an implementation of a LIFO queue- LIFO for Last In First Out. The work like a stack of plates. You add new plates to the top of the stack, and takes plates off the top of the stack. The next plate that gets removed is the most recent plate added to the stack- the plate that has been in the stack the least amount of time. By contrast, FIFO queues- First In, First Out- work like the check out lines at your local grocery store. Here, customers get served in the order they arrive. The customer at the head of the queue is the customer who has been waiting in line the longest, and theyre the next customer to be served. New customers arriving join the end of the line. Since the FIFO queue otherwise works like a list, we might be tempted to implement it on top of a list, something like this:
type a fifo = a list;; let remove fifo = (* remove the first element from the fifo, returning a tuple * containing the first element and the rest of the fifo. *) match fifo with [] -> invalid_arg "Fifo.remove" h :: t -> h, t ;; let add x fifo = (* add x to the end of the fifo *) fifo @ [ x ] ;;

Unfortuately, our implementation uses the dreaded @ operator. To add an element to our fo, we have to allocate a whole new list just to add the new element onto the end. If our queue holds a million elements, this can get very expensive. We might also consider doing something fancy with trees- this limits the cost of adding an element to the queue to O(log N). But the solution that no doubt immediately popped into your head, using mutable linked lists, is O(1) to both insert and remove elements. The solution is to implement lazy appending. Now, when Ocaml programmers (and programmers of other functional languages) use the term "lazy", they have two different meanings of the term, and tend to assume that you know which one they mean just by context (Im bad for doing this). The rst meaning is to refer to a precise linguistic construct, where values are not computed until they are rst used- this is implemented in Ocaml in the Lazy module. The second is the general concept where 106

Chapter 5. Lists and Recursion a number of changes are cached, so to speak, until they can all be done as a group and more efciently. Dont worry about the rst (precise) denition of the term- in this case, Im using the second denition. The obvious, expensive operation we want to make cheaper is appending an element on to the end of the queue. The rst, obvious, step is to split the queue into two partsthe "front" part of the queue, a list of items that have already been appended to the queue, and the "back" part of the queue, the list of items waiting to be added to the queue. Now, we want adding items to the list of items to be appended to be fast- so we keep the list in reverse order. The most recently added items are at the head of the list, and that is where we add new items to the queue. Adding items to the head of a list is cheap. Now, when do we move items over from the back part of the list to the front part of the list? Every item that is still in the front part of the list needs to get a new list item allocated for it, when we append elements from the back part- so obviously the fewer items in the front part of the list, the cheaper the appending will be. So why not wait until the front part of the list is entirely empty? We only do an append when there is nothing to append to. How many elements do we append when we do the appends? No matter what happens, we have to allocate new list elements for anything left in the back part of the list, and new list elements for anything moved to the front part. If we have N elements in the back part of the queue, were allocating N new list elements anyways- so we might as well move them all to the front part of the list. This idea gives rise to the following implementation:
type a queue = (a list) * (a list);; (* front and back parts *) let remove (front, back) = match front with | [] -> (* This should only happen when the whole queue is * empty. *) invalid_arg "Fifo.remove" | h :: [] -> (* We only have one element in the front part of the * queue- removing it empties the front part. So we * reverse the back part (using List.rev) to make it * the new front part, and the new back part is empty. *) h, ((List.rev back), []) | h :: t -> (* We have more than one element in the front part of * the queue- so we can remove it without emptying the * front part. *) h, (t, back) ;; let add x (front, back) = (* We need to special case the situation where the whole queue * is empty- in this case, we add the element to the front part * of the queue as a 1-element list. Otherwise, we just prepend * the new element onto the back part of the queue. *) match front with | [] -> ([x], []) | _ -> (front, (x :: back)) ;;

How expensive is it to add and remove elements from this implementation of the queue? Consider the following case: we add a million elements to a queue, and then 107

Chapter 5. Lists and Recursion remove all million elements- how many steps of work did we do? Adding each element has a constant cost- you are allocating one list element, and either making it the single element in the front part of the queue (for the rst element added), or prepending it to the back part of the queue. Adding the million elements to the queue costs a millions steps. At the end, we will have a queue with a one element long front part list, and a 999,999 element long back part of the queue. When we remove the rst element from the queue, at that point he get hit with a single, expensive, operation- reversing a list of 999,999 elements. But we only pay that cost once- after the list is reversed, removing the rest of the element is just removing the head element of the list each time. So removing the million elements costs a single 999,999 step list reversal, and a million removals. So the total cost to add and remove 1,000,000 elements is 1,000,000 steps to add them all, a single 999,999 step list reversal, and 1,000,000 remove the head of a list steps, for a total cost of 2,999,999 steps. Adding, and then removing, N elements costs about 3N steps- but since we dont care about the constant factor, we say its O(N). Therefor, on average, every element we add or remove costs O(1).

An Introduction to the List Module


This section serves two purposes. The rst is an introduction to the highlights of the List standard library. If a standard library implements what you need, dont reimplement it. The caveat to this is that you should know your standard libraries, so you know what to not reimplement. The second purpose is to provide more examples of list manipulation and recursion as looping. The functions of the list standard library are wonderfull examples of how to do things in Ocaml- much of my early experience with Ocaml came from reading this very library. Unfortunately, the List library may not remain so "Newbie Friendly"- there is a project3 that replaces the standard List module with a more efcient one. Unfortunately, this more efcient version utilizes several obscure and dangerous capabilities of Ocaml for optimal speed. In addition to making how the functions work much more obscure, they are very bad role models to base your code on. So I reimplement them here in their original, Newbie Friendly, versions. Note that I dont implement all functions from the List module- reading the documentation is still a good idea. But I implement the important ones, and several that are just interesting. All functions are tail recursive unless explicitly specied otherwise.

List.hd
List.hd simply returns the rst element of a list, or raises Failure "hd" if the list is

empty. Its an example of pattern matching on a list.


let hd lst = match lst with | h :: _ -> h | [] -> raise (Failure "hd") ;;

List.tl
List.tl returns the tail, or rest, of a list- the list of all elements of the list except the head element. Its a pair with List.hd, and like that function throws Failure "tl"

108

Chapter 5. Lists and Recursion if given an empty list.


let tl lst = match lst with | _ :: t -> t | [] -> raise (Failure "tl") ;;

List.length
Weve already covered List.length before- we just iterate down the list counting elements as we go. Its an example of iterating on a list.
let length lst = let rec loop cnt lst = match lst with | [] -> cnt | _ :: t -> loop (cnt + 1) t in loop 0 lst ;;

List.nth
List.nth returns the Nth element from a list. Its another example of iterating on a

list.
let nth lst idx = let rec loop idx lst = match lst with | h :: t -> if idx == 0 then h else loop (idx - 1) t | [] -> raise (Failure "nth") in loop idx lst ;;

List.rev
As talked about before, List.rev reverses a list- it allocates a new list with the same elements as a given list, but in reverse order. It is an example of building a list backwards.
let rev lst = let rec loop accum lst = match lst with | h :: t -> loop (h :: accum) t | [] -> accum in loop [] lst ;;

109

Chapter 5. Lists and Recursion

List.rev_append
List.rev_append reverses and appends one list onto another. So List.rev_append [1;2;3] [4;5;6] returns [3;2;1;4;5;6]. This is another example of building up a

list backwards.
let rev_append lst1 lst2 = let rec loop lst1 lst2 = match lst1 with | h :: t -> loop t (h :: lst2) | [] -> lst2 in loop lst1 lst2 ;;

List.append
List.append appends one list onto another, without reversing either list. So List.append [1;2;3] [4;5;6] returns [1;2;3;4;5;6]. This function is also known as the @ operator, with all associated dire warnings. It is an example of

reversing a list so that we can work on it backwards. It is also an example of nested loops (well, chained loops might be a better term). A better implementation would call List.rev and List.rev_append.
let append lst1 lst2 = let rec rev_append rlst1 lst2 = match rlst1 with | h :: t -> rev_append t (r :: lst2) | [] -> lst2 in let rec rev lst1 rlst1 = match lst1 with | h :: t -> rev t (h :: rlst1) | [] -> rev_append rlst1 lst2 in rev lst1 [] ;;

List.concat
Given a list of lists, List.concat appends them all into one big list. So List.concat [ [1;2;3]; [4;5]; [6;7;8;9] ] returns [1;2;3;4;5;6;7;8;9]. Its an example of having multiple nested loops, of reversing lists before working on them, and of why calling the standard libraries rather than reimplementing them is a good thing. Ive kept the names of the standard library functions the same so you can see how theyve been implemented- but this function would be a fraction of its current size if I hadnt inlined some standard functions.
let concat lstlst = let rev lst = let rec loop lst accum = match lst with | h :: t -> loop t (h :: accum)

110

Chapter 5. Lists and Recursion


| [] -> accum in loop lst [] in let rec rev_append lst1 lst2 = match lst1 with | h :: t -> rev_append t (h :: lst2) | [] -> lst2 in let rec loop lstlst accum = match lstlst with | [] -> accum | [] :: t -> loop t accum | lst :: t -> let rlst = rev lst in let accum = rev_append rlst accum in loop t accum in loop lstlst [] ;;

List.iter
List.iter is the same idea as Assoc.iter from Chapter 4. Its a visitor pattern (for

those of whove read your "Design Patterns"), that calls a function on every member of a list. Its an example of iterating through a list.
let iter f lst = let rec loop lst = match lst with | h :: t -> let _ = f h in loop t in loop lst ;;

List.map
List.map is the equivelent for lists of Assoc.map. Given a function that converts from

type a to type b, and a list of type a, it returns a list of type b, with the elements of the returned list the converted elements of the original list. So List.map float_of_int [1;2;3] returns [1.;2.;3.]. Type a and type b can be the same type, in which case the returned value can be different. So, for example, let f x = x * 2 in List.map f [1;2;3] returns [2;4;6]. It is an example of building a list backwards and reversing it at the end.
let map f lst = let rec loop lst accum = match lst with | h :: t -> loop t ((f h) :: accum) | [] -> List.rev accum in loop lst [] ;;

111

Chapter 5. Lists and Recursion

List.rev_map
List.rev_map works like List.map, except it reverses the list as it transforms it. So List.rev_map float_of_int [1;2;3] returns [3.;2.;1.]. The reason for this

odd function is that it doesnt have to build up an unnecessary function which is immediately thrown away in the reversing. It is an example of building lists backwards and iterating through a list.
let rev_map f lst = let rec loop lst accum = match lst with | h :: t -> loop t ( (f h) :: accum ) | [] -> accum in loop lst [] ;;

List.fold_left
List.fold_left is the same idea as Assoc.fold. It accumulates a value over the listgiven an initial value, and a function that takes the current value and the current list element, it walks the list accumulating the nal value. Its different from its sibling List.fold_right both in the order of its arguments (watch them!) and in the fact that it walks the list left to right- i.e. starting at the head of the list. It is an example of walking a list. let fold_left f initval lst = let rec loop accumval lst = match lst with | h :: t -> loop (f accumval h) t | [] -> accumval in loop initval lst ;;

List.fold_right
List.fold_right is just like List.fold_left, except that it works right to left instead- it starts at the end of the list and works backwards. This means that will be less efcient (however its implemented) than List.fold_left is. Also, the order of parameters is annoyingly different from List.fold_left (especially the parameters to the accumulation function- watch them!). I offer two versions of this function for your edication and enjoyment. The rst is a non-tail recursive version which makes it plain what the function does. The second is a tail recursive version which reverses the list before operating on it. let fold_right f lst initval = (* This is the non-tail-recursive version *) let rec loop lst = match lst with | h :: t -> let accumval = loop t in f h accumval (* notice the different order of parameters in the

112

Chapter 5. Lists and Recursion


* above line! *) | [] -> initval in loop lst ;; let fold_right f lst initval = (* this is the tail-recursive version *) let rec loop accumval lst = match lst with | h :: t -> loop (f h accumval) t | [] -> accumval in loop (List.rev lst) ;;

List.iter2
List.iter2 works just like List.iter except it works on two lists, not just one. It throws an Invalid_argument "List.iter2" if the lists are not the same length. In

addition to the standard list walking, it also demonstrates how to use tuples to match from multiple lists simultaneously.
let iter2 f lst1 lst2 = let rec loop lst1 lst2 = match lst1, lst2 with | (h1 :: t1), (h2 :: t2) -> let _ = f h1 h2 in loop t1 t2 | [], [] -> () | _ -> invalid_arg "List.iter2" in loop lst1 lst2 ;;

List.map2
As you can probably guess, List.map2 works like List.map, but with two input lists instead of just one. Like List.iter2, it raises Invalid_argument "List.map2" if the lists are of different lengths. Its an example of building lists backwards and of using tuples to match on two lists simultaneously.
let map2 f lst1 lst2 = let rec loop accumlst lst1 lst2 = match lst1, lst2 with | (h1 :: t1), (h2 :: t2) -> loop ((f h1 h2) :: accumlst) t1 t2 | [], [] -> List.rev accumlst | _ -> invalid_arg "List.map2" in loop [] lst1 lst2 ;;

113

Chapter 5. Lists and Recursion

List.rev_map2
List.rev_map2 works just like List.map2 except it returns the list in reverse order.

This just means we dont have to reverse the list after we build it backwards.
let rev_map2 f lst1 lst2 = let rec loop accumlst lst1 lst2 = match lst1, lst2 with | (h1 :: t1), (h2 :: t2) -> loop ((f h1 h2) :: accumlst) t1 t2 | [], [] -> accumlst | _ -> invalid_arg "List.rev_map2" in loop [] lst1 lst2 ;;

List.fold_left2
List.fold_left2 works like List.fold_left, except it accumulates a value over two lists and not just one. It throws Invalid_argument "List.fold_left2" if the

lists are not the same length.


let fold_left2 f initval lst1 lst = let rec loop accumval lst1 lst2 = match lst1, lst2 with | (h1 :: t1), (h2 :: t2) -> loop (f accumval h1 h2) t1 t2 | [], [] -> accumval | _ -> invalid_arg "List.fold_left2" in loop initval lst1 lst2 ;;

List.fold_right2
List.fold_right2 works like List.fold_right except on two lists, not just one. Like List.fold_right, it also reverses the list before working on it. let fold_right2 f lst1 lst2 initval = let rec loop lst1 lst2 accumval = match lst1, lst2 with | (h1 :: t1), (h2 :: t2) -> loop t1 t2 (f h1 h2 accumval) | [], [] -> accumval | _ -> invalid_arg "List.fold_right2" in loop lst1 lst2 initval ;;

114

Chapter 5. Lists and Recursion

List.for_all
List.for_all tests to see if every member of a list satises some condition. You pass in to List.for_all a function which, given a member of the list, tests to see if that

member fulllls the condition. The function you pass in returns true if the member fulllls the condition, false otherwise. If there is one member of the list for which the passed in function returns false, then List.for_all returns false. If all members of the list return true (or if the list is empty), List.for_all returns true. It demonstrates walking a list, and exiting a loop early (like a C break statement).
let for_all test lst = let rec loop lst = match lst with | h :: t -> if (test h) == false then (* We can stop testing now- were done. *) false else loop t | [] -> true in loop lst ;;

List.exists
List.exists works just like List.for_all, except that List.exists returns true if there is just one element in the given list for which the given test returns true, and false otherwise or if the list is empty. If List.for_all is a logical and of the tests, then List.exists is a logical or. Its another example of walking a list and breaking early. let exists test lst = let rec loop lst = match lst with | h :: t -> if (test h) == true then (* all done *) true else loop t | [] -> false in loop lst ;;

List.for_all2
Much to no ones surprise at this point, List.for_all2 acts just like List.for_all, but on two lists and not just one.
let for_all2 f alst blst = let rec loop a b = match a, b with | (afirst :: arest), (bfirst :: brest) -> if (f afirst bfirst) == false then false else

115

Chapter 5. Lists and Recursion


loop arest brest | [], [] -> true | _ -> invalid_arg "List.for_all2" in loop alst blst ;;

List.exists2
The sign of a good library design is that you can predict the meaning and behavior of functions you have never seen before based upon other stuff in the library. Which means you can accurately predict what List.exists2 does, based upon what List.exists and List.for_all2 do.
let exists2 f alst blst = let rec loop a b = match a, b with | (afirst :: arest), (bfirst :: brest) -> if (f afirst bfirst) == true then true else loop arest brest | [], [] -> false | _ -> invalid_arg "List.exists2" in loop alst blst ;;

List.mem
List.mem scans a list, and returns true if an element in the list is equal to the given element. It uses structural equality (=) instead of referential equality (==). Note that

we get rid of the inner function and simply use the whole function as our loop.
let rec mem a lst = match lst with | [] -> false | h :: t -> if h = a then true else mem a t ;;

List.memq
List.memq works just like List.mem, except it uses referential equality instead of

structural equality.
let rec memq a lst = match lst with | [] -> false | h :: t -> if h == a then true

116

Chapter 5. Lists and Recursion


else memq a t ;;

List.nd
Searches a list for a given element. Were given a function which tests every element, and returns true if its the element were looking for, false otherwise. It returns the element being looked for. It throws the exception Not_found if the list doesnt contain an element which the test function returns true for.
let rec find test lst = match lst with | [] -> raise Not_found | h :: t -> if test h then h else find test t ;;

List.lter
Given a test function like the one passed in to List.find, List.filter returns the list of all elements of the list for which the test function returned true.
let filter test lst = let rec loop accum lst = match lst with | [] -> List.rev accum | h :: t -> if test h then loop (h :: accum) t else loop accum t in loop [] lst ;;

List.partition
Given a test function like List.find and List.filter, List.partition returns a tuple of two lists. The rst list is the list of all elements for which the test function returned true, and the second list is the list of all elements for with the test function returned false.
let partition test lst = let rec loop tlst flst lst = match lst with | [] -> (List.rev tlst), (List.rev flst) | h :: t -> if test h then loop (h :: tlst) flst t else loop tlst (h :: flst) t

117

Chapter 5. Lists and Recursion


in loop [] [] lst ;;

List.sort
Yet another sorting implementation. Just for kicks, we implement a merge sort. Other than that, it works just like the bubble sort and quicksort implementations earlier in the chapter. One point of interest though- notice that we only calculate the length once, and then just pass the length around. This is more efcient than just repeatedly calling List.length.
let sort cmpfn lst = let rec merge accum lst1 lst2 = match lst1, lst2 with | [], [] -> List.rev accum | _, [] -> List.rev_append accum lst1 | [], _ -> List.rev_append accum lst2 | (afirst :: arest), (bfirst :: brest) -> if (cmpfn afirst bfirst) <= 0 then merge (afirst :: accum) arest lst2 else merge (bfirst :: accum) lst1 brest in let rec split len accum lst = if len == 0 then (List.rev accum), lst else match lst with | [] -> assert false | h :: t -> split (len-1) (h :: accum) t in let rec loop len lst = if (len > 1) then let len = len/2 in let alst, blst = split len [] lst in merge [] (loop len alst) (loop (len - len) blst) else lst in loop (List.length lst) lst ;;

Exercises
1. Improve the bubblesort algorithm by changing it so that instead of reversing the list every step, the list is reversed at most one (hint: change the direction the list is being sorted in). 2. An improvement to the quicksort algorithm is to pick three elements from the list, and make the "middle-most" of the three the pivot. Improve the quicksort example to do this. 3. Reimplement the sparse vector class from chapter 4, but instead of using an association as the underlying data structure, use an (int * float) list instead. In what ways might the list based implementation be better than the 118

Chapter 5. Lists and Recursion association based implementation? In what ways might the association based implementation be better?

Notes
1. The proof that this works is well beyond the scope of this book. If you dont already own a copy of Knuths "Art of Computer Programming", it is well worth the money. 2. Some languages actually do have this magic_return function. Python, for example, calls it yield. It is a minor bit of syntactic sugar Ocaml lacks- not that implementing iterators without it is that difcult in Ocaml, as we see. Assuming we have it makes it easier for me to explain how to convert a recursive function with a machine-managed stack into the same recursive program with a hand-managed stack. 3. ExtLib- a project to which I contribute. As because you are likely to go look at ExtLib due to reading this footnote, let me say a few words. The evil hack ExtLib uses to optimize performance is called Obj.magic- an a knowledge of how Ocaml stores its lists internally. If there is one rule with Obj.magic hacks, its dont do them. They totally circumvent Ocamls typecheck and consistency checks, and allow the code to play merry havoc with the entire program. ExtLib is a special case. Dont follow its example.

119

Chapter 5. Lists and Recursion

120

Chapter 6. Higher Order Functions


TODO: Find a good quote for here. William Shakespear Back in chapter 2 I stated that a function with a type of, say, int -> int -> int is read as "a function that takes two arguments, both ints, and returns an int". While technically correct, this denition is incomplete. The real denition is that -> is a sort of type operator. Its a binary operator, like + and * are, except that it operates on two types. Its meaning is effectively "a function which takes a single argument of the type to the left of the ->, and returns a value of the type to the right". For functions that take a single argument, this is just a more verbose way of saying the same thing- a function with the type int -> int still means "a function that takes a single argument of type int, and returns an int". But for functions with multiple arguments, the -> pseudo-operator is applied multiple times, and now the difference in meaning becomes more importantly. As an operator, -> binds to the right. This means that a type like int -> int -> int is interpreted like int -> (int -> int). Literally, the type int -> int -> int is read as "a function taking a single argument of type int, and returning a function taking a single argument of type int and returning an int." Another example: the type float -> char -> string -> bool is read as "a function taking a oat argument and returning a function taking a char argument and returning a function taking a string argument and returning a bool". This is a very deep concept, with a lot of consequences. And its a very different way of thinking about the types of functions than most other programming languages. So dont be surprised if you dont get it immediately. It is also, however, a very usefull and powerful concept for real-world programming, and not just a weird artifact of an obtuse theory.

Partial Function Application


The rst, and most obvious, consequence of Ocamls way of typing is partial function application. Consider a function with the type int -> int -> int. This is a function that takes a single argument and returns another function- is it possible, you might wonder, to somehow capture the returned function (or at least a reference to it)? The answer is yes, we can. How? We only pass in one int to a function that takes two. Assume we have a function like:
# let f x y = x + y;; val f : int -> int -> int = <fun> #

Now, what happens if we pass in only one int?


# f 3;; - : int -> int = <fun> #

The type of the expression f 3 is, as we might have guessed, int -> int. This is a real function, conceptually no different from any other function, except it doesnt have a name. We can give it a name, and keep a reference to it around, by assigning it to a variable using a standard let clause, like:
# let g = f 3;;

121

Chapter 6. Higher Order Functions


val # g - : # g - : # g : int -> int = <fun>

2;;
int = 5

7;;
int = 10

The rst thing to notice is that g doesnt have any variables to mark it as a functionand yet it is still a function. You can have expressions that create functions and assign them to variables. When you dene a function with a given name, like f or g, what you are actually doing is creating a variable that holds a reference to the given function, like a oat variable holds a reference to a oating point value. This point becomes more important later on. The second thing to notice is that, as the examples show, the function g works exactly as if we had declared it:
let g y = f 3 y;;

up to and including actually calling y, as we can determine:


# let f x y =

let _ = print_string ("In f with x=" ^ (string_of_int x) ^ " and y=" ^ (string_of_int y) ^ "\n") in x + y ;;
val f : int -> int -> int = <fun> # let g = f 3;; val g : int -> int = <fun> # g 2;; In f with x=3 and y=2 - : int = 5 #

Because we can "call" a function with too few arguments (and get another function back, conceptually), Ocaml programmers talk about applying arguments to a function, rather than passing the arguments into the function. If you dont apply as many arguments to the function as the function wants, youve partially applied the function, giving us the fty cent term "partial function application". Another example: remember our deriv function from back in chapter 2:
let deriv f x = ((f (x *. 1.001)) -. (f x)) /. (x *. 0.001);;

Rather than evaluating the derivitive only at a given function, we can actually use this function to create (numerically approximate) derivitive functions directly:
# let x_sq x = x *. x;; val x_sq : float -> float = <fun> # let dx_sq = deriv x_sq;; val dx_sq : float -> float = <fun> # dx_sq 2.;; - : float = 4.00199999999939493 # dx_sq 3.;; - : float = 6.00299999999916611 #

122

Chapter 6. Higher Order Functions Partial function application also allows us to write the composite function- the composite of two functions, f(y) and g(x) is the function f(g(x)). Dening it then becomes easy:
# let composite f g x =

f (g x) ;;
val composite : (a -> b) -> (c -> a) -> c -> b = <fun> # let char_of_float = composite char_of_int int_of_float;; val char_of_float : float -> char = <fun> # char_of_float 65.3;; - : char = A #

Returning Functions
Another consequence of Ocamls concept of how types work shows up when you try to return a function from another function. Consider the following example:
# let alpha x = x + 1;; val alpha : int -> int = <fun> # let beta x = 2 * x;; val beta : int -> int = <fun> # let f test =

if test then alpha else beta ;;


val f : bool -> int -> int = <fun> #

We were expecting Ocaml to return a type of bool -> (int -> int) for function f. But it dropped then parentheses. Why? Because the two types are equivelent. The -> "type operator" binds to the right. The type equivelences work both ways. Notice that the -> operator binding to the right means that applying arguments to functions. Applying arguments to a function happens left to right. So the expression (f true) 1 is the same as f true 1- just like (1 + 2) + 3 is the same as 1 + 2 + 3. In addition to being able to create new functions via partial function application, Ocaml is capable of "papering over" explicit function returns. There is one difference between the function f above and this function:
let f test x = if test then alpha x else beta x ;;

(note the in the name!) And that shows up when we experiment to see when the code is being called:
# let alpha x =

let _ = print_string "In alpha\n" in x + 1 ;;


val alpha : int -> int = <fun> # let beta x =

123

Chapter 6. Higher Order Functions


let _ = print_string "In beta\n" in 2 * x ;;
val beta : int -> int = <fun> # let f test =

let _ = print_string "In f\n" in if test then alpha else beta ;;


val f : bool -> int -> int = <fun> # let f test x =

let _ = print_string "In f \n" in if test then alpha x else beta x ;;


val f : bool -> int -> int = <fun> # f true;; In f - : int -> int = <fun> # f true;; - : int -> int = <fun> # f true 1;; In f In alpha - : int = 2 # f true 1;; In f In alpha - : int = 2 #

Notice that the code for f doesnt get called until there are enough arguments. In the next several examples, I use the List module from the standard library, and also the Cplex module from chapter 3. These examples start giving an idea of the power and exibility of partial function application. If we wanted to write a function that adds a given complex number to all the elements of a list of complex numbers, we might write:
let add_list x lst = (* add x to all the elements of lst *) List.map (Cplex.add x) lst ;;

Cplex.add has the type Cplex.complex_t -> Cplex.complex_t -> Cplex.complex_t. By giving it only one argument- x (which obviously must be of type Cplex.complex_t), we get a function of the type Cplex.complex_t -> Cplex.complex_t. The partially applied function then has the correct number of argument left (one) to be passed in to List.map.

Note that partially applying functions can set some universal types. The function List.map has the type (a -> b) -> a list -> b list. By applying a argument of type Cplex.complex_t -> Cplex.complex_t (like, for example, the partially applied function Cplex.add x), the universal type a is now known to be Cplex.complex.t, as is the universal type b. Weve specialized the universal types, so the returned function has the type Cplex.complex_t list -> Cplex.complex_t list, and not a -> b.

124

Chapter 6. Higher Order Functions We can then partially apply add_list if we want to. If we wanted to write a function to add one to every element of a list of complex numbers, we could write:
let incr_list = add_list (Cplex.make 1. 0.);;

Yes, it is that easy. We only have to call Cplex.make because we dont have a handy constant one lying around (you should x that). Notice we dont actually specify any parameters! The function add_list has the type Cplex.complex_t -> Cplex.complex_t list -> Cplex.complex_t list. By applying only one argument (of type Cplex.complex_t), we get a function of type Cplex.complex_t list -> Cplex.complex_t list, which becomes the type of incr_list. Given a list of complexs, it returns another list of complexs. If you wanted to sum a list of lists of complex numbers, you could simply do:
let sum_list_of_lists = List.fold_left (List.fold_left Cplex.add);;

Usefullness of Partial Function Application


When I rst learned of partial function application, my initial response was something like "cute trick. While it might be neat for dening derivite functions and composition functions, I dont see a real need for it in normal programming." So I thought I would give you an idea on how this feature can be used in a non-trivial way. The rst, and most obvious use is to bind state up with a call back function. Object oriented programs do this by passing in an object with a given member function, the member function is then the callback function, and the object itself is the state, or context, the callback is called with. The Java JFC class ActionListener is a classic example of this- its an interface with exactly one function, actionPerformed. You instantiate a class that implements the interface (provides an actionPerformed member function), and pass the resulting object in. When the time comes, the member function is called on the object. The object itself is the state that the function executes in. APIs for procedural languages have to pass around explicit state pointers, because they cant use the implicit this pointers of object oriented languages. An example of this is the client_data argument to XtAddCallback function in the X Toolkit library- a generic pointer passed in with the callback function that is passed to the function to give it a context to execute in. In Ocaml, the type of the function passed in as a callback could simply be something like event_t -> unit. You could then write a function with a type signature of context_t -> event_t -> unit, and use partial function application to bind the context up with the reference to the function. This has several advantages as a programming pattern. First, it cleans up the API- no extraneous classes or interfaces to dene, no extra parameters. Second, unlike the C style of passing in a void pointer, its type safe. The Ocaml type checker makes sure the parameter passed in to the callback function is the correct type. Third, any function can become a callback, even functions not originally intended to be callbacks. The map, fold, and iter functions are especially good candidates (both to have partially applied functions passed in to them, and to be passed in as partially applied callback functions themselves). In fact, there is nothing you can do to prevent a function from being partially applied even if you wanted to. More generally, partial function application, along with inner functions (and anonymous functions, introduced a little later in this chapter), are used to glue code together. Functions become building blocks, put together in many different ways. Object Oriented design offers the same promise- except the building blocks are objects and not functions. Ocaml recognizes this- the O in Ocaml stands for Object oriented. 125

Chapter 6. Higher Order Functions Its not a question of objects or functions, its objects and functions. I speak more about the relative strengths and weaknesses of partial function application vr.s object orientation, and when to use each, in the chapter on object orientation.

Anonymous Functions
The Function Keyword
By know, you are probably very familiar with this pattern in code:
let foo arg = match arg with ...

Where arg isnt used anywhere else in the function- it only serves to be the basis for the pattern matching. A classic example of this is the List.hd function:
let hd lst = match lst with | h :: _ -> h | [] -> raise (Failure "hd") ;;

Ocaml has a mure succinct way of stating this, using the function keyword. First, a demonstration on how to use the keyword, and then a discussion of what it means. Heres the List.hd function again, only this time using the function keyword:
let hd = function | h :: _ -> h | [] -> raise(Failure "hd") ;;

What the function keyword does is to create an anonymous, unnamed function (like the functions created with partial function application) with one unnamed parameter. That parameter is immediately fed to a pattern matching. The general syntax for this construct is:
function [ | ] pattern [ | pattern ] [ | pattern ]

[ ... ] -> expression [ [ [ [


| pattern | pattern ] | pattern ]

... ]

-> expression ]

[ ...

The pattern matching syntax of the function keyword is precisely identical to the pattern matching of the match/with construct. The only limitation of the function keyword is that you can only match on a single parameter- with the match/with construct, you can match on several different parameters at once, by making them into a tuple. You can, however, match a parameter that is a tuple. 126

Chapter 6. Higher Order Functions The key point in understand why function is more than just a little bit of syntactic sugar is that it is just an expression. This has several implications. First, you can use it to pattern match the last parameter of a function with multiple parameters. For example, we might code up the List.nth function like:
let nth lst idx = let rec loop idx = function | [] -> raise (Failure "nth") | h :: t -> if idx == 0 then h else loop (idx - 1) t in loop idx lst ;;

Note that the function keyword only pattern matches the last argument, so for our inner loop function, we had to reverse the order of the arguments. Another consequence is that you can use the function keyword to pattern match multiple arguments, by using multiple levels of the function keyword. Remember that to the right of the ->s in the pattern matching are expressions- they can use the function keyword as well. For example, given our original number_t denition from chapter 4:
type number_t = | Int of int | Float of float ;;

We might write a function to add two number_ts like:


let number_add = function | Int(a) -> begin function | Int(b) -> Int(a + b) | Float(b) -> Float((float_of_int a) +. b) end | Float(a) -> begin function | Int(b) -> Float(a +. (float_of_int b)) | Float(b) -> Float(a +. b) end ;;

Note how we have to contain the inner function expressions. This construct is rarely used, however, as it tends to actually create more unwieldly code (as the above example also demonstrates). But most important of all, you dont have to ever assign the newly created variable a name. We can simply use it as-is anywhere where we need a function that takes one argument. For example, to write a function to convert a list of number_ts (from above) to a list of oats, we can write:
let numlist_to_floatlist = List.map (function | Int(i) -> float_of_int i | Float(f) -> f ) ;;

127

Chapter 6. Higher Order Functions

Another consequence is that, being an expression, you can "shoehorn in" local variables before the function expression:
let f x = let t = x *. x in function | Int(i) -> t +. (float_of_int i) | Float(f) -> t +. f ;;

Note that the variables are evaluated before the function expression is evaluated, and that the function expression returns a reference to the function- which Ocaml can then implicitly dereference. But this can lead to the variables being evaluated "early":
# let f x =

let _ = print_string "In f and evaluating the variables!\n" in function | true -> abs x | false -> x ;;
val f : int -> bool -> int = <fun> # f 3;; In f and evaluating the variables! - : bool -> int = <fun> #

Note that, because they use the same syntax, match/with expressions inside a function expression have to be protected with begin and end (or parentheses) just like they do in match/with expressions.

The fun Keyword


Another way to dene functions as simple expressions is the fun. Like the function keyword, the fun keyword denes a function with a single argument. The syntax for this is:
fun argname -> expression

The syntax above works exactly the same as:


function | argname -> expression

Note that the fun keyword does no provide all the power of pattern matching that the function keyword does. All you can do is name the parameter. The fun keyword is much more syntactic sugar than the function keyword is, but it still has its uses. As example, here is a function that adds a constant to every member of a list of oating point numbers:
let add_to_list x = List.map (fun y -> x +. y);;

Like function, multiple fun expressions can be chained together to create a function with multiple arguments. For example, a function to add two lists of oats together might be written:
let add_lists = List.map2 (fun x -> fun y -> x +. y);;

128

Chapter 6. Higher Order Functions

Likewise, because its an expression, you can assign the function a name using a let expression, like:
let fpadd = fun x -> fun y -> x +. y;;

More Pattern Matching


The as Keyword
Often times, you want to pattern match on an expression (often just a sub-expression) but still be able to refer to the whole expression. For example, in pattern matching on a list of tuples, you might want to pattern match on the members of the rst tuple in the list, but also be able to refer to the tuple as a whole, and even the list as a whole. As an example of this in action, consider the case where you are representing vectors as lists of int * float tuples. We might write the vector add function like:
let vector_add a b = let rec loop accum a b = match a, b with | [], _ -> List.rev_append accum b | _, [] -> List.rev_append accum a | ((a_idx, a_val) :: a_rest), ((b_idx, b_val) :: b_rest) -> if a_idx < b_idx then loop ((a_idx, a_val) :: accum) a_rest b else if a_idx > b_idx then loop ((b_idx, b_val) :: accum) a b_rest else loop ((a_idx, (a_val +. b_val)) :: accum) a_rest b_rest ;;

Note that in two different cases, were allocating a new tuple with the exact same values of a tuple we already have- (a_idx, a_val) and (b_idx, b_val). We could do a two-level pattern matching, matching on the lists, and then having an internal pattern matching to match on the tuples, but Ocaml has a more elegant way to do this. We can use the as keyword to give a name to an entire subpattern. Adding the keyword as followed by a variable name to the end of a pattern means the
value matched by the entire pattern is stored in the given variable.

So, for example, if we wanted to match both the tuple as a whole and the individual elements of the tuple, we could use the pattern (a_idx, a_val) as a_tuple. Here a_tuple holds a reference to the tuple, a_idx its rst element, and a_val its second element. We need the parens, because otherwise the patern a_idx, a_val as a_tuple would get matched like a_idx, (a_val as a_tuple), not what we meant at all. Also, note that patterns using the as keyword are themselves patterns, and thus can be subpatterns to larger patterns yet. So, if we wanted to match a list of tuples, and have both the tuple elements of the rst tuple, and the tuple itself, in variables where we can access them, we could use the pattern ((a_idx, a_val) as a_first) :: a_rest. This allows us to rewrite our vector add routine so we dont reallocate tuples unnecessarily like this: 129

Chapter 6. Higher Order Functions


let vector_add a b = let rec loop accum a b = match a, b with | [], _ -> List.rev_append accum b | _, [] -> List.rev_append accum a | (((a_idx, a_val) as a_first) :: a_rest), (((b_idx, b_val) as b_first) :: b_rest) -> if a_idx < b_idx then loop (a_first :: accum) a_rest b else if a_idx > b_idx then loop (b_first :: accum) a b_rest else loop ((a_idx, (a_val +. b_val)) :: accum) a_rest b_rest ;;

The as literal keyword to name subpatterns can be used in both match/with expressions, and in function expressions. Also note that like every thing else in pattern matching, as binds to the left. This means the pattern a, b as c binds like (a, b) as c, and a :: b as c binds like (a :: b) as c. One last minor nitpick. You can use as naming on the discard pattern. The pattern _ as x matches anything, and places the value in the variable x. Why youd want to do that is anyones guess, as the pattern x works exactly the same way.

The when Keyword


Consider writting an absolute value function for our number_t type introduced earlier. We might write code like:
let abs_number = function | Int(i) as e -> if i < 0 then Int(~-i) else e | Float(f) as e -> if f < 0. then Float(~-.f) else e ;;

A better way to implement that function would be to use a gaurd. A gaurd on a pattern is an expression that evaluates to a boolean. When the pattern is tested, the gaurd expression is evaluated, and the pattern is only matched if the expression is true. Gaurd expressions are only permissiable on the top level pattern- you can not apply them to subpatterns. On a function or match/with construct, you follow the pattern (right before the ->) with the keyword when and the gaurd expressions. The gaurd expression can use variables dened in the pattern itself. The full syntax for the match/with construct is:
match expression with [ | ] pattern [ when gaurd-expr ] [ | pattern [ when gaurd-expr ] ] [ | pattern [ when gaurd-expr ] ]

[ ... ] -> expression

130

Chapter 6. Higher Order Functions


[ [ [ [
| pattern [ when gaurd-expr ] | pattern [ when gaurd-expr ] ] | pattern [ when gaurd-expr ] ]

... ]

-> expression ]

[ ...

So we can rewrite our abs_number function more succinctly list:


let abs_number = function | Int(i) when i < 0 -> Int(~-i) | Float(f) when f < 0. -> Float(~-.f) | e -> e ;;

Note that the last pattern, e, matchs both Int(i) when i >= 0 and Float(f) when
f >= 0 in one pattern.

The Limitations of Pattern Matching


Overall, pattern matching is a sweet bit of syntactic sugar for Ocaml. It can be used to express surprisingly complex algorithms succinctly and obviously. But, as with all syntactic sugar, there are limitations. You can get functions that are so complex that pattern matching breaks down- the more patterns that are matched, the more complexity the function has. Especially combined with pattern matchings fall through nature, which makes the order of the patterns important. I realized this after I wrote a function that matched 27 different patterns, with subtle fall through rules, lots of named subpatterns, and lots of gaurd expressions. The function was unmaintainable- by the time I was done writting it, even I couldnt read it. I rewrote it using if statements instead, and it became much more clear (it was still a hugely complex function- true simplicity was acheived until later when I split it into multiple different functions- but at least it was a manageable complexity). Of course, it turned out later that in all the complexity of the pattern matching, I had missed cases. My rule of thumb is that whenever the entire pattern matching doesnt t on a single screen, or whenever I start hitting more than about half a dozen patterns to match, I either switch over to using if/then/else clauses and/or I break the function up into multiple different functions. Simply because you can do something doesnt mean you should do it.

The Dangers of Default Matches


While Im holding forth on pattern matching, Id also like to mention the dangers of default matching. Consider our implementation of the abs_number function:
let abs_number = function | Int(i) when i < 0 -> Int(~-i) | Float(f) when f < 0. -> Float(~-.f) | e -> e ;;

Now, what happens when, sometime later, we expand the denition of a number_t to include complex number?
type number_t =

131

Chapter 6. Higher Order Functions


| Int of int | Float of float | Complex of float * float ;;

What happens is that our abs_number function breaks. Worse than that, it breaks silently. For complex numbers, it acts like the identity function. The problem is that the e pattern in abs_number is a catch-all pattern, and so catches the newly dene Complex numbers as well as the non-negative Ints and Floats. All the type system does is to make sure that the behavior of the function is denedit doesnt make sure the behavior is correct. A strong type system doesnt replace unit testing, it augments it. As a general rule, I only do default patterns for types visible only to the local module. The assumption here is that if youre changing the type, you should pass through the rest of the le to see if there are any functions you need to change at the same time. On the other hand, I am a huge fan of abstract types and accessor functions.

Exercises
1. Exercise 1.

132

Appendix A. Garbage Collection Fundamentals


Most programming languages nowadays provide for automatic memory management, i.e. garbage collection. In these languages, the programmer does not have to specify precisely when an object allocated on the heap is to be freed- the execution environment gures out when the object is no longer needed and frees it automatically. This generally solves the two most common errors with memory managementmemory leaks (not freeing objects that are no longer needed) and dangling pointers (accessing freed objects). Despite its popularity and utility, the technology of garbage collection, its strengths and weaknesses, is still not widely understood. Ocaml, and indeed the entire family of functional programming languages, are much more dependent on garbage collection than more traditional languages. Their inclination to share data between different data structures (for example, sharing tails between multiple different lists) makes determining when an object is no longer used almost impossible for the programmer. Functional programs also tend to have a very high allocation rate- the way you modify objects is that you allocate new copies of them. And because objects cant be modied once they are initialized, standard memory allocation optimizations (like object pools) cant be applied. One study Ive seen says that ML programs allocate an average of about one word every six program instructions. This is an allocation rate that would be considered insane in most other programming languages. However, most of the objects allocated become garbage very quickly. Because of this, functional programs require garbage collection, and are very sensitive to their performance. So functional languages have been driving research into garbage collection in a way imperitive languages havent. That research paid off in the 1980s with the development of garbage collection systems fast enough to not be a performance drag on functional languages, and even to possibly give functional languages a performance edge. But rst, what do we mean by garbage? The intuitive denition is that its memory we dont need anymore- objects allocated on the heap which will never again be accessed. Unfortunately, this denition depends upon being able to predict the future behavior of the program. So all garbage collection systems instead go for a more conservative denition: a object is garbage if there is no sequence of valid pointer dereferences that reach the object. In other words, an object is garbage if you cant get there from here. This denition gave rise to the rst, and still most popular, garbage collection technology: reference counting. Each object on the heap kept a counter of how many pointers were pointing at it. When the program had a new pointer point at the object, the counter was incremented. When a pointer that pointed at an object was modied to point somewhere else, or freed, the counter for that object was decremented. When an objects counter reached zero, it was freed- since an object with no pointers to it obviously couldnt be reached from anywhere. Reference counting garbage collection is popular because its simple to implement and easy to understand. However, it has a whole host of problems- most of which have been xed with different GC algorithms. The rst problem is performance, rather surprisingly. Every time you set or change a value of a pointer, you end up incrementing a counter, or decrementing a counter and checking to see if the count is 0 and freeing the object if it is. Often, the program needs to do both (increment one counter, decrement and check for 0 another). This cost is spread out more or less evenly over the entire program, and it can be drowned out by other inefciencies (especially in interpreted programs). But it exists, and its real. Another, and more obvious, problem is circular data structures. If you have two objects, A and B, where A has a pointer to B, and B has a pointer to A, reference counting garbage collection wont be able to free either, even if A and B as a pair cant be reached from anywhere else. After all, they both still have valid pointers pointing at them. 133

Appendix A. Garbage Collection Fundamentals The circular data structure problem lead to the next advancement in garbage collection- the mark and sweep algorith. The idea here is that there are certain variables the program can access directly, without having a pointer to them- local and global variables, mostly. All objects on the heap that can be reached must be reached by some chain of pointer dereferences starting with one of these directly accessible objects. This means the question of what is garbage and what isnt becomes a graph theory problem- a question of reachability. This gives rise to what is called the 3-color mark & sweep algorithm. All objects on the heap can be colored one of three colors- black, white, and gray. The rst phase of the garbage collection is called the mark phase. We start by coloring all objects in the heap black. Then we go through the set of directly accesible objects (local and global variables) and color all the objects they point to gray. Then, while we still have gray objects, we pick one gray object. If it has pointers to any objects on the heap colored black, we color those objects gray. When were done, we color our chosen object white. Note that white objects never have pointers to black objects. White objects can have pointers to gray objects, and the gray objects to black objects. Note that if we view our heap as a graph, with the nodes of the graph being objects on the heap, and an edge from node i to node j means that object i has a pointer to object j, then the gray nodes are our "wavefront" in a breadth-rst traversal of the subset of the graph which is reachable. Each object is touched at most three times- once to color it black, once to color it gray, and once to color it white. Which means that worst case the algorithm is O(N) to traverse the entire heap. When were done with the mark phase, all objects on the heap are colored either black or white. The objects colored white are all reachable by one or more sequences of valid pointer dereferences, and so are deemed not garbage. All the objects still colored black are not reachable, are are thus deemed garbage. So we "sweep" the heap, freeing all objects still colored black. There are several things to note about the mark & sweep algorithm. The rst is that circular data structures are no longer a problem. If object A points at object B and vice versa, this doesnt matter. If either can be reached, obviously both are reachedbut both are still only visited at most three times. If object A is reached rst, itll be colored gray while B stays black. Then, when A is colored white, B (which A points at) will be colored gray. When B is colored white, A will already be white, and wont be visited again. If neither can be reached, but objects will remain colored black- and will both be freed in the sweep. Because of this, reference counting garbage collection is often backed up by a mark & sweep algorithm, to "catch" circular data structures. But mark & sweep still has problems. The rst problem is that you can no longer depend upon what order objects are freed in. With reference counting (and with explicit freeing with destructors), if object A points to object B (and B doesnt point to A), you are gaurenteed that A will be freed before B is. With mark and sweep, what order objects A and B are freed in depends upon what order the sweep phase nds them in. Algorithms which depend upon the order objects are freed in no longer work. But a bigger problem, by far, is that when you garbage collect, you garbage collect the entire heap. The amount of work each garbage collect does is of the same magnitude of the size of the heap. This causes the infamous pauses and stuttering of garbage collected programs. Worse yet, collecting the whole heap is inefcient. Long lived objects get marked and unswept again and again in their lifetime. The solution to this problem is generational collection. The heap is divided into "generations", ranked by age. Objects are always allocated in the youngest generation. Younger generations are collected more aggressively than older generations- so long lived objects get collected less often, if theyve lived a while, theyre like to live a while longer. If you keep track of intergenerational pointers (which are reasonably rare in practice, especially in languages like Ocaml), you dont even have to scan older generations. This means the garbage collector is nding a high percentage of garbage for the number of items it scans- its being very efcient at its work. 134

Appendix A. Garbage Collection Fundamentals But even generational mark and sweep garbage collection still isnt fast enough. The problem is that it- and reference counting GC, and even malloc/free or new/delete memory management, fragments memory. After the program has been running for a while, youll have blocks of allocated memory interlaced with blocks of free memory. When we go to allocate a new block of memory we have to traverse all the varied free blocks of memory, looking for one big enough to t. This slows down allocation. You can move the cost around, but the fundamental cost remains. Ocamls solution to this is to use copying garbage collection. Before collecting a generation, Ocaml allocates a whole new space. As objects are determined to be nongarbage, in addition to being colored (at least conceptually), they are copied out of the old space and into the new space. All pointers to the old object are then updated to point to the new object, and the old object is now garbage. When the marking phase is done, all live objects have been moved to the new space, and the entire old space is freed in one chunk. The biggest advantage to a copying garbage collector is that the heap is always compact- all the currently allocated blocks are contiguous. This means allocating on the heap is very cheap. The code Ocaml emits to allocate a small block of memory is approximated by the following C code:
extern char * caml_young_ptr; extern char * caml_young_limit; void * alloc(size_t size) { while (1) { register r = caml_young_ptr; r -= size; caml_young_ptr = r; if (r < caml_young_limit) { caml_call_gc(); } else { return (void *) r; } }

On the x86, this works out to be ve simple instructions (two moves, a subtraction, a comparison, and a branch) in the common case when the garbage collector is not called. Another, unexpected, performance benet arose from copying garbage collection. It provides for better cache locality for code running on it. This arises from two effects. The rst is that the youngest generation generally simply resides in cache. And in Ocaml code, the major of memory references are to recently allocated objects (i.e. objects still in the youngest generation). The other effect is that in copying the objects, the garbage collector also sorts the objects, so that objects that reference each other are more likely to be near each other in memory. Consider a linked list, for example. With classic allocation stratagies, the list elements would wind up where ever there was room for them. The classic response to this is to convert the lists into arrays- but this increase the complexity of the code, makes it harder to maintain the code, and limits the exibility of the data structure. With a copying garbage collection, once the garbage collector nds the head of the list, it will simply walk down the list, copying each member of the list into its new home, which just happens to be contiguous to the rest of the list. If the list elements are small enough to have multiple elements t on a cache line, a single cache hit would bring in multiple elements when next the list is walked. Even if the list elements are not small enough to t multiple on a cache line, modern CPUs are optimized for regular memory accesses, like those create in walking down an array. Once the list is collected once, walking down the list looks to the CPU exactly like walking down an array. With the cost of a cache miss rising steadily (as I write this, modern processors generally take 100 clock cycles and more to load a cache line from 135

Appendix A. Garbage Collection Fundamentals memory- some processors take up to 350 clock cycles), any decrease in cache misses can signigantly improve code performance. The cost of actually copying the object is minimal, compared to the cost of just scanning the object for pointers. Reads and writes to cached memory are cheap- the main cost is reading the old object into cache the rst time. Using a generational scheme is even more important for copying collectors, to limit the amount of unnecessary copying of long-lived objects they do. Since the youngest generation generally lives in memory anyways, the cost of a collection of the youngest generation (by far the most common type of collection) is minimal- the memory is already loaded into cache.

136

You might also like