Getting started with OCaml and Functional Programming
Sandro Maglione
Get in touch with meFunctional programming
6 December 2023
•15 min read
Sandro Maglione
Functional programming
Get started writing functional programming code using OCaml:
- Install OCaml and setup a full developer environment (formatting, compiling, auto complete)
- Learn how to create an OCaml project using
dune
- Learn how to write functional code using OCaml (pattern matching, types, piping)
Installing OCaml
These are the steps I followed to install OCaml:
- Make sure
gcc
,build-essential
,curl
,unzip
, andbubblewrap
are installed
- Run the install command
The next step is initializing opam
:
opam
(OCaml Package Manager) is a package manager for OCaml (similar tonpm
for javascript)It allows to install and manage packages and dependencies.
This process will ask you to setup the ocaml
command in PATH
:
It will then install the compiler and all relative dependencies:
Setting up developer environment
I also installed some other dependencies suggested to run a productive developer environment. These are tools that take care of compilation, auto completion, formatting:
This command will install some new dependencies:
Finally, I installed the OCaml Platform
extension for VSCode.
This is all to start being productive with OCaml on VSCode.
There is more.
Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.
Create your first OCaml program
An OCaml file ends with the .ml
extension. You can create a single main.ml
file and add some code:
You can then run the ocaml
command to execute it:
This is your first program in OCaml: "Hello, World!".
Create a project using dune
dune
is the standard build system for OCaml.
dune
allows to create a full OCaml project and build executables, libraries, run tests, and much more.
We can create a new project running the dune init
command:
_build
: Built files (after runningdune build
)bin
: Executable filelib
: Modulestest
: Testing
Initial project structure after running dune init
We can then compile the project running the build
command:
dune will compile the project and generate some files inside the _build folder
Finally, we can execute the program by running the exec
command:
This is the standard workflow when working with
dune
:
- Write implementation inside modules in
lib
- Import and use modules for executables inside
bin
- Run the
dune build
command- Run the
dune exec ...
command
Code formatting
We need to create a new .ocamlformat
file to enable auto-formatting:
Now we can execute the formatting command:
Final dune
project setup and files
The final project contains the following files:
dune-project
: Defines general config and informationaoc_ocaml.opam
: Autogenerated fromdune-project
, do not touch!.ocamlformat
: Adds formatting. Define the style type asprofile
lib/dune/dune-project
: Modules configurations,name
defines the export to use insidebin
(in the examplename aoc
means that you useAoc
as module insidebin
)
bin/dune/dune-project
: Execution configurationpublic_name
defines the command to run withexec
(in the examplepublic_name aoc
means callingdune exec aoc
)name
references the name of the entry file to execute (in the examplename main
means executing themain.ml
file)libraries
references thename
assigned in thelib/dune/dune-project
file
Final project configuration for a complete dune program
With this setup we run the following commands in sequence to format, build, and execute the program:
There is more.
Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.
OCaml and Functional Programming
Any OCaml program is based on functions composed together.
You define a function using let
followed by the function name and parameters:
OCaml is very smart in inferring types for you, no need to write them manually.
In the
sum
function OCaml knows thata
andb
areint
because we use the+
operator (which works onint
types).
OCaml embraces functional programming. The key features are:
- All variables are immutable
- "Mostly pure" (OCaml allows side effect through things like references and arrays)
- Function composition
- Functional types like
Option
andEither
- Powerful pattern matching
Write OCaml code
Let's solve a puzzle to learn how to write OCaml code:
Given a string, combine the first and last digit to form a single two-digit number.
For example 1abc2
(12), pqr3stu8vwx
(38), a1b2c3d4e5f
(15), treb7uchet
(77).
This puzzle comes from Advent of code 2023 (day 1)
When working with functional programming you usually start by defining types (states).
In this example our program can be in 2 states:
- No digit yet found (
Empty
) - Digits found (
Full
)
We can implement this in OCaml using type
:
digits
: Name of the typeEmpty
: State without parameters (notice how we did not add any type after it)Full
: State that contains a pair of int (int * int
represents a tuple, meaning a value that requires 2int
, also called Product Type, hence the*
syntax)
We can now create instances of the digits
type using Empty
and Full
:
Pattern matching
Another feature that is powerful and used everywhere in OCaml and functional programming is pattern matching.
For example we can pattern match on a value of type digits
.
Since the definition of
digits
allows for only 2 states, OCaml will make sure that we matched all possible states.
Pattern matching in OCaml uses the match ... with
syntax:
The digits_sum
function takes a dig
of type digits
as input (the type is inferred by OCaml because we are matching on Empty
and Full
).
We then use match
to return some value for each state:
Empty
: No digits available, return0
Full
: We have both digits so we can combine them
For example from the input
1abc2
we will extractFull (1, 2)
, and then usingdigits_sum
:(1 * 10) + 2 = 12
When we have a single input that we pattern match directly we can shorten the code by using function
:
Collecting digits
Solving the problem requires to iterate over all the characters in the string and extract the first and last digit.
We can achieve this using digits
and pattern matching. We define a new collect_digits
function that takes 2 parameters:
dig
: The currentdigits
value (initiallyEmpty
)str
: The current character (of typestring
)
First we use the int_of_string_opt
function to convert str
to int option
.
Here
option
will be:
None
ifstr
cannot be converted toint
Some
containingstr
converted toint
whenstr
represents a valid numberYou can learn more about the
Option
type here: Functional Programming Option type - Introduction
We then pattern match on both dig
and int_option
:
Empty
+None
: No digits yet, and no new digit found, returnEmpty
Empty
+Some
: No digits yet, and we found a new one, returnFull
containing the found digitn
Full
+None
: We already have some digits, and no new digit found, return sameFull
Full
+Some
: We already have some digits, and we found a new one, therefore update the last digit we the new one found
Using pattern matching we can be sure that we defined all possible combinations.
We have 4 combinations: 2 digits (
Empty
,Full
) x 2 option (None
,Some
).OCaml will inform us if we forget to match a possible case.
Folding in functional programming
We now need to extract each character from an input string
.
We can do this using String.fold_left
:
fun x -> ...
represent an anonymous function (lambda)- Calling a function in OCaml is done by writing the name of the function (
collect_digits
) followed by its parameters (no need of parenthesis) Char.escaped
is used to convertchr
(of typechar
) to astring
- We pass
Empty
as initial state for folding
String.fold_left
will iterate over all characters inside source
from left to right and provide them as char
inside fun
.
For each character we call collect_digits
. read_chars
will therefore return a single digits
value containing the digits extracted from the string.
Piping
Finally we can compose all the function we defined to create the final program
.
We use the pipe operator |>
to achieve this:
program
takes the string source
as input. Using |>
we apply source
to read_chars
, the result is then passed to digits_sum
.
The above code is equivalent to the following:
Function composition is at the core of functional programming and also in OCaml.
Every program is a series of small function composed together to create the final result.
Done! The final solution is the following:
This is it!
You have now a complete configuration to start writing your next OCaml project. You also learned how functional programming works and how to write functional code in OCaml.
If you are interested to learn more, every week I publish a new open source project and share notes and lessons learned in my newsletter. You can subscribe here below 👇
Thanks for reading.