OCaml Transition Guide
This document is a transition guide for students in the Swarthmore CS curriculum. It contains examples of code written in Python, C++, and OCaml.
- Variable Declaration
- String Concatenation
- Standard User Interaction
- Function Declaration
- Recursive Functions
- Loops
- Using Libraries
- String Formatting and Printing
- Exceptions
- Tuples
- Absent Values
- Polymorphic Dictionaries
Variable Declaration
- Python
x = 5 y = "Hello"
- C++
int x = 5; string y = "Hello";
- OCaml
let x : int = 5;; let y : string = "Hello";;
Note: the OCaml compiler is capable of some type inference, which allows it to guess which types you meant. The above can be written
let x = 5;;
let y = "Hello";;
You are free to use type inference as much as you want, but instruction in this course will avoid it (at least early on) to reduce confusion.
String Concatenation
- Python
s1 = "Hello," s2 = " world!" result = s1 + s2
- C++
string s1 = "Hello,"; string s2 = " world!"; result = s1 + s2;
- OCaml
let s1 : string = "Hello,";; let s2 : string = " world!";; let result : string = s1 ^ s2;;
Standard User Interaction
- Python 3
print("Please enter a line of text.") text = input() print("I like this text: " + text)
- C++11
cout << "Please enter a line of text." << endl; string text; getline(cin, text); cout << "I like this text: " << text << endl;
- OCaml
print_endline "Please enter a line of text.";; let text : string = read_line ();; print_endline ("I like this text: " ^ text);;
Function Declaration
- Python
def sum_two(x,y): return x + y
- C++
int sum_two(int x, int y) { return x + y; }
- OCaml
let sum_two (x : int) (y : int) : int = x + y ;;
Again, type inference can be used to write the above as
let sum_two x y = x + y;;
This course avoids relying on type inference especially in the case of functions, as the inference rules can be quite confusing to newcomers at first.
Recursive Functions
- Python
def count_down(n): if n == 0: print("Liftoff!") else: print(n) count_down(n-1)
- C++
void count_down(int n); void count_down(int n) { if (n == 0) { cout << "Liftoff!" << endl; } else { cout << n << endl; count_down(n-1); } }
- OCaml
let rec count_down (n : int) : unit = if n = 0 then print_endline "Liftoff!" else print_endline (string_of_int n); count_down (n-1) ;;
Loops
- Python
n = 0 while n < 10: print(n) n += 1
- C++
int n = 0; while (n < 10) { cout << n << endl; n += 1; }
- OCaml
let rec loop (n : int) = if n < 10 then print_endline (string_of_int n); loop (n+1) else () in loop 0
Technically, OCaml does have mutable variables and while
loops. For this course, however, we will rely heavily upon our ability to think recursively and we will avoid those language features whenever possible. Much of the code we write will be much better expressed recursively!
Using Libraries
- Python
import random x = random.randint(0,4)
- C++
#include <cstdlib> #include <ctime> using namespace std; ... srand((int)time(0)); int x = rand() % 4;
- OCaml
Random.self_init ();; let x : int = Random.int 4;;
String Formatting and Printing
- Python
Python has two forms of built-in string formatting. The first we discuss in CS21:
print("I am %d years old." % age)
That form is somewhat dated, however, and we are recommended to use the new form:
print("I am {} years old.".format(age))
- C++
C++ string formatting is done largely through the
iostream
library. However, theprintf
library from C is also available:#include <cstdio> ... printf("I am %d years old.", age);
- OCaml
OCaml provides a
Printf
module similar to that of C:open Printf;; ... printf "I am %d years old." age;;
Exceptions
- Python
try: do_some_stuff() except SomeError as e: handle_error() except OtherError as e: handle_other_error()
- C++
try { doSomeStuff(); } catch (SomeError& e) { handleError(); } catch (OtherError& e) { handleOtherError(); }
- OCaml
try do_some_stuff () with | SomeError _ -> handle_error() | SomeOtherError _ -> handle_other_error()
Tuples
- Python
Tuples are a built-in feature of Python, but we avoid discussing them in CS21.
my_pair = (4,"yes") my_triple = ("stuff",True,1)
- C++
C++ does not have tuples as a language feature. In CS35, we use a standard library class called
pair
:pair<int,string> myPair(4,"yes"); // statically allocated
The C++ STL also has a more general class called
tuple
:tuple<string,bool,int> myTriple("stuff",true,1); // statically allocated
- OCaml
OCaml has a built-in syntax for tuple similar to Python’s.
let my_pair : int * string = (4,"yes");; let my_triple : string * bool * int = ("stuff",true,1);;
Absent Values
- Python
Python’s built-in
None
value typically serves as a way of signaling no value (without raising an exception).def first_element_of_list(the_list): if len(the_list) == 0: return None else: return the_list[0]
- C++
In C++, it is not uncommon (though not very tidy) to use
NULL
to refer to the absence of a value.template <typename T> T* firstElementOfList(List<T*>* theList) { if (theList->getSize() == 0) { return NULL; } else { return theList->get(0); } }
Note, however, that this only works if the value is a pointer.
- OCaml
In OCaml, a built-in type declaration
type 'a option = None | Some of 'a
allows (and requires) you to be explicit about when “no value” is a possible answer.let first_element_of_list (the_list : 'a list) : 'a option = match the_list with | [] -> None | first :: rest -> Some(first) ;;
Polymorphic Dictionaries
- Python
In Python, there is no static type system. As a result, you can store any key-value mappings you like in any dictionary. If you mess up, it’s your problem.
my_dictionary = {} my_dictionary[1] = "one" my_dictionary[2] = "two" print(my_dictionary[1])
- C++
C++ uses templates for polymorphic data types. C++ templates are usually instantiated implicitly, meaning that you simply need to name the data type you want to use.
std::map<int, string> my_dictionary; my_dictionary[1] = "one"; my_dictionary[2] = "two"; cout << my_dictionary[1] << endl;
- OCaml
OCaml uses functors, which are similar to C++ templates but must be invoked explicitly. In the case of dictionaries, this means that we need a module which defines the type of the key and how it is compared. We only need to define our dictionary (e.g.
IntMap
) once and we may then use it anywhere, but there is no implicit instantiation similar to C++.module IntKey = struct type t = int let compare = Pervasives.compare end;; module IntMap = Map.Make(IntKey);; ... let empty_dictionary = IntMap.empty in let singleton_dictionary = IntMap.add 1 "one" empty_dictionary in let doubleton_dictionary = IntMap.add 2 "two" singleton_dictionary in print_endline (IntMap.find 1 doubleton_dictionary);;