My first experience with OCaml
Recently I’ve been motivated to learn more about functional programming and the name OCaml came up quite a few times. I have seen some praise about it from the people I follow on social media and decided to give it a try. I finally finished a small project in OCaml and would like to share my first impressions while the memory is still fresh.
Please keep in mind that I am relatively new to functional programming and mainly develop in Go nowadays, previously PHP, Python and Javascript. Therefore I will try not to make any conclusions but rather share my perception.
Resources
I started with the official documentation, which was enough to get an overview of the language and its type model, install the development tools and compile a small “hello world” application. However as I dove deeper I discovered other resources as well, such as Real World OCaml, OCamlverse, OCaml Operators where I could find the information I couldn’t find on the official page.
It all gave me a feeling that there is a need for better-structured resources (especially for newcomers), which should probably live on the official website. Ideally with some interactive step-by-step language tour.
Program
My idea was to build a daemon that receives a Yaml configuration with the list of websites and monitors them concurrently, the results are stored in SQLite database afterward.
|
|
You can find the full source code here.
Installing OCaml
OCaml comes with two compilers: one translating sources into native binaries and another turning sources into a bytecode format. It offers a great runtime performance and has always been exceptionally reliable and stable.
To start compiling/running our program we need to install the opam package manager, which also installs the OCaml compiler.
I am using macos, so the commands for me were the following:
|
|
This worked without any issues on my machine, and then I was able to install some platform and build tools, such as dune and utop.
|
|
utop is an interactive REPL which you can run in your terminal. Honestly, I haven’t used it much during the development time and just built my program with dune.
When it comes to dune, I spent some significant time understanding how it works and battling with different dependencies errors. Generally it felt not intuitive to work with multiple dune
files spread across your codebase.
|
|
The following command created a project directory which looks similar to this:
|
|
As you can see there are 3 dune
files, one dune-project
file and one websites_monitoring.opam
file, all of them responsible for building and managing dependencies. It felt like extra work when I had to change lib dependencies, I had to add them to both lib/dune
and dune-project
files as well as run dune build @install
which updates websites_monitoring.opam
.
Once I added some pseudo code it was time to build an executable:
|
|
This didn’t work for me because there was no default switch installed, even though the documentation say that there should be one. Switches are isolated OCaml environments, similar to Python’s virtualenv
. I didn’t dive into this much and just chose some switch from the list of available options:
|
|
Now I was able to compile and run my program, the binary is located in ./_build/install/default/bin/monitoring
.
All in all, it seems that dune is an interesting tool, but I also think it needs a better documentation and simplified developer experience.
Language Syntax and Types
OCaml is type-safe and statically type language, which means it detects type errors at compile time. But what’s cool is that there is a type inference, so you do not have to write type information everywhere. The compiler automatically figures out most types. This can make the code easier to read and maintain.
For example in my code I have this function to get a config filename from the environment variable:
|
|
I could omit : string
part and compiler still understands that it returns a string:
|
|
There are user-defined types in OCaml such as variants, records and aliases. Here is the example of custom website type that corresponds to the yaml configuration of my program:
|
|
The language has lots of operators, and it took me some time to understand how they work. This webpage helped me a lot.
Preprocessors and PPXs
Preprocessors are programs meant to be called at compile time, so that they alter the program before the actual compilation.
In my case they’ve been really helpful to decode a Yaml file into a custom type after I spent few hours to do the same without them. I used ppx_deriving_yaml for that:
|
|
Concurrency & Parallelism
Since I have the list of websites I wanted to process each website concurrently with some interval between runs. I couldn’t use Domains
for that, because the amount of websites could be larger than the amount of cores available.
It seems there are many libraries to implement concurrency or parallelism in OCaml and it’s not obvious what to use. I think that it’s the case with OCaml in general, the stdlib is quite minimal and there are many small independent libraries. Which is not an issue maybe, but hard to navigate especially for newcomers.
So I went with domainslib library:
|
|
I actually spent a lot of time battling the segmentation fault
error because the crawl_website
function used cohttp-lwt
under the hood. After I switched crawl_website to be synchronous everything worked well.
It was actually hard to find a library that does synchronous HTTP calls, everything uses LWT or Async.
Tests
There seem to be different ways of defining your tests, but I went with a simple approach of running the tests manually in let():
|
|
And then I could run:
|
|
Containerization
ocaml/opam
Docker image works well and I was able to quickly put my application into a container. I don’t plan to deploy this app, so didn’t worry much about multi-stage builds for now, just wanted to make sure it works.
|
|
Conclusion
Will I be confident deploying this program to production? Probably not. But it kinda works. I would like to understand the domainslib better and maybe try few other approaches. And probably run some performance tests.
Was my journey learning OCaml and doing first steps easy? Probably not. I wish the documentation was easily accessible for the newcomers and structured better. Ideally in one place.
But definitely it was enjoyable and I am 100% sure I will be continuing playing with OCaml and maybe publish some libraries.
You can find the full source code here.