<feed xmlns="http://www.w3.org/2005/Atom"><id>https://ocaml.org/planet.xml</id><title type="text">The OCaml Planet</title><updated>2025-01-07T12:00:00-00:00</updated><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2025.01.07.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#1&quot;&gt;Playing with Windows on ARM64&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#2&quot;&gt;Opam repository archival, Phase 1: unavailable packages&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#3&quot;&gt;CCL: Categorical Configuration Language&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#4&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#5&quot;&gt;&quot;Cram tests: a hidden gem of dune&quot; and &quot;Snapshot tests for your own ppx&quot;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2025.01.07.html#6&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2025.01.07.html</id><title type="text">OCaml Weekly News, 07 Jan 2025</title><updated>2025-01-07T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://chshersh.com/atom.xml" rel="alternate"/><id>https://chshersh.com/atom.xml</id><title type="text">Dmitrii Kovanikov</title></source><link href="https://chshersh.com/blog/2025-01-06-the-most-elegant-configuration-language.html" rel="alternate"/><content type="html">&lt;p&gt;CCL: Categorical Configuration Language&lt;/p&gt;
</content><id>https://chshersh.com/blog/2025-01-06-the-most-elegant-configuration-language.html</id><title type="text">The Most Elegant Configuration Language</title><updated>2025-01-06T00:00:00-00:00</updated><author><name>chshersh</name></author></entry><entry><source><link href="https://soap.coffee/~lthms/posts/index.xml" rel="alternate"/><id>https://soap.coffee/~lthms/posts/index.xml</id><title type="text">Thomas Letan’s Blog</title></source><link href="https://soap.coffee/~lthms/posts/December2024.html" rel="alternate"/><content type="html">&lt;pre&gt;&lt;code&gt;    &amp;lt;h1&amp;gt;What Happened in 2024?&amp;lt;/h1&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/meta.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;meta&amp;lt;/a&amp;gt; &amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/opinions.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;opinions&amp;lt;/a&amp;gt; &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are done with 2024, and now is a good time to reflect on what has happened
over the past 12 months. I was not planning to, but &lt;a href=&quot;https://www.paulox.net/2024/12/31/my-2024-in-review/&quot; marked=&quot;&quot;&gt;my feed convinced me to
give it a try&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. Plus, it is a good opportunity to revive my
“&lt;a href=&quot;https://soap.coffee/~lthms/posts/series/Retrospectives.html&quot; marked=&quot;&quot;&gt;Retrospective&lt;/a&gt;” series.&lt;/p&gt;
&lt;h2&gt;Free and Open Source Software&lt;/h2&gt;
&lt;p&gt;I’ve been a “prolific contributor” at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;, but less so with my
personal projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lthms/spatial-shell&quot; marked=&quot;&quot;&gt;Spatial Shell&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; remains my most “popular” project&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;We should reach 100 stars on GitHub in 2025 😅. &lt;/span&gt;
&lt;/span&gt;, but
except for a very minor 7th release in January, I have not touched it. It’s
basically a done project, and I very much enjoy using it on a daily basis. My
main regret is that, contrary to what is stated in its README, Spatial Shell
does not work &lt;em&gt;at all&lt;/em&gt; with i3.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lthms/ezjsonm-encoding&quot; marked=&quot;&quot;&gt;&lt;code class=&quot;hljs&quot;&gt;ezjsonm-encoding&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; was initially written for Spatial Shell, but I
turned it into its own OCaml package in 2024. It is a JSON-only encoding
library heavily borrowing on &lt;a href=&quot;https://ocaml.org/p/data-encoding/latest&quot; marked=&quot;&quot;&gt;Data-encoding API&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, but with a
more flexible default behavior for object parsing. I enjoyed writing the
documentation, inspired by a tweet from &lt;a href=&quot;https://bsky.app/profile/chshersh.com&quot; marked=&quot;&quot;&gt;Dmitrii Kovanikov&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#bsky&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Yes, cool kids moved to Bluesky in 2024, and Dmitrii is definitely a
cool kid. &lt;/span&gt;
&lt;/span&gt;.
That being said, I have never bothered to benchmark this package properly, so
if performances are important, it may not be a good fit for you.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lthms/jsonrpc2&quot; marked=&quot;&quot;&gt;&lt;code class=&quot;hljs&quot;&gt;jsonrpc2&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; is, as of January 1st, 2025, an experiment in
providing a general-purpose framework for servers and clients communicating
with the &lt;a href=&quot;https://www.jsonrpc.org/specification&quot; marked=&quot;&quot;&gt;JSON RPC 2.0&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; protocol. I quite like the API I’m proposing there,
and maybe I’ll try to polish and publish it in 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lthms/bepo-tsrn.nvim&quot; marked=&quot;&quot;&gt;&lt;code class=&quot;hljs&quot;&gt;bepo-tsrn.nvim&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; is another thing I have done for myself, but
published as if it was a public good. Now, instead of having to copy/paste
the same Neovim configuration file on every computer I use, I can just type
&lt;code class=&quot;hljs&quot;&gt;yay -S neovim-bepo-tsrn-git&lt;/code&gt; and be done with it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lthms/celtchar&quot; marked=&quot;&quot;&gt;celtchar&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; has seen its first commits since 2021, which is not nothing. As a
reminder, celtchar is a little tool I have written to generate ebooks and
static websites for the stories I write; and as I was doing the 2024 edition
of &lt;a href=&quot;https://nanowrimo.org&quot; marked=&quot;&quot;&gt;NaNoWriMo&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, I found myself in need to add a missing feature (supporting
books split in parts, not only chapters).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, I’ve been defaulting to OCaml for the past two years or so, and I am
starting to think it is time to widen my perspective again. I will probably
start with relearning Go&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;I don’t know why, but I have been mildly obsessed with this language
lately. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Blog posts&lt;/h2&gt;
&lt;p&gt;2024 was not a very productive year when it comes to this website. I have
published 5 articles, which is half the number of publications of 2023. As a
logical consequence, not a lot of folks have visited my website this year.
Funnily enough, the &lt;a href=&quot;http://localhost:8000/~lthms/posts/SpatialShell6.html&quot; marked=&quot;&quot;&gt;most read article&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; (by far) in 2024 was published in
2023&lt;label for=&quot;fn4&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;To be fair, it was published on December 30, 2023. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://soap.coffee/~lthms/img/2024.png&quot;&gt;&lt;figcaption&gt;&lt;p&gt;Yes, 2024 was a quiet year for this website&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;That being said, I am quite happy with the content published in 2024.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://soap.coffee/~lthms/posts/GitMaintenanceSshEncryptedKeys.html&quot; marked=&quot;&quot;&gt;Using &lt;code class=&quot;hljs&quot;&gt;git maintenance&lt;/code&gt; with Encrypted SSH Keys&lt;/a&gt; is the first article I
published in 2024. It is a direct consequence of my trip to Brussels in
February to attend to &lt;a href=&quot;https://archive.fosdem.org/2024/&quot; marked=&quot;&quot;&gt;FOSDEM&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. If you haven’t already, you should watch
&lt;a href=&quot;https://www.youtube.com/watch?v=aolI_Rz0ZqY&amp;amp;pp=ygUZc28geW91IHRoaW5rIHlvdSBrbm93IGdpdA==&quot; marked=&quot;&quot;&gt;Scott Chacon’s talk&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#youtube&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; about Git less known commands; it is the only
reason why I learned about &lt;code class=&quot;hljs&quot;&gt;git maintenance&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://soap.coffee/~lthms/posts/LUKSEncryptedVPS.html&quot; marked=&quot;&quot;&gt;Installing a LUKS-Encrypted Arch Linux on a Vultr VPS&lt;/a&gt; is mostly a
gift I have made to Future Me. It is a very specific how-to that I can use to
quickly set up a new server with disk encryption. Funny story, I was planning
to publish a follow-up about &lt;a href=&quot;https://github.com/lthms/nspawn&quot; marked=&quot;&quot;&gt;how I use systemd-nspawn to run my web services
in containers&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, but no matter how many times I tried, I’ve never
quite found a good way to tell this story. As I plan to educate myself on
Kubernetes in 2025, it is not clear I will ever publish it now.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://soap.coffee/~lthms/posts/BepoNvim.html&quot; marked=&quot;&quot;&gt;Introducing &lt;code class=&quot;hljs&quot;&gt;bepo-tsrn.nvim&lt;/code&gt;&lt;/a&gt; is probably the less useful article I
have published in 2024, considering I expect the userbase to &lt;code class=&quot;hljs&quot;&gt;bepo-tsrn.nvim&lt;/code&gt;
to stick to 1 until the very end&lt;label for=&quot;fn5&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;But who knows? Maybe one of you will prove me wrong! &lt;/span&gt;
&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://soap.coffee/~lthms/posts/VestigialStructures.html&quot; marked=&quot;&quot;&gt;On Vestigial Structures&lt;/a&gt; hardly qualifies as a blog post, and is
mostly a joke. It is also the only content on my website that was mostly
generated by ChatGPT, and it is flagged as such. I don’t like using AI to
&lt;em&gt;write&lt;/em&gt;, but I do appreciate having a reviewer always at hand.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://soap.coffee/~lthms/posts/DreamWebsite.html&quot; marked=&quot;&quot;&gt;Serving This Article from RAM for Fun and No Real Benefit&lt;/a&gt; was very
fun to write. This little experiment was stuck in my head for basically two
years, and it turned out basically exactly as I had pictured it. That being
said, I want to learn about CDNs now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, I still enjoy having my own little corner of the Internet, but if there is
one thing I’d like to improve in 2025, it is its reach. I’d like you folks to
run into my website, instead of having to promote it every time I write
something. 2025, the year of SEO?&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;2024 started with my decision to go back to a Software Engineering position,
after giving an honest try at being an Engineering Manager in late 2023.&lt;/p&gt;
&lt;p&gt;I want to remember 2024 for two things.&lt;/p&gt;
&lt;p&gt;This year, more than ever, I have tried to appreciate my work beyond my
individual contributions. I am confident in my programming skills (although I
have so much to learn), but being an accomplished engineer is much more than
contributing code. Making sure every engineer in the team can work to the best
of their current ability, fostering a work environment favoring growth and
initiative, estimating as precisely as possible the amount of time needed to
deliver the next important thing, collaborating efficiently with non-technical
teams... I am becoming increasingly interested in these areas.&lt;/p&gt;
&lt;p&gt;Besides, this year was all about delivering and deploying in production. It’s
been a &lt;a href=&quot;https://medium.com/etherlink/post-mortem-etherlink-mainnet-beta-public-endpoint-denial-of-service-cfcaf1a7bb77&quot; marked=&quot;&quot;&gt;wild ride&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, and I loved it even if it was very demanding. After having
mostly contributed to R&amp;amp;D projects, the focus on UX, backward compatibility,
etc. was very refreshing. I learned so much through the year, and had many
opportunities to make significant impacts.&lt;/p&gt;
&lt;p&gt;In 2025, I want to keep learning about software engineering, and maybe start
sharing my thoughts on the subject on my website.&lt;/p&gt;
&lt;h2&gt;Talks&lt;/h2&gt;
&lt;p&gt;I gave only one talk in 2024, at a conference called &lt;a href=&quot;https://ethcc.io/&quot; marked=&quot;&quot;&gt;EthCC&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. It was actually a
follow-up to the talk I gave the year before. You can watch me deliver the talk
&lt;a href=&quot;https://ethcc.io/archives/being-a-stage-2-rollup-from-day-1-etherlinks-journey&quot; marked=&quot;&quot;&gt;here&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, but if you are more into written content, I have also published
a &lt;a href=&quot;https://soap.coffee/~lthms/posts/BeingStage2Rollup.html&quot; marked=&quot;&quot;&gt;transcript&lt;/a&gt; on this very website. I actually loved writing it down, and plan
to systematically publish similar content for every recorded talk I will give
in the future.&lt;/p&gt;
&lt;p&gt;I also had the opportunity to participate in a &lt;a href=&quot;https://x.com/etherlink/status/1852378990712930664&quot; marked=&quot;&quot;&gt;Twitter Space&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#twitter&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This year, my public speaking opportunities were all &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;-related. I
would like to change this in the future, because there are enough events out
there for me to start speaking about something other than work&lt;label for=&quot;fn6&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;It’s too late to apply to FOSDEM, but maybe I can find something to
say to an event later in the year! I think. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Sport&lt;/h2&gt;
&lt;p&gt;This year was a bit of a disappointment, sport-wise. I tried several times to
get back to running regularly, and failed miserably. I went to Lyon for a 10km
run without proper training, and skipped the half-marathon I signed up for. I
should update my &lt;a href=&quot;https://soap.coffee/~lthms/running.html&quot; marked=&quot;&quot;&gt;Running Log&lt;/a&gt; nonetheless. I started swimming
regularly during the Summer, only to pierce my earlobes in September 😅.&lt;/p&gt;
&lt;p&gt;Let’s hope I do better in 2025! I am planning to register for the &lt;a href=&quot;https://triathlondeauville.com/&quot; marked=&quot;&quot;&gt;Triathlon de
Deauville&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; with my sister. That promises to be fun! And I
want to commit to the &lt;a href=&quot;https://vredestein.20kmparis.com/&quot; marked=&quot;&quot;&gt;20km de Paris&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;On the bright side, I have started to use my bike again. I love riding around
Paris, especially at night.&lt;/p&gt;
&lt;h2&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Overall, I’ve devoted a large part of my time to &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt; in 2024.
Hopefully, I will find a better balance over the course of 2025, which should
give me more time to explore and experiment more things.&lt;/p&gt;
&lt;p&gt;Anyway, happy new year everyone! And happy Dry January!&lt;/p&gt;
</content><id>https://soap.coffee/~lthms/posts/December2024.html</id><title type="text">What Happened in 2024?</title><updated>2025-01-01T00:00:00-00:00</updated><author><name>Thomas Letan’s Blog</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.12.31.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.31.html#1&quot;&gt;Using Property-Based Testing to Test OCaml 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.31.html#2&quot;&gt;First release of elm_playground&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.31.html#3&quot;&gt;First release of flatunionfind&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.31.html#4&quot;&gt;Serving This Article from RAM with Dream for Fun and No Real Benefit&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.31.html#5&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.12.31.html</id><title type="text">OCaml Weekly News, 31 Dec 2024</title><updated>2024-12-31T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://debajyatidey.hashnode.dev/rss.xml" rel="alternate"/><id>https://debajyatidey.hashnode.dev/rss.xml</id><title type="text">Debajyati's Blog</title></source><link href="https://debajyatidey.hashnode.dev/build-a-cli-in-ocaml-with-the-cmdliner-library" rel="alternate"/><content type="html">&lt;p&gt;In this tutorial we are building a simple CLI named textsearch which is able to search a provided piece of string (probably a word) in a given text file.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Prior knowledge of components of a dune project is &lt;strong&gt;recommended&lt;/strong&gt; but &lt;strong&gt;not required&lt;/strong&gt;. I will guide you setting up the dune project.&lt;/p&gt;&lt;p&gt;Entire source code of this demo project is available at - &lt;a href=&quot;https://github.com/Debajyati/textsearch/releases/tag/v0.2.1&quot; target=&quot;_blank&quot;&gt;Release v0.2.1  Debajyati/textsearch&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This tutorial assumes you have dune preinstalled. If not, then install it via opam -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;opam install dune&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;Summary of the Content Prior to Reading the Article&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;In this tutorial, you'll learn to build a simple command-line interface (CLI) tool called &amp;quot;textsearch&amp;quot; using OCaml and the Cmdliner library. The tool searches for a specified term in a text file with options for case sensitivity and character order. You'll be guided through setting up a Dune project, configuring the necessary files, writing the search logic, and defining command-line arguments using Cmdliner. The tutorial covers the project structure, dependencies, and detailed explanations of the code components to help you understand and implement the CLI tool effectively.&lt;/p&gt;&lt;/blockquote&gt;&lt;h1&gt;What is Cmdliner?&lt;/h1&gt;&lt;p&gt;Cmdliner is a library that helps you build command line interface programs in OCaml.&lt;/p&gt;&lt;p&gt;It provides a simple and compositional mechanism to create commands and arguments in a declarative syntax.&lt;/p&gt;&lt;p&gt;So, lets get started!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://gifdb.com/images/high/let-the-games-begin-498-x-378-gif-y1jl2z80kk0g3ewi.gif&quot; alt=&quot;&quot; class=&quot;image--center mx-auto&quot;&gt;&lt;/p&gt;&lt;h1&gt;Create a Dune Project&lt;/h1&gt;&lt;p&gt;Lets start writing code. But before that we need to create a dune project.&lt;/p&gt;&lt;p&gt;Initialize the dune project by -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;dune init proj textsearch&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We will need the Cmdliner library installed in our system to build and run our project. So, install it via opam.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;opam install cmdliner&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Great! Now before you write the actual code, you need to setup the dune project by writing the configuration files properly.&lt;/p&gt;&lt;h1&gt;Setting Up the Dune Project&lt;/h1&gt;&lt;p&gt;At the end of this tutorial the file tree of our project will be like -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-markdown&quot;&gt;. bin    dune    main.ml lib    dune    search.ml test    dune    test&lt;span class=&quot;hljs-emphasis&quot;&gt;_textsearch.ml dune-project textsearch.opam&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here the textsearch.opam file is not your concern, dont touch it. This file is generated by dune, edit dune-project instead.&lt;br&gt;The dune-project file defines project-wide settings.&lt;/p&gt;&lt;p&gt;For now, you dont need to edit most of the parts of the file dune-project.&lt;br&gt;Currently the file must look like -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-apache&quot;&gt;(&lt;span class=&quot;hljs-attribute&quot;&gt;lang&lt;/span&gt; dune &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;.&lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;)(&lt;span class=&quot;hljs-attribute&quot;&gt;name&lt;/span&gt; textsearch)(&lt;span class=&quot;hljs-attribute&quot;&gt;generate_opam_files&lt;/span&gt; true)(&lt;span class=&quot;hljs-attribute&quot;&gt;source&lt;/span&gt; (&lt;span class=&quot;hljs-attribute&quot;&gt;github&lt;/span&gt; username/reponame))(&lt;span class=&quot;hljs-attribute&quot;&gt;authors&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Author Name &amp;lt;author@example.com&amp;gt;&amp;quot;&lt;/span&gt;)(&lt;span class=&quot;hljs-attribute&quot;&gt;maintainers&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Maintainer Name &amp;lt;maintainer@example.com&amp;gt;&amp;quot;&lt;/span&gt;)(&lt;span class=&quot;hljs-attribute&quot;&gt;license&lt;/span&gt; LICENSE)(&lt;span class=&quot;hljs-attribute&quot;&gt;documentation&lt;/span&gt; https://url/to/documentation)(&lt;span class=&quot;hljs-attribute&quot;&gt;package&lt;/span&gt; (&lt;span class=&quot;hljs-attribute&quot;&gt;name&lt;/span&gt; textsearch) (&lt;span class=&quot;hljs-attribute&quot;&gt;synopsis&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;A short synopsis&amp;quot;&lt;/span&gt;) (&lt;span class=&quot;hljs-attribute&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;A longer description&amp;quot;&lt;/span&gt;) (&lt;span class=&quot;hljs-attribute&quot;&gt;depends&lt;/span&gt; ocaml) (&lt;span class=&quot;hljs-attribute&quot;&gt;tags&lt;/span&gt;  (&amp;quot;&lt;span class=&quot;hljs-attribute&quot;&gt;add&lt;/span&gt; topics&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;to describe&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot; your project))); See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now change the package stanza to this -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-apache&quot;&gt;(&lt;span class=&quot;hljs-attribute&quot;&gt;package&lt;/span&gt; (&lt;span class=&quot;hljs-attribute&quot;&gt;name&lt;/span&gt; textsearch) (&lt;span class=&quot;hljs-attribute&quot;&gt;synopsis&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;A simple text search CLI tool&amp;quot;&lt;/span&gt;) (&lt;span class=&quot;hljs-attribute&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;CLI tool to search terms in files&amp;quot;&lt;/span&gt;) (&lt;span class=&quot;hljs-attribute&quot;&gt;depends&lt;/span&gt;  (&lt;span class=&quot;hljs-attribute&quot;&gt;ocaml&lt;/span&gt; (&amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;.&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;))  (&lt;span class=&quot;hljs-attribute&quot;&gt;cmdliner&lt;/span&gt; (&amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;.&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;.&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;))  (&lt;span class=&quot;hljs-attribute&quot;&gt;dune&lt;/span&gt; (&amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;.&lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;))) (&lt;span class=&quot;hljs-attribute&quot;&gt;tags&lt;/span&gt;  (&amp;quot;&lt;span class=&quot;hljs-attribute&quot;&gt;cmdline&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;text&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;search&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nice!&lt;br&gt;We have two important directories here -&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;lib/&lt;/code&gt; - Holds the source code for libraries within your project.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;bin/&lt;/code&gt; - Contains the source code for executables.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Forget the test folder for now as we are not going to write test cases for our project (At least not in this tutorial).&lt;/p&gt;&lt;p&gt;We also have a dune file in each of the folders. These files define how to build the specific executable within that directory.&lt;br&gt;Specifically, they are for -&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Specifying dependencies (both internal and external libraries).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Specifying rules for building the target.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Paste the following code in &lt;code&gt;bin/dune&lt;/code&gt; file -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-apache&quot;&gt;(&lt;span class=&quot;hljs-attribute&quot;&gt;executable&lt;/span&gt; (&lt;span class=&quot;hljs-attribute&quot;&gt;public_name&lt;/span&gt; textsearch) (&lt;span class=&quot;hljs-attribute&quot;&gt;name&lt;/span&gt; main) (&lt;span class=&quot;hljs-attribute&quot;&gt;libraries&lt;/span&gt; search cmdliner))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Paste the code below in the &lt;code&gt;lib/dune&lt;/code&gt; file -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-apache&quot;&gt;(&lt;span class=&quot;hljs-attribute&quot;&gt;library&lt;/span&gt;  (&lt;span class=&quot;hljs-attribute&quot;&gt;name&lt;/span&gt; search) (&lt;span class=&quot;hljs-attribute&quot;&gt;public_name&lt;/span&gt; textsearch))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All good! Finally, the configuration work is DONE! HUH!&lt;/p&gt;&lt;p&gt;We can finally write the actual code.&lt;/p&gt;&lt;h1&gt;Writing The Actual Code&lt;/h1&gt;&lt;h2&gt;Our Internal library&lt;/h2&gt;&lt;p&gt;We will need an internal library called &lt;code&gt;search.ml&lt;/code&gt; as we have specified in the dune files. Create that file in the &lt;code&gt;lib/&lt;/code&gt; directory.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;touch lib/search.ml&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, paste this code in the file -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; search_in_file ~case_sensitive ~consider_order term file =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; normalize s =    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; case_sensitive &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; s &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.lowercase_ascii s  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; term = normalize term &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; contains_term line =    &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; line = normalize line &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; consider_order &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; line_len = &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length line &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; term_len = &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length term &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;rec&lt;/span&gt; check_substring i =          &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; i + term_len &amp;gt; line_len &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;          &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.sub line i term_len = term &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;          &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; check_substring (i + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)        &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;        check_substring &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; _ -&amp;gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; term_chars = &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.to_seq term |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.of_seq &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; line_chars = &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.to_seq line |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.of_seq &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;      &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.for_all (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; c -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.mem c line_chars) term_chars  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; ic = open_in file &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;rec&lt;/span&gt; process_lines line_num acc =    &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; line = input_line ic &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;      &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; new_acc = &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; contains_term line &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; (line_num, line) :: acc &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; acc &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;      process_lines (line_num + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) new_acc    &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;End_of_file&lt;/span&gt; -&amp;gt;      close_in ic;      &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.rev acc  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  process_lines &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;;;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; print_matches matches =  &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.iter    (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; (line_num, line) -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Printf&lt;/span&gt;.printf &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;%d: %s\n&amp;quot;&lt;/span&gt; line_num line)    matches;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Weve two functions in this file.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;search_in_file -&lt;/strong&gt; This function takes four arguments:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;case_sensitive&lt;/code&gt;: A boolean value indicating whether the search should be case sensitive or not.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;consider_order&lt;/code&gt;: A boolean value indicating whether the order of characters in the term matters during the search.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;term&lt;/code&gt;: The string to search for in the file.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;file&lt;/code&gt;: The path to the file to search in.&lt;/p&gt;&lt;p&gt;  The function opens a file for reading. Then it recursively iterates over the lines of the file. On each iteration, the function reads a line, checks if the line contains the term. If the line contains the term, it adds the line number and the line itself as a tuple to an accumulator list. It recursively calls itself on the next line number and the updated accumulator list. When it reaches the end of the file (&lt;code&gt;End_of_file&lt;/code&gt; exception), it closes the file and reverses the accumulated list (to display lines in their original order).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;print_matches - This function&lt;/strong&gt; takes a list of matches as input where each match is a tuple containing the line number and the line itself. It iterates over the list of matches and prints each match in the format &amp;quot;line_num: line&amp;quot;.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I hope this explanation is clear and good to go ahead.&lt;/p&gt;&lt;h2&gt;Our Executable Program&lt;/h2&gt;&lt;p&gt;This is our &lt;code&gt;bin/main.ml&lt;/code&gt; file. Here we will import both of our internal library &lt;code&gt;Search&lt;/code&gt; and external library &lt;code&gt;Cmdliner&lt;/code&gt;, and then define the command and arguments.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;Cmdliner&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* Define arguments &lt;em&gt;)&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; search_term =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; doc = &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;The term to search for&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-type&quot;&gt;Arg&lt;/span&gt;.(required &amp;amp; pos &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; (some &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;) &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt; &amp;amp; info &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt; ~docv:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;TERM&amp;quot;&lt;/span&gt; ~doc)&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; file_name =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; doc = &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;The file to search in&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-type&quot;&gt;Arg&lt;/span&gt;.(required &amp;amp; pos &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; (some &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;) &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt; &amp;amp; info &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt; ~docv:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;FILE&amp;quot;&lt;/span&gt; ~doc)&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; case_sensitive =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; doc = &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Perform a case-sensitive search&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-type&quot;&gt;Arg&lt;/span&gt;.(&lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt; &amp;amp; flag &amp;amp; info [&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;case-sensitive&amp;quot;&lt;/span&gt;] ~doc)&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; consider_order =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; doc = &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Consider the order of characters in the search term&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-type&quot;&gt;Arg&lt;/span&gt;.(&lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt; &amp;amp; flag &amp;amp; info [&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;o&amp;quot;&lt;/span&gt;; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;consider-order&amp;quot;&lt;/span&gt;] ~doc)&lt;span class=&quot;hljs-comment&quot;&gt;(&lt;/em&gt; Core command logic &lt;em&gt;)&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; run case_sensitive consider_order term file =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; matches = search_in_file ~case_sensitive ~consider_order term file &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  print_matches matches;  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.length matches = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;    (&lt;span class=&quot;hljs-type&quot;&gt;Printf&lt;/span&gt;.eprintf &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;No matches found.\n&amp;quot;&lt;/span&gt;; exit &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)  &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;    exit &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(&lt;/em&gt; Create the command &lt;em&gt;)&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; cmd =  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; info =    &lt;span class=&quot;hljs-type&quot;&gt;Cmd&lt;/span&gt;.info &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;textsearch&amp;quot;&lt;/span&gt;      ~version:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;1.0.0&amp;quot;&lt;/span&gt;      ~doc:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Search for a term in a file with options for case sensitivity and character order&amp;quot;&lt;/span&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;hljs-type&quot;&gt;Cmd&lt;/span&gt;.v info    &lt;span class=&quot;hljs-type&quot;&gt;Term&lt;/span&gt;.(const run $ case_sensitive $ consider_order $ search_term $ file_name)&lt;span class=&quot;hljs-comment&quot;&gt;(&lt;/em&gt; Main entry point *)&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; = &lt;span class=&quot;hljs-type&quot;&gt;Stdlib&lt;/span&gt;.exit (&lt;span class=&quot;hljs-type&quot;&gt;Cmd&lt;/span&gt;.eval cmd)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lets break down the &lt;code&gt;bin/main.ml&lt;/code&gt; file to understand its components and the utilities of the &lt;code&gt;Cmdline&lt;/code&gt; library used here.&lt;/p&gt;&lt;p&gt;After imports, we first define all the arguments as variables. The arguments for our program are defined using &lt;strong&gt;Cmdliner.Arg&lt;/strong&gt; module.&lt;br&gt;In this program we defined 4 arguments.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;search_term (positional argument): - The term (word) to search for in the file.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;file_name (positional argument): - The file in which the term is to be searched.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;consider_order (flag): - Ensures that the order of characters in the search term is considered.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;case_sensitive (flag): - Enables case-sensitive search.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Explanation of the Cmdliner library utilities used here:&lt;/strong&gt; -&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Arg&lt;/strong&gt;: This module provides functions for defining command-line arguments, including their types, descriptions, and help messages.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.required&lt;/code&gt;: This function defines a required argument (makes the argument mandatory).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.pos&lt;/code&gt;: This function specifies the position of an argument in the command-line syntax. So, for example, &lt;code&gt;pos 0&lt;/code&gt; will make the argument the first positional argument.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.info&lt;/code&gt;: This function provides a docstring and other metadata for an argument.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.value&lt;/code&gt;: This function defines the value of an argument. The documentation says, - &lt;code&gt;(value a) is a term that evaluates to a's value&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.flag&lt;/code&gt;: This function defines a flag argument that may appear &lt;em&gt;at most&lt;/em&gt; once on the command line. The argument holds &lt;code&gt;true&lt;/code&gt; if the flag is present on the command line and &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.some&lt;/code&gt;: This function wraps the argument value in an &lt;code&gt;Option&lt;/code&gt; type.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Arg.(&amp;amp;)&lt;/code&gt;: This is an infix operator which performs a right associative composition operation, which simply means &lt;code&gt;Arg.(&amp;amp;)&lt;/code&gt; enables composition of the combinators of a command-line argument by connecting individual components (arguments type, default value, parsing logic and docs) in a declarative way.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;After imports we defined the core command logic. The whole command logic is encapsulated in the run function.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; function calls the &lt;code&gt;search_in_file&lt;/code&gt; function from the &lt;strong&gt;Search&lt;/strong&gt; library. Prints the matching lines using &lt;code&gt;print_matches&lt;/code&gt; function. Exits with code &lt;strong&gt;1&lt;/strong&gt; if no matches are found, else exit code &lt;strong&gt;0&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Next, we define the command in the &lt;code&gt;cmd&lt;/code&gt; variable.&lt;/p&gt;&lt;p&gt;And finally, there is the main entry point of our program where it starts its execution from. That is -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; = &lt;span class=&quot;hljs-type&quot;&gt;Stdlib&lt;/span&gt;.exit (&lt;span class=&quot;hljs-type&quot;&gt;Cmd&lt;/span&gt;.eval cmd)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, the &lt;code&gt;cmd&lt;/code&gt; command gets evaluated and then exits the program with the return code from run.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Explanation of all the Cmdliner library utilities used here:&lt;/strong&gt; -&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Cmd&lt;/strong&gt;: This module is the core of Cmdliner. It provides functions to define the commands, specify their options and arguments, and handle argument parsing, grouping and usage messages.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Cmd.info&lt;/code&gt;: This function creates a command description, including its name, version, and helptexts (a docstring explaining its purpose).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Cmd.v&lt;/code&gt;: This function creates a fully functional command combining the provided info and terms.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Cmd.eval&lt;/code&gt;: This function evaluates the given command, parses arguments, and executes the corresponding logic.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Term&lt;/strong&gt;: This module deals with creating and manipulating command-line terms (arguments and subcommands).&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Term.const&lt;/code&gt;: This function creates a term that evaluates to a constant value. In the given code, it's used to create terms for the &lt;code&gt;run&lt;/code&gt; function and its arguments.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Term.($)&lt;/code&gt; - An infix operator that combines terms to pass arguments to a function (eventually the command to run from terminal).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So, you finally understood the &lt;code&gt;Cmdline&lt;/code&gt; library.&lt;/p&gt;&lt;p&gt;Now its time to build and run our project to see if this works or not.&lt;/p&gt;&lt;h1&gt;Building &amp;amp; Running the Project&lt;/h1&gt;&lt;p&gt;Without waiting, let's build and run it!&lt;/p&gt;&lt;h2&gt;Building the Project&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Open your terminal and navigate to the root directory of your project (where &lt;code&gt;dune-project&lt;/code&gt; is located).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the following command to build the program:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-apache&quot;&gt;  &lt;span class=&quot;hljs-attribute&quot;&gt;dune&lt;/span&gt; build&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This command will use the dune files to compile the source code in &lt;code&gt;lib/search.ml&lt;/code&gt; and &lt;code&gt;bin/main.ml&lt;/code&gt; and create an executable named &lt;code&gt;textsearch&lt;/code&gt; in the &lt;code&gt;_build/install/default/bin/&lt;/code&gt; directory.&lt;/p&gt;&lt;p&gt;Now, you can run the executable by this command -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;dune &lt;span class=&quot;hljs-built_in&quot;&gt;exec&lt;/span&gt; -- textsearch -c -o &amp;lt;term&amp;gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;path/to/file/to/search/in&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, &lt;code&gt;&amp;lt;term&amp;gt;&lt;/code&gt; is the term you want to search in the file.&lt;/p&gt;&lt;p&gt;Replace &amp;quot;path/to/file/to/search/in&amp;quot; with the actual path of the file you want to search the term in.&lt;/p&gt;&lt;p&gt;&lt;code&gt;-c&lt;/code&gt; is the optional flag for case-sensitivity,&lt;/p&gt;&lt;p&gt;&lt;code&gt;-o&lt;/code&gt; is the optional flag for respecting order of characters.&lt;/p&gt;&lt;p&gt;For example, I use vim and have the vim config file &lt;code&gt;.vimrc&lt;/code&gt; located in the &lt;code&gt;$HOME&lt;/code&gt; directory of my system.&lt;/p&gt;&lt;p&gt;I want to search my name in the file if its there. I would run -&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;dune &lt;span class=&quot;hljs-built_in&quot;&gt;exec&lt;/span&gt; -- textsearch -c -o Debajyati ~/.vimrc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the program would print the matching lines in the console.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1735309890159/34559be3-7997-4bd3-a1af-48ba533085c0.png&quot; alt=&quot;output of the command I've run&quot; class=&quot;image--center mx-auto&quot;&gt;&lt;/p&gt;&lt;p&gt;Tadaaa! And it works pretty well and smooth! Neat!&lt;/p&gt;&lt;p&gt;Congratulations! 🎉 You now have a functional CLI tool built with OCaml and Cmdliner!&lt;/p&gt;&lt;h1&gt;Ending&lt;/h1&gt;&lt;p&gt;I would really appreciate your feedback on this tutorial. How would you rate it? How can I improve it?&lt;/p&gt;&lt;p&gt;If you found this project or tutorial helpful, please consider starring the repository below. It will encourage me to create more content like this.&lt;/p&gt;&lt;div class=&quot;embed-wrapper&quot;&gt;&lt;div class=&quot;embed-loading&quot;&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;a href=&quot;https://github.com/Debajyati/textsearch&quot; class=&quot;embed-card&quot;&gt;https://github.com/Debajyati/textsearch&lt;/a&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;If you found this POST helpful, if this blog added some value to your time and energy, please show some love by giving the article some likes and share it with your developer friends.&lt;/p&gt;&lt;p&gt;Feel free to connect with me at - &lt;a href=&quot;https://twitter.com/ddebajyati&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/debajyati-dey&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/Debajyati&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt; :)&lt;/p&gt;&lt;p&gt;Happy Coding 🧑🏽💻👩🏽💻! Have a nice day ahead! 🚀&lt;/p&gt;]]&amp;gt;&lt;/p&gt;
</content><id>https://debajyatidey.hashnode.dev/build-a-cli-in-ocaml-with-the-cmdliner-library</id><title type="text">Build A CLI in OCaml with the Cmdliner Library</title><updated>2024-12-27T15:31:54-00:00</updated><author><name>Debajyati's OCaml Blog</name></author></entry><entry><source><link href="https://soap.coffee/~lthms/posts/index.xml" rel="alternate"/><id>https://soap.coffee/~lthms/posts/index.xml</id><title type="text">Thomas Letan’s Blog</title></source><link href="https://soap.coffee/~lthms/posts/DreamWebsite.html" rel="alternate"/><content type="html">&lt;pre&gt;&lt;code&gt;    &amp;lt;h1&amp;gt;Serving This Article from RAM with Dream for Fun and No Real Benefit&amp;lt;/h1&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/ocaml.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;ocaml&amp;lt;/a&amp;gt; &amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/meta.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;meta&amp;lt;/a&amp;gt; &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In 2022, Xe Iaso published a &lt;a href=&quot;https://xeiaso.net/talks/how-my-website-works/&quot; marked=&quot;&quot;&gt;transcript of their talk on how their website was
working at the time&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. In a nutshell, their approach consisted of a
server preprocessing the website from its source at startup, then serving its
contents from memory. If you have not already, I can only encourage you to read
the article or watch the talk, as the story they tell is very interesting. For
me personally, it sparked a question: what if, instead of preprocessing the
website at startup, one decided to embed the already preprocessed website
within the program of the HTTP server tasked to serve it?&lt;/p&gt;
&lt;p&gt;Fast-forward today, and this question has finally been answered. The webpage
you are currently reading has been served to you by an ad hoc HTTP server built
with &lt;a href=&quot;https://aantron.github.io/dream/&quot; marked=&quot;&quot;&gt;Dream&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, whose binary is the only file I need to push to my server to
deploy the latest version of my website. I have actually deployed it, and it’s
been serving the contents of this website for more than a week now.&lt;/p&gt;
&lt;p&gt;What did I learn from this fun, little experiment? Basically, that this
approach changes nothing, as far as &lt;a href=&quot;https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?pli=1&quot; marked=&quot;&quot;&gt;Lighthouse&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; and my monitoring is
concerned. I couldn’t find any meaningful differences between a static website
served by Nginx, a piece of software with thousands and thousands of
engineering work behind it, and my little toy web server pieced together in an
hour or so. Still. It was fun, so why not write about it?&lt;/p&gt;
&lt;p&gt;This article is a kind of experience report. I’ll dive into what I have done to
turn my website into a single, static binary. Not only does it mean writing
some OCaml, which is always fun, but it also requires understanding a little
some key HTTP headers, as well as using Docker to build easily deployable
binaries. All in all, I hope it will be an interesting read for the curious
minds.&lt;/p&gt;
&lt;h2&gt;Embedding My Website in a Binary&lt;/h2&gt;
&lt;p&gt;Not much had changed much since &lt;a href=&quot;https://soap.coffee/~lthms/posts/August2022.html&quot; marked=&quot;&quot;&gt;I stopped using &lt;strong&gt;&lt;code class=&quot;hljs&quot;&gt;cleopatra&lt;/code&gt;&lt;/strong&gt; to generate
this website&lt;/a&gt;, and &lt;a href=&quot;https://soap.coffee/~lthms/posts/Thanks2023.html&quot; marked=&quot;&quot;&gt;the article I published in 2023 still
stands&lt;/a&gt;. In a nutshell, I work in the &lt;code class=&quot;hljs&quot;&gt;site/&lt;/code&gt; directory, and &lt;a href=&quot;https://soupault.app&quot; marked=&quot;&quot;&gt;soupault&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;
generates my website in the &lt;code class=&quot;hljs&quot;&gt;out/~lthms&lt;/code&gt; directory, thanks to a collection of
built-in and ad hoc plugins&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;For instance, Markdown footnotes are turned into side notes with
a soupault plugin. &lt;/span&gt;
&lt;/span&gt;. To deploy the website, I was relying
on &lt;code class=&quot;hljs&quot;&gt;rsync&lt;/code&gt; to sync the contents of the &lt;code class=&quot;hljs&quot;&gt;out/~lthms&lt;/code&gt; directory with the
directory statically served by a Nginx instance on my personal server.&lt;/p&gt;
&lt;p&gt;The first step of my little toy project is to actually embed the output of
soupault into an OCaml program.&lt;/p&gt;
&lt;p&gt;That’s where &lt;a href=&quot;https://github.com/mirage/ocaml-crunch&quot; marked=&quot;&quot;&gt;ocaml-crunch&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; comes in handy. It is a little program published by
&lt;a href=&quot;https://tarides.com/&quot; marked=&quot;&quot;&gt;Tarides&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, whose only job is to generate an OCaml module from a file system
directory. It is straightforward to use it from Dune.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-lisp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;; file: out/dune&lt;/span&gt;
(&lt;span class=&quot;hljs-name&quot;&gt;rule&lt;/span&gt;
 (&lt;span class=&quot;hljs-name&quot;&gt;target&lt;/span&gt; website_content.ml)
 (&lt;span class=&quot;hljs-name&quot;&gt;deps&lt;/span&gt; (&lt;span class=&quot;hljs-name&quot;&gt;source_tree&lt;/span&gt; ~lthms))
 (&lt;span class=&quot;hljs-name&quot;&gt;action&lt;/span&gt;
  (&lt;span class=&quot;hljs-name&quot;&gt;run&lt;/span&gt; ocaml-crunch -m plain -o %{target} -s ~lthms)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This snippet generates the &lt;code class=&quot;hljs&quot;&gt;website_content.ml&lt;/code&gt; module, which we can then
expose through a library with the &lt;code class=&quot;hljs&quot;&gt;library&lt;/code&gt; stanza.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-lisp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;; file: out/dune&lt;/span&gt;
(&lt;span class=&quot;hljs-name&quot;&gt;library&lt;/span&gt;
 (&lt;span class=&quot;hljs-name&quot;&gt;name&lt;/span&gt; website_content))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we are basically done. Excluding an &lt;code class=&quot;hljs&quot;&gt;Internal&lt;/code&gt; module, the signature of
&lt;code class=&quot;hljs&quot;&gt;Website_content&lt;/code&gt; is pretty straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; file_list : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; read : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; option
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; hash : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; option
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; size : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; option
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Serving the content with Dream&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://aantron.github.io/dream/&quot; marked=&quot;&quot;&gt;Dream&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; is a cool project, and provides a straightforward API that we can
leverage to turn our list of in-memory files into an HTTP server.&lt;/p&gt;
&lt;h3&gt;Naive Approach&lt;/h3&gt;
&lt;p&gt;Our goal now is to create a &lt;code class=&quot;hljs&quot;&gt;Dream.handler&lt;/code&gt; for each item in
&lt;code class=&quot;hljs language-ocaml&quot;&gt;file_list&lt;/code&gt;. Done naively (as was my first attempt), it gives you
something of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; make_handler ~content path =
  &lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.get path (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; req -&amp;gt;
    &lt;span class=&quot;hljs-type&quot;&gt;Lwt&lt;/span&gt;.return (&lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.response content)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which we can use to build the main route we will then pass to &lt;code class=&quot;hljs&quot;&gt;Dream.router&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; website_route =
  &lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.scope &lt;span class=&quot;hljs-string&quot;&gt;&quot;~lthms&quot;&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;
  @@ &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map
       (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; path -&amp;gt;
         &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content = &lt;span class=&quot;hljs-type&quot;&gt;Option&lt;/span&gt;.get (&lt;span class=&quot;hljs-type&quot;&gt;Website_content&lt;/span&gt;.read path) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
         make_handler ~content path)
       &lt;span class=&quot;hljs-type&quot;&gt;Website_content&lt;/span&gt;.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this approach, we build our handlers once, and then the lookup is done by
Dream’s router. It could be an interesting experiment to see if doing the
lookup ourselves is more performant (since Dream’s router is very generic,
while in our case we don’t really need to parse anything). I remember Xe
routing is basically going through a linked list, which seems strange at first,
but works very well in practice because they have ordered said list with the
most recent articles up front, and everybody comes to their website to read the
latest article anyway.&lt;/p&gt;
&lt;p&gt;It does not take an extensive QA process to figure out that this approach
is far from being enough. To name a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My website assumes &lt;code class=&quot;hljs&quot;&gt;http://path/index.html&lt;/code&gt; can be accessed with
&lt;code class=&quot;hljs&quot;&gt;http://path/&lt;/code&gt; or &lt;code class=&quot;hljs&quot;&gt;http://path&lt;/code&gt;. Our little snippet does not handle this.&lt;/li&gt;
&lt;li&gt;Browsers expect the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; headers to be correctly set. To give an
example, they won't load a CSS file if the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header is not set
to &lt;code class=&quot;hljs&quot;&gt;text/css&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Browsers work best for websites that take the time to provide caching
directives. Our little snippet does not care to do so.&lt;/li&gt;
&lt;li&gt;Even if my website is rather lightweight&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;20MBytes at the time of writing the first version of this article. &lt;/span&gt;
&lt;/span&gt;, compressing the response
of our HTTP server for clients that support it is always a good idea.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a gentle reminder of all the things Nginx can do for you with very
little configuration.&lt;/p&gt;
&lt;h3&gt;Handling &lt;code class=&quot;hljs&quot;&gt;index.html&lt;/code&gt; Synonyms&lt;/h3&gt;
&lt;p&gt;This one is rather simple. For files named &lt;code class=&quot;hljs&quot;&gt;index.html&lt;/code&gt;, we need 3 handers, not
just one. We can achieve this with an additional helper
&lt;code class=&quot;hljs&quot;&gt;make_handler_remove_suffix&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; make_handler_remove_suffix ~content path suffix
    =
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.ends_with ~suffix path &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; alt_path =
      &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.sub path &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; (&lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length path - &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length suffix)
    &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
    [ make_handler ~content alt_path ]
  &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating the &lt;code class=&quot;hljs&quot;&gt;website_route&lt;/code&gt; definition to use &lt;code class=&quot;hljs&quot;&gt;make_handler_remove_suffix&lt;/code&gt; is
quite easy as well.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt; let website_route =
   Dream.scope &quot;~lthms&quot; []
&lt;span class=&quot;hljs-deletion&quot;&gt;-  @@ List.map&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  @@ List.concat_map&lt;/span&gt;
        (fun path -&amp;gt;
          let content = Option.get (Website_content.read path) in
&lt;span class=&quot;hljs-deletion&quot;&gt;-         make_handler ~content path)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+         if path = &quot;index.html&quot; then&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           (* Special case to deal with &quot;index.html&quot; which needs to be&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+              recognized by the route &quot;/&quot; *)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           [&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;index.html&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           ]&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+         else&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           make_handler_remove_suffix ~content path&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             &quot;/index.html&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ make_handler_remove_suffix ~content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               path &quot;index.html&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ [ make_handler ~content path ])&lt;/span&gt;
        Website_content.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts/index.html&lt;/code&gt; returns the same pages
as &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts&lt;/code&gt; or &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts&lt;/code&gt;.
Check.&lt;/p&gt;
&lt;h3&gt;Supporting &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; is an HTTP header which is used by the receiver of the HTTP
message (whether it is a request or a response) to interpret its content.&lt;/p&gt;
&lt;p&gt;For instance, when building a RPC API, &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; is used by the server to
know how to parse the request body (&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;application/json&lt;/code&gt; or
&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;application/octet-stream&lt;/code&gt; being two popular choices, for
JSON or binary encoding, respectively).&lt;/p&gt;
&lt;p&gt;In our case, the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header is used by the HTTP server to
communicate the nature of the content to browsers. For my website, I can just
use the file extensions to infer the correct header to set. First, we list the
extensions that are actually used.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content_types =
  [
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.html&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/html&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.css&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/css&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.xml&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/xml&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.png&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;image/png&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.svg&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;image/svg+xml&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.gz&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;application/gzip&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.pub&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;);
  ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A header in Dream is encoded as a &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; * &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&lt;/code&gt; value, with the
first &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&lt;/code&gt; being the header name and the second being the header
value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content_type_header path =
  &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.filter_map
    (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; (ext, content_type) -&amp;gt;
      &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.ends_with ~suffix:ext path &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; (&lt;span class=&quot;hljs-string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;, content_type)
      &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;)
    content_types
  |&amp;gt; assert_f
       ~error_msg:&lt;span class=&quot;hljs-type&quot;&gt;Format&lt;/span&gt;.(sprintf &lt;span class=&quot;hljs-string&quot;&gt;&quot;Unsupported file type %s&quot;&lt;/span&gt; path)
       (( &amp;lt;&amp;gt; ) &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with &lt;code class=&quot;hljs language-ocaml&quot;&gt;assert_f&lt;/code&gt; being defined as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; assert_f ~error_msg f v =
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; f v &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; v &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; failwith error_msg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;assert_f&lt;/code&gt; is used to enforce that I don’t deploy a website which
contains route lacking a &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header. For instance, if I remove the
&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;/code&gt; entry of the &lt;code class=&quot;hljs language-ocaml&quot;&gt;content_type&lt;/code&gt; list, I get this error
when I try to execute the server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;Fatal error: exception Failure(&quot;Unsupported file type index.html&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because the headers are only computed once, when each &lt;code class=&quot;hljs&quot;&gt;route&lt;/code&gt; are
defined. This is a key principle of this project: compute once, serve many
time&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;I would love to get a compilation error instead (considering there
are no runtime values involved), but have not looked into this just yet. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt; let website_route =
   Dream.scope &quot;~lthms&quot; []
   @@ List.concat_map
        (fun path -&amp;gt;
          let content = Option.get (Website_content.read path) in
&lt;span class=&quot;hljs-addition&quot;&gt;+         let headers = content_type_header path in&lt;/span&gt;
          if path = &quot;index.html&quot; then
            (* Special case to deal with &quot;index.html&quot; which needs to be
               recognized by the route &quot;/&quot; *)
            [
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;index.html&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;index.html&quot;;&lt;/span&gt;
            ]
          else
&lt;span class=&quot;hljs-deletion&quot;&gt;-           make_handler_remove_suffix ~content path&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           make_handler_remove_suffix ~headers ~content path&lt;/span&gt;
              &quot;/index.html&quot;
&lt;span class=&quot;hljs-deletion&quot;&gt;-           @ make_handler_remove_suffix ~content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ make_handler_remove_suffix ~headers ~content&lt;/span&gt;
                path &quot;index.html&quot;
&lt;span class=&quot;hljs-deletion&quot;&gt;-           @ [ make_handler ~content path ])&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ [ make_handler ~headers ~content path ])&lt;/span&gt;
        Website_content.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(The changes in &lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt; and &lt;code class=&quot;hljs&quot;&gt;make_handler_remove_prefix&lt;/code&gt; are left as an
exercise to enthusiast readers)&lt;/p&gt;
&lt;h3&gt;Compressing if Requested&lt;/h3&gt;
&lt;p&gt;Nowadays, computations are cheap, while downloading data costs time (and
sometimes money). As a consequence, it is often a good idea for a server to
compress a large HTTP response, and browsers do ask them to do so, by setting
the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding&quot; marked=&quot;&quot;&gt;&lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; header of their requests.&lt;/p&gt;
&lt;p&gt;The value of the &lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt; header is a comma-separated list of
supported compression algorithms, optionally ordered with a priority value &lt;code class=&quot;hljs&quot;&gt;q&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, &lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;gzip;q=0.5, deflate;q=0.3, identity&lt;/code&gt;
tells you that the browser supports three encoding methods: &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt;, &lt;code class=&quot;hljs&quot;&gt;deflate&lt;/code&gt;
and &lt;code class=&quot;hljs&quot;&gt;identity&lt;/code&gt; (no compression), and the browser prefers &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt; over &lt;code class=&quot;hljs&quot;&gt;deflate&lt;/code&gt;.
Besides, the request can provide several &lt;code class=&quot;hljs&quot;&gt;Accept-Encoding&lt;/code&gt; headers instead of
just one, so we can have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;gzip;q=0.5
&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;deflate;q=0.3
&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;identity
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code class=&quot;hljs&quot;&gt;String&lt;/code&gt; module provides everything we need to check if a browser
supports gzip as an encoding method&lt;label for=&quot;fn4&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Spoiler: they do. I was even wondering at some point if I could
just &lt;em&gt;always&lt;/em&gt; return GZIP-compressed values, ignoring the
&lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt; header altogether. If you do that, though, &lt;code class=&quot;hljs&quot;&gt;curl&lt;/code&gt;
becomes annoying to use (it does not uncompress the response automatically,
and instead complains about being about to write binary to the standard
output). &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* For [method(; q=val)?], returns [method], except if
   [q=0]. *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; to_directive str =
  &lt;span class=&quot;hljs-keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;';'&lt;/span&gt; str |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt;
  | [ x ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; x
  | [ x; y ] -&amp;gt; (
      &lt;span class=&quot;hljs-keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;'='&lt;/span&gt; y |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt;
      | [ &lt;span class=&quot;hljs-string&quot;&gt;&quot;q&quot;&lt;/span&gt;; &lt;span class=&quot;hljs-string&quot;&gt;&quot;0&quot;&lt;/span&gt; ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;
      | [ &lt;span class=&quot;hljs-string&quot;&gt;&quot;q&quot;&lt;/span&gt;; _ ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; x
      | _ -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;)
  | _ -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;
&lt;p&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* [contains ~value:v header] returns [true] if [v] is a
supported method listed in [header]. *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; contains ~&lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt; header =
&lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;','&lt;/span&gt; header
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.to_seq |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.filter_map to_directive
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.exists (( = ) &lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;We use &lt;code class=&quot;hljs language-ocaml&quot;&gt;contains&lt;/code&gt; to tell us if we can return a compressed response,
which leaves us with one final question: how to compress said response?&lt;/p&gt;
&lt;p&gt;The OCaml ecosystem seems to have picked &lt;a href=&quot;https://ocaml.org/p/camlzip/latest&quot; marked=&quot;&quot;&gt;camlzip&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; library when GZIP is
involved&lt;label for=&quot;fn5&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;You know it is a legitimate OCaml library when one of the top-level
modules &lt;a href=&quot;https://ocaml.org/p/camlzip/latest/doc/Zlib/&quot; marked=&quot;&quot;&gt;is not documented at all&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. &lt;/span&gt;
&lt;/span&gt;. What is surprising with this library is that it does not
support in-memory compression: the functions expect channels, not
&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;bytes&lt;/span&gt;&lt;/code&gt;. That is quite annoying, because we are specifically doing this
&lt;strong&gt;not&lt;/strong&gt; to use files.&lt;/p&gt;
&lt;p&gt;The Internet is helpful here, and quickly suggests using pipes. It works when
you remember –or figure out– that pipes are a blocking mechanism: one does not
just write a buffer of arbitrary size in a pipe, because after something like
4KBytes, writing becomes blocking until a read happens to free some space.
That’s not a big problem: we can read and write concurrently to the pipe using
threads, and OCaml 5 makes it quite easy to do so with the &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;Domain&lt;/span&gt;&lt;/code&gt;
module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; gzip content =
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; inc, ouc = &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.pipe &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; ouc = &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.open_out_chan ~level:&lt;span class=&quot;hljs-number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.(out_channel_of_descr ouc) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; _writer =
    &lt;span class=&quot;hljs-type&quot;&gt;Domain&lt;/span&gt;.spawn (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; -&amp;gt;
        &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.output_substring ouc content &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.(length content);
        &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.close_out ouc)
  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; res = &lt;span class=&quot;hljs-type&quot;&gt;In_channel&lt;/span&gt;.input_all &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.(in_channel_of_descr inc) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.close inc;
  res
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We know how to decide whether to compress or not, and how to compress. The next
step is to modify &lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt; accordingly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt;&lt;span class=&quot;hljs-deletion&quot;&gt;-let make_handler ~headers ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+let make_handler ~headers ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let gzip_headers = (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: headers in&lt;/span&gt;
   Dream.get path (fun req -&amp;gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-    Lwt.return (Dream.response content)))&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+        when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | _ -&amp;gt; Lwt.return @@ Dream.response ~headers content)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs&quot;&gt;gzip_content&lt;/code&gt; is computed only once (using our &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt; function), and passed to
&lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt;. This way, the only computation the handler needs to do is to
“parse” &lt;code class=&quot;hljs&quot;&gt;Accept-Encoding&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Caching&lt;/h3&gt;
&lt;p&gt;Compressing a page to reduce the number of bytes a browser needs to download is
fine. Letting the browser know it does not need to download anything because
it's previous version is still accurate is better.&lt;/p&gt;
&lt;p&gt;This is achieved through two complementary mechanisms: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag&quot; marked=&quot;&quot;&gt;entity tags&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;
(&lt;code class=&quot;hljs language-http&quot;&gt;ETag&lt;/code&gt;)&lt;label for=&quot;fn6&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;My first encounter with entity tags was around the time GDPR was a
hot topic, because you can use them as a cheap replacement for cookies to
&lt;a href=&quot;https://levelup.gitconnected.com/no-cookies-no-problem-using-etags-for-user-tracking-3e745544176b&quot; marked=&quot;&quot;&gt;track your users&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. I remained at the surface level at the time,
it was fun learning more about them through this little project. &lt;/span&gt;
&lt;/span&gt;, and &lt;a href=&quot;https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Cache-Control&quot; marked=&quot;&quot;&gt;cache policies&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; (&lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Entity tags are used to identify a resource, and are expected to change
every time the resource is updated. The general workflow goes like this: the
first time a browser requests &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/index.html&lt;/code&gt;, it
caches the result along with the value of the &lt;code class=&quot;hljs language-http&quot;&gt;ETag&lt;/code&gt; header. The next
time it needs the page, it adds the header &lt;code class=&quot;hljs language-http&quot;&gt;If-None-Match&lt;/code&gt;, with the
ETag as its value. For such requests, the server is expected to return an empty
response with HTTP code 304 (&lt;em&gt;Not Modified&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;I decided to use the sha256 hash algorithm to compute the entity tag of each
resource of my website. The &lt;a href=&quot;https://ocaml.org/p/sha/latest&quot; marked=&quot;&quot;&gt;sha&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; OCaml library looked like a good enough
candidate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* in `website_route` *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; etag = &lt;span class=&quot;hljs-type&quot;&gt;Sha256&lt;/span&gt;.(&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; content |&amp;gt; to_hex) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interestingly, one question I had to answer was whether the entity tag of a
page needed to be different whether it was compressed or not. Internet almost
unanimously answered yes. So be it. We just need to keep in mind ETag values
are expected to be surrounded by quotes, and we are good to go. It is just a
matter of suffixing the ETag with &lt;code class=&quot;hljs&quot;&gt;+gzip&lt;/code&gt; in the compressed case.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt;&lt;span class=&quot;hljs-deletion&quot;&gt;-let make_handler ~headers ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-  let gzip_headers = (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: headers in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+let make_handler ~headers ~etag ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let etag_gzip = Format.sprintf &quot;\&quot;%s+gzip\&quot;&quot; etag in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let etag = Format.sprintf &quot;\&quot;%s\&quot;&quot; etag in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let gzip_headers =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+    (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: (&quot;ETag&quot;, etag_gzip) :: headers in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let identity_headers = (&quot;ETag&quot;, etag) :: headers in&lt;/span&gt;
   Dream.get path (fun req -&amp;gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-        when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-          Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      | _ -&amp;gt; Lwt.return @@ Dream.response ~headers content)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      match Dream.headers req &quot;If-None-Match&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | [ previous_etag ] when previous_etag = etag || previous_etag = etag_gzip&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+        -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          Lwt.return&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          @@ Dream.response&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               ~headers:((&quot;ETag&quot;, previous_etag) :: headers)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               ~code:304 &quot;&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | _ -&amp;gt; (&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+            when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+              Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          | _ -&amp;gt; Lwt.return @@ Dream.response ~headers:identity_headers content))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entity tags are useful, but you still need the browser to make an HTTP request
every single time you visit the website. By setting a cache policy, we can
remove even remove the need for this request most of the time. The
&lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt; header is used to set a number of parameters, including
the &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; value (in seconds).&lt;/p&gt;
&lt;p&gt;In my Nginx configuration, I had set &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; to a year for images. I did
the same thing here. Besides, I decided to set &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; for other resources
to 5 minutes. This seems like a good compromise: since my website does not
change very often, it is very unlikely that you happen to visit it when I
publish new content. Setting a 5-minute cache policy should let my readers
download each resource only once, yet get the freshest version at their next
visit&lt;label for=&quot;fn7&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Dealing with the &lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt; header is basically the same
exercise as setting the correct &lt;code class=&quot;hljs language-http&quot;&gt;Content-Type&lt;/code&gt; header, and this
article is already long enough, which is why there is no diff or snippet in
this section. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;And with this, we are done. We get a standalone library to server our website
in a browser-friendly manner, which I can theoretically use to replace my
current Nginx-powered setup. Although… is it &lt;em&gt;that&lt;/em&gt; simple?&lt;/p&gt;
&lt;h2&gt;Building and Deploying the Website&lt;/h2&gt;
&lt;p&gt;Files are quite easy to share and deploy. As I mentioned earlier in this
article, you just need to &lt;code class=&quot;hljs language-bash&quot;&gt;rsync&lt;/code&gt; them and be done with it. &lt;em&gt;Binaries&lt;/em&gt;,
on the other hand… One cannot just assume a binary build on a machine X will
work on another machine Y. Some additional works need to be done.&lt;/p&gt;
&lt;p&gt;The most straightforward solution I know is to rely on static binaries. &lt;a href=&quot;https://soap.coffee/~lthms/posts/%5BOCamlStaticBinaries.html%5D&quot; marked=&quot;&quot;&gt;I have
already written about how to generate static binaries for OCaml
projects&lt;/a&gt;, so I had a pretty strong head start, but one drawback of the
approach I’ve described there (and that I have been using for &lt;a href=&quot;https://github.com/lthms/spatial-shell/releases&quot; marked=&quot;&quot;&gt;Spatial Shell’s
releases&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;label for=&quot;fn8&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Not that there were many of them lately. &lt;/span&gt;
&lt;/span&gt;) is that it is rather slow (I have a script creating
a new local switch each time) and requires static libraries to be installed
(which Arch Linux does not provide).&lt;/p&gt;
&lt;p&gt;And so, I figured, why not build the static binary in Docker? This allows me to
use Alpine to get a static version of my system dependencies, and can be quite
fast thanks to &lt;a href=&quot;https://docs.docker.com/build/cache/&quot; marked=&quot;&quot;&gt;Docker build cache&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. The Dockerfile is quite simple:
one stage for building the system and OCaml dependencies, and one stage for
building the static binary.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-dockerfile&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; alpine:&lt;span class=&quot;hljs-number&quot;&gt;3.21&lt;/span&gt; AS build_environment
&lt;p&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Use alpine /bin/ash and set shell options&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# See https://docs.docker.com/build/building/best-practices/#using-pipes&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; [&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;/bin/ash&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;-euo&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;pipefail&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;-c&amp;quot;&lt;/span&gt;]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; root
&lt;span class=&quot;hljs-keyword&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; /root&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; apk add autoconf automake bash build-base ca-certificates opam gcc \ &lt;/span&gt;
git rsync gmp-dev libev-dev openssl-libs-static pkgconf zlib-static &lt;br&gt;
openssl-dev zlib-dev
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; opam init --bare --&lt;span class=&quot;hljs-built_in&quot;&gt;yes&lt;/span&gt; --disable-sandboxing&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; makefile dune-project .&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; make _opam/.init OCAML=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;ocaml-option-static,ocaml-option-no-compression,ocaml.5.2.1&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;eval&lt;/span&gt; $(opam &lt;span class=&quot;hljs-built_in&quot;&gt;env&lt;/span&gt;) &amp;amp;&amp;amp; make server-deps&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; build_environment AS builder&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; server ./server&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; out ./out&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; dune .&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;eval&lt;/span&gt; $(opam &lt;span class=&quot;hljs-built_in&quot;&gt;env&lt;/span&gt;) &amp;amp;&amp;amp; dune build server/main.exe --profile=static&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; alpine:&lt;span class=&quot;hljs-number&quot;&gt;3.21&lt;/span&gt; AS soap.coffee&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; --from=builder /root/_build/default/server/main.exe /bin/soap.coffee&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Then, building my static binary becomes as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;docker build . -f ./build.Dockerfile \
  --target soap.coffee \
  -t soap.coffee:latest
docker create --name soap-coffee-build soap.coffee:latest
docker &lt;span class=&quot;hljs-built_in&quot;&gt;cp&lt;/span&gt; soap-coffee-build:/bin/soap.coffee .
docker &lt;span class=&quot;hljs-built_in&quot;&gt;rm&lt;/span&gt; -f soap-coffee-build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;docker &lt;span class=&quot;hljs-built_in&quot;&gt;cp&lt;/span&gt;&lt;/code&gt; does not work on an image, but on a container, so we need to
create one which can be destroyed shortly after.&lt;/p&gt;
&lt;p&gt;This little binary weights 38MBytes, which seems relatively reasonable to me,
considering my website weights 20MBytes. I guess it could be easy to reduce
this size by embedding the compressed version of my articles and images,
instead of the uncompressed one. But really, for my website, I’m not really
interested in investing the extra effort.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would not recommend anyone to use this in production for anything remotely
important, but from my perspective, it was both fun and insightful. I was able
to refresh my memories about HTTP “internal,” among other things.&amp;nbsp;Again, as
far as I can tell, deploying my website this way did not bring me any benefit,
performance-wise; even worse, I am pretty sure the Dream server will not behave
as well as Nginx when it comes to handling the load (since it is limited to one
core, instead of several with Nginx).&lt;/p&gt;
&lt;p&gt;That being said, I am way too invested now… which is why, yes, you are
reading a blog post served to you directly from memory 🎉.&lt;/p&gt;
</content><id>https://soap.coffee/~lthms/posts/DreamWebsite.html</id><title type="text">Serving This Article from RAM with Dream for Fun and No Real Benefit</title><updated>2024-12-25T00:00:00-00:00</updated><author><name>Thomas Letan’s Blog</name></author></entry><entry><source><link href="https://soap.coffee/~lthms/posts/index.xml" rel="alternate"/><id>https://soap.coffee/~lthms/posts/index.xml</id><title type="text">Thomas Letan’s Blog</title></source><link href="https://soap.coffee/~lthms/posts/DreamWebsite.html" rel="alternate"/><content type="html">&lt;pre&gt;&lt;code&gt;    &amp;lt;h1&amp;gt;Serving This Article from RAM for Fun and No Real Benefit&amp;lt;/h1&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/ocaml.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;ocaml&amp;lt;/a&amp;gt; &amp;lt;span class=&amp;quot;icon&amp;quot;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;/~lthms/img/icons.svg#tag&amp;quot;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;lt;a href=&amp;quot;https://soap.coffee/~lthms/tags/meta.html&amp;quot; marked=&amp;quot;&amp;quot; class=&amp;quot;tag&amp;quot;&amp;gt;meta&amp;lt;/a&amp;gt; &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In 2022, Xe Iaso published a &lt;a href=&quot;https://xeiaso.net/talks/how-my-website-works/&quot; marked=&quot;&quot;&gt;transcript of their talk on how their website was
working at the time&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. In a nutshell, their approach consisted of a
server preprocessing the website from its source at startup, then serving its
contents from memory. If you have not already, I can only encourage you to read
the article or watch the talk, as the story they tell is very interesting. For
me personally, it sparked a question: what if, instead of preprocessing the
website at startup, one decided to embed the already preprocessed website
within the program of the HTTP server tasked to serve it?&lt;/p&gt;
&lt;p&gt;Fast-forward today, and this question has finally been answered. The webpage
you are currently reading has been served to you by an ad hoc HTTP server built
with &lt;a href=&quot;https://aantron.github.io/dream/&quot; marked=&quot;&quot;&gt;Dream&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, whose binary is the only file I need to push to my server to
deploy the latest version of my website. I have actually deployed it, and it’s
been serving the contents of this website for more than a week now.&lt;/p&gt;
&lt;p&gt;What did I learn from this fun, little experiment? Basically, that this
approach changes nothing, as far as &lt;a href=&quot;https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?pli=1&quot; marked=&quot;&quot;&gt;Lighthouse&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; and my monitoring is
concerned. I couldn’t find any meaningful differences between a static website
served by Nginx, a piece of software with thousands and thousands of
engineering work behind it, and my little toy web server pieced together in an
hour or so. Still. It was fun, so why not write about it?&lt;/p&gt;
&lt;p&gt;This article is a kind of experience report. I’ll dive into what I have done to
turn my website into a single, static binary. Not only does it mean writing
some OCaml, which is always fun, but it also requires understanding a little
some key HTTP headers, as well as using Docker to build easily deployable
binaries. All in all, I hope it will be an interesting read for the curious
minds.&lt;/p&gt;
&lt;h2&gt;Embedding My Website in a Binary&lt;/h2&gt;
&lt;p&gt;Not much had changed much since &lt;a href=&quot;https://soap.coffee/~lthms/posts/August2022.html&quot; marked=&quot;&quot;&gt;I stopped using &lt;strong&gt;&lt;code class=&quot;hljs&quot;&gt;cleopatra&lt;/code&gt;&lt;/strong&gt; to generate
this website&lt;/a&gt;, and &lt;a href=&quot;https://soap.coffee/~lthms/posts/Thanks2023.html&quot; marked=&quot;&quot;&gt;the article I published in 2023 still
stands&lt;/a&gt;. In a nutshell, I work in the &lt;code class=&quot;hljs&quot;&gt;site/&lt;/code&gt; directory, and &lt;a href=&quot;https://soupault.app&quot; marked=&quot;&quot;&gt;soupault&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;
generates my website in the &lt;code class=&quot;hljs&quot;&gt;out/~lthms&lt;/code&gt; directory, thanks to a collection of
built-in and ad hoc plugins&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;For instance, Markdown footnotes are turned into side notes with
a soupault plugin. &lt;/span&gt;
&lt;/span&gt;. To deploy the website, I was relying
on &lt;code class=&quot;hljs&quot;&gt;rsync&lt;/code&gt; to sync the contents of the &lt;code class=&quot;hljs&quot;&gt;out/~lthms&lt;/code&gt; directory with the
directory statically served by a Nginx instance on my personal server.&lt;/p&gt;
&lt;p&gt;The first step of my little toy project is to actually embed the output of
soupault into an OCaml program.&lt;/p&gt;
&lt;p&gt;That’s where &lt;a href=&quot;https://github.com/mirage/ocaml-crunch&quot; marked=&quot;&quot;&gt;ocaml-crunch&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; comes in handy. It is a &lt;a href=&quot;https://mirage.io/&quot; marked=&quot;&quot;&gt;MirageOS&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; project, whose
only job is to generate an OCaml module from a file system directory. It is
straightforward to use it from Dune.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-lisp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;; file: out/dune&lt;/span&gt;
(&lt;span class=&quot;hljs-name&quot;&gt;rule&lt;/span&gt;
 (&lt;span class=&quot;hljs-name&quot;&gt;target&lt;/span&gt; website_content.ml)
 (&lt;span class=&quot;hljs-name&quot;&gt;deps&lt;/span&gt; (&lt;span class=&quot;hljs-name&quot;&gt;source_tree&lt;/span&gt; ~lthms))
 (&lt;span class=&quot;hljs-name&quot;&gt;action&lt;/span&gt;
  (&lt;span class=&quot;hljs-name&quot;&gt;run&lt;/span&gt; ocaml-crunch -m plain -o %{target} -s ~lthms)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This snippet generates the &lt;code class=&quot;hljs&quot;&gt;website_content.ml&lt;/code&gt; module, which we can then
expose through a library with the &lt;code class=&quot;hljs&quot;&gt;library&lt;/code&gt; stanza.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-lisp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;; file: out/dune&lt;/span&gt;
(&lt;span class=&quot;hljs-name&quot;&gt;library&lt;/span&gt;
 (&lt;span class=&quot;hljs-name&quot;&gt;name&lt;/span&gt; website_content))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we are basically done. Excluding an &lt;code class=&quot;hljs&quot;&gt;Internal&lt;/code&gt; module, the signature of
&lt;code class=&quot;hljs&quot;&gt;Website_content&lt;/code&gt; is pretty straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; file_list : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; read : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; option
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; hash : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; option
&lt;span class=&quot;hljs-keyword&quot;&gt;val&lt;/span&gt; size : &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt; option
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Serving the content with Dream&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://aantron.github.io/dream/&quot; marked=&quot;&quot;&gt;Dream&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; is a cool project, and provides a straightforward API that we can
leverage to turn our list of in-memory files into an HTTP server.&lt;/p&gt;
&lt;h3&gt;Naive Approach&lt;/h3&gt;
&lt;p&gt;Our goal now is to create a &lt;code class=&quot;hljs&quot;&gt;Dream.handler&lt;/code&gt; for each item in
&lt;code class=&quot;hljs language-ocaml&quot;&gt;file_list&lt;/code&gt;. Done naively (as was my first attempt), it gives you
something of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; make_handler ~content path =
  &lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.get path (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; req -&amp;gt;
    &lt;span class=&quot;hljs-type&quot;&gt;Lwt&lt;/span&gt;.return (&lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.response content)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which we can use to build the main route we will then pass to &lt;code class=&quot;hljs&quot;&gt;Dream.router&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; website_route =
  &lt;span class=&quot;hljs-type&quot;&gt;Dream&lt;/span&gt;.scope &lt;span class=&quot;hljs-string&quot;&gt;&quot;~lthms&quot;&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;
  @@ &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map
       (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; path -&amp;gt;
         &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content = &lt;span class=&quot;hljs-type&quot;&gt;Option&lt;/span&gt;.get (&lt;span class=&quot;hljs-type&quot;&gt;Website_content&lt;/span&gt;.read path) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
         make_handler ~content path)
       &lt;span class=&quot;hljs-type&quot;&gt;Website_content&lt;/span&gt;.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this approach, we build our handlers once, and then the lookup is done by
Dream’s router. It could be an interesting experiment to see if doing the
lookup ourselves is more performant (since Dream’s router is very generic,
while in our case we don’t really need to parse anything). I remember Xe
routing is basically going through a linked list, which seems strange at first,
but works very well in practice because they have ordered said list with the
most recent articles up front, and everybody comes to their website to read the
latest article anyway.&lt;/p&gt;
&lt;p&gt;It does not take an extensive QA process to figure out that this approach
is far from being enough. To name a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My website assumes &lt;code class=&quot;hljs&quot;&gt;http://path/index.html&lt;/code&gt; can be accessed with
&lt;code class=&quot;hljs&quot;&gt;http://path/&lt;/code&gt; or &lt;code class=&quot;hljs&quot;&gt;http://path&lt;/code&gt;. Our little snippet does not handle this.&lt;/li&gt;
&lt;li&gt;Browsers expect the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; headers to be correctly set. To give an
example, they won't load a CSS file if the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header is not set
to &lt;code class=&quot;hljs&quot;&gt;text/css&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Browsers work best for websites that take the time to provide caching
directives. Our little snippet does not care to do so.&lt;/li&gt;
&lt;li&gt;Even if my website is rather lightweight&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;20MBytes at the time of writing the first version of this article. &lt;/span&gt;
&lt;/span&gt;, compressing the response
of our HTTP server for clients that support it is always a good idea.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a gentle reminder of all the things Nginx can do for you with very
little configuration.&lt;/p&gt;
&lt;h3&gt;Handling &lt;code class=&quot;hljs&quot;&gt;index.html&lt;/code&gt; Synonyms&lt;/h3&gt;
&lt;p&gt;This one is rather simple. For files named &lt;code class=&quot;hljs&quot;&gt;index.html&lt;/code&gt;, we need 3 handers, not
just one. We can achieve this with an additional helper
&lt;code class=&quot;hljs&quot;&gt;make_handler_remove_suffix&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; make_handler_remove_suffix ~content path suffix
    =
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.ends_with ~suffix path &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; alt_path =
      &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.sub path &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; (&lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length path - &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.length suffix)
    &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
    [ make_handler ~content alt_path ]
  &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating the &lt;code class=&quot;hljs&quot;&gt;website_route&lt;/code&gt; definition to use &lt;code class=&quot;hljs&quot;&gt;make_handler_remove_suffix&lt;/code&gt; is
quite easy as well.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt; let website_route =
   Dream.scope &quot;~lthms&quot; []
&lt;span class=&quot;hljs-deletion&quot;&gt;-  @@ List.map&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  @@ List.concat_map&lt;/span&gt;
        (fun path -&amp;gt;
          let content = Option.get (Website_content.read path) in
&lt;span class=&quot;hljs-deletion&quot;&gt;-         make_handler ~content path)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+         if path = &quot;index.html&quot; then&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           (* Special case to deal with &quot;index.html&quot; which needs to be&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+              recognized by the route &quot;/&quot; *)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           [&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~content &quot;index.html&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           ]&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+         else&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           make_handler_remove_suffix ~content path&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             &quot;/index.html&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ make_handler_remove_suffix ~content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               path &quot;index.html&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ [ make_handler ~content path ])&lt;/span&gt;
        Website_content.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts/index.html&lt;/code&gt; returns the same pages
as &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts&lt;/code&gt; or &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/posts&lt;/code&gt;.
Check.&lt;/p&gt;
&lt;h3&gt;Supporting &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; is an HTTP header which is used by the receiver of the HTTP
message (whether it is a request or a response) to interpret its content.&lt;/p&gt;
&lt;p&gt;For instance, when building a RPC API, &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; is used by the server to
know how to parse the request body (&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;application/json&lt;/code&gt; or
&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;application/octet-stream&lt;/code&gt; being two popular choices, for
JSON or binary encoding, respectively).&lt;/p&gt;
&lt;p&gt;In our case, the &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header is used by the HTTP server to
communicate the nature of the content to browsers. For my website, I can just
use the file extensions to infer the correct header to set. First, we list the
extensions that are actually used.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content_types =
  [
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.html&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/html&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.css&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/css&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.xml&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/xml&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.png&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;image/png&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.svg&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;image/svg+xml&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.gz&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;application/gzip&quot;&lt;/span&gt;);
    (&lt;span class=&quot;hljs-string&quot;&gt;&quot;.pub&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;);
  ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A header in Dream is encoded as a &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; * &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&lt;/code&gt; value, with the
first &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&lt;/code&gt; being the header name and the second being the header
value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; content_type_header path =
  &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.filter_map
    (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; (ext, content_type) -&amp;gt;
      &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.ends_with ~suffix:ext path &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; (&lt;span class=&quot;hljs-string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;, content_type)
      &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;)
    content_types
  |&amp;gt; assert_f
       ~error_msg:&lt;span class=&quot;hljs-type&quot;&gt;Format&lt;/span&gt;.(sprintf &lt;span class=&quot;hljs-string&quot;&gt;&quot;Unsupported file type %s&quot;&lt;/span&gt; path)
       (( &amp;lt;&amp;gt; ) &lt;span class=&quot;hljs-literal&quot;&gt;[]&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with &lt;code class=&quot;hljs language-ocaml&quot;&gt;assert_f&lt;/code&gt; being defined as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; assert_f ~error_msg f v =
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; f v &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt; v &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; failwith error_msg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;assert_f&lt;/code&gt; is used to enforce that I don’t deploy a website which
contains route lacking a &lt;code class=&quot;hljs&quot;&gt;Content-Type&lt;/code&gt; header. For instance, if I remove the
&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;/code&gt; entry of the &lt;code class=&quot;hljs language-ocaml&quot;&gt;content_type&lt;/code&gt; list, I get this error
when I try to execute the server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;Fatal error: exception Failure(&quot;Unsupported file type index.html&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because the headers are only computed once, when each &lt;code class=&quot;hljs&quot;&gt;route&lt;/code&gt; are
defined. This is a key principle of this project: compute once, serve many
time&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;I would love to get a compilation error instead (considering there
are no runtime values involved), but have not looked into this just yet. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt; let website_route =
   Dream.scope &quot;~lthms&quot; []
   @@ List.concat_map
        (fun path -&amp;gt;
          let content = Option.get (Website_content.read path) in
&lt;span class=&quot;hljs-addition&quot;&gt;+         let headers = content_type_header path in&lt;/span&gt;
          if path = &quot;index.html&quot; then
            (* Special case to deal with &quot;index.html&quot; which needs to be
               recognized by the route &quot;/&quot; *)
            [
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-             make_handler ~content &quot;index.html&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;/&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;&quot;;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+             make_handler ~headers ~content &quot;index.html&quot;;&lt;/span&gt;
            ]
          else
&lt;span class=&quot;hljs-deletion&quot;&gt;-           make_handler_remove_suffix ~content path&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           make_handler_remove_suffix ~headers ~content path&lt;/span&gt;
              &quot;/index.html&quot;
&lt;span class=&quot;hljs-deletion&quot;&gt;-           @ make_handler_remove_suffix ~content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ make_handler_remove_suffix ~headers ~content&lt;/span&gt;
                path &quot;index.html&quot;
&lt;span class=&quot;hljs-deletion&quot;&gt;-           @ [ make_handler ~content path ])&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+           @ [ make_handler ~headers ~content path ])&lt;/span&gt;
        Website_content.file_list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(The changes in &lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt; and &lt;code class=&quot;hljs&quot;&gt;make_handler_remove_prefix&lt;/code&gt; are left as an
exercise to enthusiast readers)&lt;/p&gt;
&lt;h3&gt;Compressing if Requested&lt;/h3&gt;
&lt;p&gt;Nowadays, computations are cheap, while downloading data costs time (and
sometimes money). As a consequence, it is often a good idea for a server to
compress a large HTTP response, and browsers do ask them to do so, by setting
the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding&quot; marked=&quot;&quot;&gt;&lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; header of their requests.&lt;/p&gt;
&lt;p&gt;The value of the &lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt; header is a comma-separated list of
supported compression algorithms, optionally ordered with a priority value &lt;code class=&quot;hljs&quot;&gt;q&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, &lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;gzip;q=0.5, deflate;q=0.3, identity&lt;/code&gt;
tells you that the browser supports three encoding methods: &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt;, &lt;code class=&quot;hljs&quot;&gt;deflate&lt;/code&gt;
and &lt;code class=&quot;hljs&quot;&gt;identity&lt;/code&gt; (no compression), and the browser prefers &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt; over &lt;code class=&quot;hljs&quot;&gt;deflate&lt;/code&gt;.
Besides, the request can provide several &lt;code class=&quot;hljs&quot;&gt;Accept-Encoding&lt;/code&gt; headers instead of
just one, so we can have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-http&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;gzip;q=0.5
&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;deflate;q=0.3
&lt;span class=&quot;hljs-attribute&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;: &lt;/span&gt;identity
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code class=&quot;hljs&quot;&gt;String&lt;/code&gt; module provides everything we need to check if a browser
supports gzip as an encoding method&lt;label for=&quot;fn4&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Spoiler: they do. I was even wondering at some point if I could
just &lt;em&gt;always&lt;/em&gt; return GZIP-compressed values, ignoring the
&lt;code class=&quot;hljs language-http&quot;&gt;Accept-Encoding&lt;/code&gt; header altogether. If you do that, though, &lt;code class=&quot;hljs&quot;&gt;curl&lt;/code&gt;
becomes annoying to use (it does not uncompress the response automatically,
and instead complains about being about to write binary to the standard
output). &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* For [method(; q=val)?], returns [method], except if
   [q=0]. *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; to_directive str =
  &lt;span class=&quot;hljs-keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;';'&lt;/span&gt; str |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt;
  | [ x ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; x
  | [ x; y ] -&amp;gt; (
      &lt;span class=&quot;hljs-keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;'='&lt;/span&gt; y |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt;
      | [ &lt;span class=&quot;hljs-string&quot;&gt;&quot;q&quot;&lt;/span&gt;; &lt;span class=&quot;hljs-string&quot;&gt;&quot;0&quot;&lt;/span&gt; ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;
      | [ &lt;span class=&quot;hljs-string&quot;&gt;&quot;q&quot;&lt;/span&gt;; _ ] -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Some&lt;/span&gt; x
      | _ -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;)
  | _ -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;None&lt;/span&gt;
&lt;p&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* [contains ~value:v header] returns [true] if [v] is a
supported method listed in [header]. *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; contains ~&lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt; header =
&lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.split_on_char &lt;span class=&quot;hljs-string&quot;&gt;','&lt;/span&gt; header
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;List&lt;/span&gt;.to_seq |&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.map &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.trim
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.filter_map to_directive
|&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Seq&lt;/span&gt;.exists (( = ) &lt;span class=&quot;hljs-keyword&quot;&gt;value&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;We use &lt;code class=&quot;hljs language-ocaml&quot;&gt;contains&lt;/code&gt; to tell us if we can return a compressed response,
which leaves us with one final question: how to compress said response?&lt;/p&gt;
&lt;p&gt;The OCaml ecosystem seems to have picked &lt;a href=&quot;https://ocaml.org/p/camlzip/latest&quot; marked=&quot;&quot;&gt;camlzip&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; library when GZIP is
involved&lt;label for=&quot;fn5&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;You know it is a legitimate OCaml library when one of the top-level
modules &lt;a href=&quot;https://ocaml.org/p/camlzip/latest/doc/Zlib/&quot; marked=&quot;&quot;&gt;is not documented at all&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. &lt;/span&gt;
&lt;/span&gt;. What is surprising with this library is that it does not
support in-memory compression: the functions expect channels, not
&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;bytes&lt;/span&gt;&lt;/code&gt;. That is quite annoying, because we are specifically doing this
&lt;strong&gt;not&lt;/strong&gt; to use files.&lt;/p&gt;
&lt;p&gt;The Internet is helpful here, and quickly suggests using pipes. It works when
you remember –or figure out– that pipes are a blocking mechanism: one does not
just write a buffer of arbitrary size in a pipe, because after something like
4KBytes, writing becomes blocking until a read happens to free some space.
That’s not a big problem: we can read and write concurrently to the pipe using
threads, and OCaml 5 makes it quite easy to do so with the &lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;Domain&lt;/span&gt;&lt;/code&gt;
module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; gzip content =
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; inc, ouc = &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.pipe &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; ouc = &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.open_out_chan ~level:&lt;span class=&quot;hljs-number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.(out_channel_of_descr ouc) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; _writer =
    &lt;span class=&quot;hljs-type&quot;&gt;Domain&lt;/span&gt;.spawn (&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;()&lt;/span&gt; -&amp;gt;
        &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.output_substring ouc content &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt;.(length content);
        &lt;span class=&quot;hljs-type&quot;&gt;Gzip&lt;/span&gt;.close_out ouc)
  &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; res = &lt;span class=&quot;hljs-type&quot;&gt;In_channel&lt;/span&gt;.input_all &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.(in_channel_of_descr inc) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;hljs-type&quot;&gt;Unix&lt;/span&gt;.close inc;
  res
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;markdown-alert markdown-alert-caution&quot;&gt;&lt;p class=&quot;markdown-alert-title&quot;&gt;&lt;svg class=&quot;octicon octicon-stop mr-2&quot; viewbox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Caution&lt;/p&gt;&lt;p&gt;As rightfully &lt;a href=&quot;https://discuss.ocaml.org/t/serving-this-article-from-ram-with-dream-for-fun-and-no-real-benefit/15856/6?u=lthms&quot; marked=&quot;&quot;&gt;pointed out&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; by &lt;a href=&quot;https://erratique.ch/contact.en&quot; marked=&quot;&quot;&gt;Daniel Bünzli&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, the &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt;
function presented in this article is full of shortcomings. To quote the
message, &lt;em&gt;the function can leak fds in case of errors and domains are not
meant to be used that way (it’s rather spawn one long running domain per CPU
you have). It’s not necessarily more complicated to correct it to use
Fun.protect invocations to make sure all your fds get closed even if the
function blows up and use Thread.create so that the netizens cut and paste
correct code.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In my opinion, this implementation is “good enough” for my use case, which is
compressing arbitrary strings before the HTTP server is even started. If it
were to be called in the handlers themselves, then definitely, it would not
be suitable.&lt;/p&gt;
&lt;p&gt;Keep that in mind if you want to borrow this code.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We know how to decide whether to compress or not, and how to compress. The next
step is to modify &lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt; accordingly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt;&lt;span class=&quot;hljs-deletion&quot;&gt;-let make_handler ~headers ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+let make_handler ~headers ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let gzip_headers = (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: headers in&lt;/span&gt;
   Dream.get path (fun req -&amp;gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-    Lwt.return (Dream.response content)))&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+        when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | _ -&amp;gt; Lwt.return @@ Dream.response ~headers content)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs&quot;&gt;gzip_content&lt;/code&gt; is computed only once (using our &lt;code class=&quot;hljs&quot;&gt;gzip&lt;/code&gt; function), and passed to
&lt;code class=&quot;hljs&quot;&gt;make_handler&lt;/code&gt;. This way, the only computation the handler needs to do is to
“parse” &lt;code class=&quot;hljs&quot;&gt;Accept-Encoding&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Caching&lt;/h3&gt;
&lt;p&gt;Compressing a page to reduce the number of bytes a browser needs to download is
fine. Letting the browser know it does not need to download anything because
it's previous version is still accurate is better.&lt;/p&gt;
&lt;p&gt;This is achieved through two complementary mechanisms: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag&quot; marked=&quot;&quot;&gt;entity tags&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;
(&lt;code class=&quot;hljs language-http&quot;&gt;ETag&lt;/code&gt;)&lt;label for=&quot;fn6&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;My first encounter with entity tags was around the time GDPR was a
hot topic, because you can use them as a cheap replacement for cookies to
&lt;a href=&quot;https://levelup.gitconnected.com/no-cookies-no-problem-using-etags-for-user-tracking-3e745544176b&quot; marked=&quot;&quot;&gt;track your users&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. I remained at the surface level at the time,
it was fun learning more about them through this little project. &lt;/span&gt;
&lt;/span&gt;, and &lt;a href=&quot;https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Cache-Control&quot; marked=&quot;&quot;&gt;cache policies&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; (&lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Entity tags are used to identify a resource, and are expected to change
every time the resource is updated. The general workflow goes like this: the
first time a browser requests &lt;code class=&quot;hljs&quot;&gt;https://soap.coffee/~lthms/index.html&lt;/code&gt;, it
caches the result along with the value of the &lt;code class=&quot;hljs language-http&quot;&gt;ETag&lt;/code&gt; header. The next
time it needs the page, it adds the header &lt;code class=&quot;hljs language-http&quot;&gt;If-None-Match&lt;/code&gt;, with the
ETag as its value. For such requests, the server is expected to return an empty
response with HTTP code 304 (&lt;em&gt;Not Modified&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;I decided to use the sha256 hash algorithm to compute the entity tag of each
resource of my website. The &lt;a href=&quot;https://ocaml.org/p/sha/latest&quot; marked=&quot;&quot;&gt;sha&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; OCaml library looked like a good enough
candidate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ocaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;(* in `website_route` *)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;let&lt;/span&gt; etag = &lt;span class=&quot;hljs-type&quot;&gt;Sha256&lt;/span&gt;.(&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; content |&amp;gt; to_hex) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interestingly, one question I had to answer was whether the entity tag of a
page needed to be different whether it was compressed or not. Internet almost
unanimously answered yes. So be it. We just need to keep in mind ETag values
are expected to be surrounded by quotes, and we are good to go. It is just a
matter of suffixing the ETag with &lt;code class=&quot;hljs&quot;&gt;+gzip&lt;/code&gt; in the compressed case.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-patch&quot;&gt;&lt;span class=&quot;hljs-deletion&quot;&gt;-let make_handler ~headers ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-  let gzip_headers = (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: headers in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+let make_handler ~headers ~etag ~gzip_content ~content path =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let etag_gzip = Format.sprintf &quot;\&quot;%s+gzip\&quot;&quot; etag in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let etag = Format.sprintf &quot;\&quot;%s\&quot;&quot; etag in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let gzip_headers =&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+    (&quot;Content-Encoding&quot;, &quot;gzip&quot;) :: (&quot;ETag&quot;, etag_gzip) :: headers in&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+  let identity_headers = (&quot;ETag&quot;, etag) :: headers in&lt;/span&gt;
   Dream.get path (fun req -&amp;gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-        when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-          Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-deletion&quot;&gt;-      | _ -&amp;gt; Lwt.return @@ Dream.response ~headers content)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      match Dream.headers req &quot;If-None-Match&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | [ previous_etag ] when previous_etag = etag || previous_etag = etag_gzip&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+        -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          Lwt.return&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          @@ Dream.response&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               ~headers:((&quot;ETag&quot;, previous_etag) :: headers)&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+               ~code:304 &quot;&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+      | _ -&amp;gt; (&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          match Dream.headers req &quot;Accept-Encoding&quot; with&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          | accepted_encodings&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+            when List.exists (contains ~value:&quot;gzip&quot;) accepted_encodings -&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+              Lwt.return @@ Dream.response ~headers:gzip_headers gzip_content&lt;/span&gt;
&lt;span class=&quot;hljs-addition&quot;&gt;+          | _ -&amp;gt; Lwt.return @@ Dream.response ~headers:identity_headers content))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entity tags are useful, but you still need the browser to make an HTTP request
every single time you visit the website. By setting a cache policy, we can
remove even remove the need for this request most of the time. The
&lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt; header is used to set a number of parameters, including
the &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; value (in seconds).&lt;/p&gt;
&lt;p&gt;In my Nginx configuration, I had set &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; to a year for images. I did
the same thing here. Besides, I decided to set &lt;code class=&quot;hljs&quot;&gt;max-age&lt;/code&gt; for other resources
to 5 minutes. This seems like a good compromise: since my website does not
change very often, it is very unlikely that you happen to visit it when I
publish new content. Setting a 5-minute cache policy should let my readers
download each resource only once, yet get the freshest version at their next
visit&lt;label for=&quot;fn7&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Dealing with the &lt;code class=&quot;hljs language-http&quot;&gt;Cache-Control&lt;/code&gt; header is basically the same
exercise as setting the correct &lt;code class=&quot;hljs language-http&quot;&gt;Content-Type&lt;/code&gt; header, and this
article is already long enough, which is why there is no diff or snippet in
this section. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;And with this, we are done. We get a standalone library to server our website
in a browser-friendly manner, which I can theoretically use to replace my
current Nginx-powered setup. Although… is it &lt;em&gt;that&lt;/em&gt; simple?&lt;/p&gt;
&lt;h2&gt;Building and Deploying the Website&lt;/h2&gt;
&lt;p&gt;Files are quite easy to share and deploy. As I mentioned earlier in this
article, you just need to &lt;code class=&quot;hljs language-bash&quot;&gt;rsync&lt;/code&gt; them and be done with it. &lt;em&gt;Binaries&lt;/em&gt;,
on the other hand… One cannot just assume a binary build on a machine X will
work on another machine Y. Some additional works need to be done.&lt;/p&gt;
&lt;p&gt;The most straightforward solution I know is to rely on static binaries. &lt;a href=&quot;https://soap.coffee/~lthms/posts/%5BOCamlStaticBinaries.html%5D&quot; marked=&quot;&quot;&gt;I have
already written about how to generate static binaries for OCaml
projects&lt;/a&gt;, so I had a pretty strong head start, but one drawback of the
approach I’ve described there (and that I have been using for &lt;a href=&quot;https://github.com/lthms/spatial-shell/releases&quot; marked=&quot;&quot;&gt;Spatial Shell’s
releases&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;label for=&quot;fn8&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Not that there were many of them lately. &lt;/span&gt;
&lt;/span&gt;) is that it is rather slow (I have a script creating
a new local switch each time) and requires static libraries to be installed
(which Arch Linux does not provide).&lt;/p&gt;
&lt;p&gt;And so, I figured, why not build the static binary in Docker? This allows me to
use Alpine to get a static version of my system dependencies, and can be quite
fast thanks to &lt;a href=&quot;https://docs.docker.com/build/cache/&quot; marked=&quot;&quot;&gt;Docker build cache&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. The Dockerfile is quite simple:
one stage for building the system and OCaml dependencies, and one stage for
building the static binary.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-dockerfile&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; alpine:&lt;span class=&quot;hljs-number&quot;&gt;3.21&lt;/span&gt; AS build_environment
&lt;p&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Use alpine /bin/ash and set shell options&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# See https://docs.docker.com/build/building/best-practices/#using-pipes&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; [&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;/bin/ash&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;-euo&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;pipefail&amp;quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;-c&amp;quot;&lt;/span&gt;]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; root
&lt;span class=&quot;hljs-keyword&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; /root&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; apk add autoconf automake bash build-base ca-certificates opam gcc \ &lt;/span&gt;
git rsync gmp-dev libev-dev openssl-libs-static pkgconf zlib-static &lt;br&gt;
openssl-dev zlib-dev
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; opam init --bare --&lt;span class=&quot;hljs-built_in&quot;&gt;yes&lt;/span&gt; --disable-sandboxing&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; makefile dune-project .&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; make _opam/.init OCAML=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;ocaml-option-static,ocaml-option-no-compression,ocaml.5.2.1&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;eval&lt;/span&gt; $(opam &lt;span class=&quot;hljs-built_in&quot;&gt;env&lt;/span&gt;) &amp;amp;&amp;amp; make server-deps&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; build_environment AS builder&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; server ./server&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; out ./out&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; dune .&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;eval&lt;/span&gt; $(opam &lt;span class=&quot;hljs-built_in&quot;&gt;env&lt;/span&gt;) &amp;amp;&amp;amp; dune build server/main.exe --profile=static&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; alpine:&lt;span class=&quot;hljs-number&quot;&gt;3.21&lt;/span&gt; AS soap.coffee&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;language-bash&quot;&gt; --from=builder /root/_build/default/server/main.exe /bin/soap.coffee&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Then, building my static binary becomes as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;docker build . -f ./build.Dockerfile \
  --target soap.coffee \
  -t soap.coffee:latest
docker create --name soap-coffee-build soap.coffee:latest
docker &lt;span class=&quot;hljs-built_in&quot;&gt;cp&lt;/span&gt; soap-coffee-build:/bin/soap.coffee .
docker &lt;span class=&quot;hljs-built_in&quot;&gt;rm&lt;/span&gt; -f soap-coffee-build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;docker &lt;span class=&quot;hljs-built_in&quot;&gt;cp&lt;/span&gt;&lt;/code&gt; does not work on an image, but on a container, so we need to
create one which can be destroyed shortly after.&lt;/p&gt;
&lt;p&gt;This little binary weights 38MBytes, which seems relatively reasonable to me,
considering my website weights 20MBytes. I guess it could be easy to reduce
this size by embedding the compressed version of my articles and images,
instead of the uncompressed one. But really, for my website, I’m not really
interested in investing the extra effort.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would not recommend anyone to use this in production for anything remotely
important, but from my perspective, it was both fun and insightful. I was able
to refresh my memories about HTTP “internal,” among other things.&amp;nbsp;Again, as
far as I can tell, deploying my website this way did not bring me any benefit,
performance-wise; even worse, I am pretty sure the Dream server will not behave
as well as Nginx when it comes to handling the load (since it is limited to one
core, instead of several with Nginx).&lt;/p&gt;
&lt;p&gt;That being said, I am way too invested now… which is why, yes, you are
reading a blog post served to you directly from memory 🎉.&lt;/p&gt;
</content><id>https://soap.coffee/~lthms/posts/DreamWebsite.html</id><title type="text">Serving This Article from RAM for Fun and No Real Benefit</title><updated>2024-12-25T00:00:00-00:00</updated><author><name>Thomas Letan’s Blog</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.12.24.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#1&quot;&gt;dream-html and pure-html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#2&quot;&gt;Dune 3.17&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#3&quot;&gt;First release candidate of OCaml 5.3.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#4&quot;&gt;Pragmatic Category Theory | Part 3: Associativity&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#5&quot;&gt;ocaml-stk, xtmpl, stog, ocaml-css, ocaml-ldp, higlo and chamo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#6&quot;&gt;MirageOS on OCaml 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#7&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.24.html#8&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.12.24.html</id><title type="text">OCaml Weekly News, 24 Dec 2024</title><updated>2024-12-24T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-12-23-multicore-property-based-tests-for-ocaml-5-challenges-and-lessons-learned" rel="alternate"/><content type="html">&lt;p&gt;In &lt;a href=&quot;https://tarides.com/blog/2024-04-24-under-the-hood-developing-multicore-property-based-tests-for-ocaml-5/&quot;&gt;a previous post&lt;/a&gt;, we discussed how we have developed property-based tests (PBTs) to stress test the new runtime system in OCaml 5, and gave concrete examples of such tests. In this second part, we discuss some of the challenges and the lessons learned from that effort.&lt;/p&gt;
&lt;h2&gt;Testing APIs With Hidden or Uncontrolled State&lt;/h2&gt;
&lt;p&gt;In part 1, we saw how &lt;code&gt;STM&lt;/code&gt; and &lt;code&gt;Lin&lt;/code&gt; were useful to test stateful module interfaces, like &lt;code&gt;Float.Array&lt;/code&gt;. The OCaml standard library and runtime however also expose modules that are stateful, but for which the state is hidden or outside the full control of a black-box testing process. We are nevertheless interested in helping ensure the correctness of such modules.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;Ephemeron&lt;/code&gt;s depend on the state of OCaml's heap, meaning that a garbage collection (generally out of control of the test driver) may unsuspectingly trigger and cause changes in the test outcome. As a result, we ended up abandoning &lt;code&gt;Lin&lt;/code&gt; tests of &lt;code&gt;Ephemeron&lt;/code&gt;s, as they would run multiple observations of the same system under test - one parallel observation and several sequential ones - most likely with different results, because of different garbage collection schedulings.&lt;/p&gt;
&lt;p&gt;In addition, the &lt;code&gt;Lin&lt;/code&gt; tests could cause excessive shrinking searches, in trying to find a minimal example causing &lt;code&gt;Ephemeron&lt;/code&gt; differences between such runs. Instead we favoured replacing them with &lt;code&gt;STM&lt;/code&gt; tests, that perform only &lt;em&gt;one observation&lt;/em&gt; of the system under test. We furthermore experimented with &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/367&quot;&gt;inserting an explicit call to &lt;code&gt;Gc.full_major&lt;/code&gt; in between our run of the sequential and parallel tests to increase the chance of starting the latter on a relatively stable heap basis&lt;/a&gt;. We encountered &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/365&quot;&gt;similar problems with tests of the &lt;code&gt;Weak&lt;/code&gt; module&lt;/a&gt; and recently also in &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/469&quot;&gt;the &lt;code&gt;STM&lt;/code&gt; tests of the &lt;code&gt;Gc&lt;/code&gt; module&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Despite these challenges, the above tests successfully found several issues, e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml/pull/11749&quot;&gt;racing &lt;code&gt;Weak&lt;/code&gt; functions could in some cases produce strange values&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml/issues/11934&quot;&gt;certain combinations of &lt;code&gt;Weak&lt;/code&gt; &lt;code&gt;Hashset&lt;/code&gt; functions could cause the runtime to &lt;code&gt;abort&lt;/code&gt; or segfault&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml/issues/11503&quot;&gt;the &lt;code&gt;Ephemeron&lt;/code&gt; tests could trigger an assertion failure and abort&lt;/a&gt;, and&lt;/li&gt;
&lt;li&gt;out-of-date documentation for &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13424&quot;&gt;&lt;code&gt;Gc.quick_stat&lt;/code&gt;&lt;/a&gt; and  &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13440&quot;&gt;&lt;code&gt;Gc.set&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cygwin Challenges&lt;/h2&gt;
&lt;p&gt;As Cygwin support was being restored up in the 5.1 release, we wanted to test this platform as well to help ensure its correctness. However, we found that a test-suite run took an excessively long time, often not completing within a 6-hour timeout! We solved this initially by &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/305&quot;&gt;splitting a test-suite run into two separate workflows&lt;/a&gt; and later by rephrasing it as &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/313&quot;&gt;two separate CI jobs, belonging to the same workflow&lt;/a&gt;. Thanks to general improvements to the OCaml 5 runtime system, &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/420&quot;&gt;we have since been able to merge this split back into just one job&lt;/a&gt; like the remaining platforms, and eventually also &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/449&quot;&gt;reduce the timeout for this single Cygwin workflow to 4 hours, like the remaining platforms&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another Cygwin challenge early on was &lt;a href=&quot;https://cygwin.com/packages/summary/opam.html&quot;&gt;the relatively old &lt;code&gt;opam.2.0.7&lt;/code&gt; version it includes&lt;/a&gt;. This made it harder to test the various OCaml compiler versions on Cygwin (and later MinGW). As a workaround, we set up &lt;a href=&quot;https://github.com/shym/custom-opam-repository&quot;&gt;a custom &lt;code&gt;opam&lt;/code&gt; repository&lt;/a&gt; with appropriate &lt;code&gt;opam&lt;/code&gt; files for each compiler version.&lt;/p&gt;
&lt;h2&gt;Keeping Dependencies Minimal&lt;/h2&gt;
&lt;p&gt;Initially we happily used &lt;code&gt;ppxlib&lt;/code&gt; to generate boilerplate code for QCheck generators using  &lt;code&gt;ppx_deriving_qcheck&lt;/code&gt; and &lt;code&gt;show&lt;/code&gt; functions for the tested &lt;code&gt;cmd&lt;/code&gt;s. However, &lt;code&gt;ppxlib&lt;/code&gt; depends on the OCaml compiler's AST which means that it occasionally breaks on the compiler's &lt;code&gt;trunk&lt;/code&gt; development branch whenever its AST changes. As a consequence, testing &lt;code&gt;trunk&lt;/code&gt; would halt until &lt;code&gt;ppxlib&lt;/code&gt; was fixed again - an unfortunate situation when trying to help ensure its correctness! After &lt;a href=&quot;https://github.com/ocaml-ppx/ppxlib/pull/407&quot;&gt;helping keep a branch of &lt;code&gt;ppxlib&lt;/code&gt; continuously working with &lt;code&gt;trunk&lt;/code&gt;&lt;/a&gt;, at some point we instead decided to eliminate the test suite's &lt;code&gt;ppxlib&lt;/code&gt; dependency. We therefore wrote the corresponding definitions by hand and by utilising a dedicated printing library &lt;a href=&quot;https://ocaml-multicore.github.io/multicoretests/0.4/qcheck-multicoretests-util/Util/Pp/index.html&quot;&gt;&lt;code&gt;Pp&lt;/code&gt;&lt;/a&gt; in &lt;a href=&quot;https://ocaml-multicore.github.io/multicoretests/0.4/qcheck-multicoretests-util/&quot;&gt;&lt;code&gt;qcheck-multicoretests-util&lt;/code&gt;&lt;/a&gt;. Since then, testing has not been blocked by such breakages, and the dependencies are down to just the &lt;code&gt;qcheck-core&lt;/code&gt; package, and (transitively) &lt;code&gt;dune&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Testing an increasing number of platforms such as the MinGW and Cygwin ports over the past 2-3 years has been challenging, as much of that effort predates &lt;a href=&quot;https://opam.ocaml.org/blog/opam-2-2-0/&quot;&gt;the more recent Windows support brought by opam-2.2&lt;/a&gt;. As we additionally wanted to test OCaml 5's now restored MSVC port, also during its development, we ended up abandoning &lt;code&gt;opam&lt;/code&gt; and &lt;code&gt;setup-ocaml&lt;/code&gt; in favour of just building the tested compiler and our dependencies (&lt;code&gt;qcheck-core&lt;/code&gt; and &lt;code&gt;dune&lt;/code&gt;) from source in our CI workflows. As a result, we have gained a uniform CI workflow setup across platforms that also allows us to kick-off tests of feature branches, and thereby eliminate the need for the above-mentioned custom &lt;code&gt;opam&lt;/code&gt; repository.&lt;/p&gt;
&lt;h2&gt;Testing in the Presence of Misbehaviour&lt;/h2&gt;
&lt;p&gt;Overall, when having found, investigated, reported, and sometimes also fixed an error, we would like to continue testing despite the existence of such known issues. A test-suite rerun is however likely to trigger and report the same bug again, temporarily hindering the discovery and fixing of other issues. Something similar has been observed by others, e.g. in &lt;a href=&quot;https://www.erlang-factory.com/upload/presentations/582/CertifyingyourcarwithErlang.pdf&quot;&gt;Quviq's property-based testing of AUTOSAR for Volvo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To be able to continue testing we can, in the simple cases, (temporarily) adjust the tested property to accept the observed (mis)behaviour. This was the case, for example, for &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13424&quot;&gt;&lt;code&gt;Gc.quick_stat&lt;/code&gt; which would return non-zero entries for four record fields, where the documentation was out-of-date and specifying that zeros should be returned&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However we have also had to (temporarily) adjust the generator to avoid triggering a particular buggy &lt;code&gt;cmd&lt;/code&gt;. This was the case, for example, for &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13370&quot;&gt;&lt;code&gt;Gc.counters&lt;/code&gt; which had an independently found-and-fixed issue with improper C interfacing with the GC&lt;/a&gt;. &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/pull/469&quot;&gt;Our new &lt;code&gt;Gc&lt;/code&gt; test&lt;/a&gt; would nevertheless trigger it and cause a crash on 5.2.0, until we adjusted the generator to skip generating &lt;code&gt;Gc.counters&lt;/code&gt; calls on tests of versions up to 5.2.0. Since then &lt;a href=&quot;https://discuss.ocaml.org/t/ocaml-5-2-1-released/15634&quot;&gt;the fix has been included in the 5.2.1 bugfix release&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we have also (temporarily) disabled a test on a platform. This is the case, for example, for &lt;a href=&quot;https://github.com/ocaml/ocaml/issues/13046&quot;&gt;the parallel test of &lt;code&gt;Dynlink&lt;/code&gt; which is unsafe on Windows, due to an underlying flexdll issue&lt;/a&gt;. Whereas &lt;a href=&quot;https://github.com/ocaml/flexdll/pull/136&quot;&gt;the flexdll issue is now fixed in the flexdll repository&lt;/a&gt;, we are awaiting a new release before proceeding to re-enable the parallel test on Windows.&lt;/p&gt;
&lt;h2&gt;Crashes Take Down the Test Runner Too&lt;/h2&gt;
&lt;p&gt;Since the test-driver is running in OCaml too – and in the same process – when the SUT crashes, so does the entire QCheck test-driver process. In our experience this happens more often than not, as runtime issues tend to lead to memory corruption and typically a &lt;a href=&quot;https://en.wikipedia.org/wiki/Segmentation_fault&quot;&gt;segmentation fault&lt;/a&gt;. Running the test in a &lt;a href=&quot;https://en.wikipedia.org/wiki/Fork_(system_call)&quot;&gt;&lt;code&gt;fork&lt;/code&gt;ed&lt;/a&gt; child process may guard against a crash in the child taking down the parent process, with the caveat that OCaml 5 prevents &lt;code&gt;fork&lt;/code&gt;ing child processes after the first &lt;code&gt;Domain.spawn&lt;/code&gt;. In the spirit of functional programming, this option is available as a reusable combinator &lt;a href=&quot;https://ocaml-multicore.github.io/multicoretests/0.4/qcheck-multicoretests-util/Util/index.html#val-fork_prop_with_timeout&quot;&gt;&lt;code&gt;Util.fork_prop_with_timeout : int -&amp;gt; ('a -&amp;gt; bool) -&amp;gt; 'a -&amp;gt; bool&lt;/code&gt;&lt;/a&gt; in &lt;a href=&quot;https://ocaml.org/p/qcheck-multicoretests-util/latest&quot;&gt;&lt;code&gt;qcheck-multicoretests-util&lt;/code&gt;&lt;/a&gt;, thus allowing us to easily wrap the property of a crash-triggering &lt;code&gt;QCheck&lt;/code&gt; test.&lt;/p&gt;
&lt;p&gt;Despite not designed with any of the above in mind, we have arrived at a test layout where most tests are run as separate executables, which lets us identify crashes relatively easily and simultaneously lets a test-suite run continue despite encountering a crash underway.&lt;/p&gt;
&lt;h2&gt;Positive Testing, Negative Testing, and Stress Testing&lt;/h2&gt;
&lt;p&gt;While developing the &lt;code&gt;STM&lt;/code&gt; and &lt;code&gt;Lin&lt;/code&gt; libraries it became clear that we should guard against changes mistakenly affecting their error-finding behaviour. For this reason, we added negative tests of e.g. parallel modifications of an unprotected &lt;code&gt;ref&lt;/code&gt; cell that are expected to fail, and then &lt;a href=&quot;https://github.com/c-cube/qcheck/pull/244&quot;&gt;extended &lt;code&gt;QCheck&lt;/code&gt; with a &lt;code&gt;Test.make_neg&lt;/code&gt; function to construct a negative test&lt;/a&gt;. In the CI we then use this negative testing ability e.g. to test that &lt;code&gt;Hashtbl&lt;/code&gt; is unsafe to use in parallel as mentioned in part 1 and that the generator can find a counterexample illustrating it.&lt;/p&gt;
&lt;p&gt;Often, such a parallel negative test triggers quickly, e.g. on one of the first 10 test inputs generated – effectively stress-testing a module under parallel usage for only a very limited amount of time. We therefore added stress test properties, in the form of &lt;code&gt;stress_test&lt;/code&gt; for &lt;code&gt;Lin&lt;/code&gt; and &lt;code&gt;stress_test_par&lt;/code&gt; for &lt;code&gt;STM&lt;/code&gt;, both offering a weaker, more forgiving property that only fails on unexpected exceptions or outright crashes, thus strengthening our belief in the runtime - even under longer, continued parallel misusage.&lt;/p&gt;
&lt;p&gt;Effectively, we have arrived at PBT variants of classical test concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;positive tests&lt;/strong&gt; - expected to hold across many random test inputs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;negative tests&lt;/strong&gt; - expected not to hold and produce a counterexample&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stress tests&lt;/strong&gt; - expected not to misbehave by raising an exception or crashing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;False Alarms&lt;/h2&gt;
&lt;p&gt;As more and more CI target platforms have been added, we have also seen a variation in behaviour across them: Some of the above negative tests are not triggered as consistently across platforms and some of the tests take a long time or cause timeouts on some platforms.&lt;/p&gt;
&lt;p&gt;These add noise and still require us to check whether a failure was genuine or not. We have therefore focused on reducing the noise from false positives. To better understand this effort here is a plot of CI workflow outcomes for merged PRs (361-486) spanning a period of ~1.5 years from early June 2023 to early December 2024:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tarides.com/blog/images/false-alarms-plot-1360w~dX4NQFJuZHoHK0QiRAid_g.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/false-alarms-plot-170w~kfpgpUAdT9uV0ZkyJt555g.webp 170w, /blog/images/false-alarms-plot-340w~XMa35cJ4nLEu0s52QbwbBQ.webp 340w, /blog/images/false-alarms-plot-680w~eQrc8jI6bcA5Lhq0n8SNLw.webp 680w, /blog/images/false-alarms-plot-1360w~dX4NQFJuZHoHK0QiRAid_g.webp 1360w&quot; alt=&quot;A stacked histogram illustrating the outcome of CI workflow runs split into 'OK', 'ci', 'genuine', and 'other' categories&quot;&gt;&lt;/p&gt;
&lt;p&gt;First of all, this covers a period of testing a mix of OCaml versions, starting with workflows targeting 5.1 in June 2023, then 5.2, and now 5.3 and 5.4/trunk (the current compiler development version). Note that this only plots the outcome for Jan's PRs to &lt;code&gt;main&lt;/code&gt; for a fair comparison, as these would originally trigger twice as many workflow runs (push and PR). It furthermore only includes the last run for each PR, in case there were more because of PR revisions. Each CI run may in principle trigger several errors (in different categories even!). This is a rare incident however.&lt;/p&gt;
&lt;p&gt;Further notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On 370 a ppc64 workflow was added (workflow number increase)&lt;/li&gt;
&lt;li&gt;391 added framepointer workflows (workflow number increase)&lt;/li&gt;
&lt;li&gt;392-393 revealed &lt;a href=&quot;https://github.com/ocaml/ocaml/issues/12543&quot;&gt;a cmi-file lookup regression&lt;/a&gt; that made several workflows fail, hence the spike&lt;/li&gt;
&lt;li&gt;There were CI and network issue around 395-398&lt;/li&gt;
&lt;li&gt;On 396 a first FreeBSD workflow was added (workflow number increase)&lt;/li&gt;
&lt;li&gt;On 398 a second FreeBSD workflow and extra opam install workflows were added (workflow number increase)&lt;/li&gt;
&lt;li&gt;On 420 the 2-split Cygwin workflows were merged (workflow number decrease)&lt;/li&gt;
&lt;li&gt;On 429 (merged before 431) we eliminated duplicate CI runs for both push and the PR&lt;/li&gt;
&lt;li&gt;On 438 we retired the 5.1 workflows to only run weekly (not on every PR)&lt;/li&gt;
&lt;li&gt;On 449 the older MSVC PR (399) adding 2 additional MSVC workflows had just been merged&lt;/li&gt;
&lt;li&gt;On 453 the parallel &lt;code&gt;Dynlink&lt;/code&gt; tests were disabled on Windows (since a fix had been developed and offered on the FlexDLL repo)&lt;/li&gt;
&lt;li&gt;On 458 2 macOS ARM64 workflows were temporarily duplicated while moving them from &lt;code&gt;multicoretests-ci&lt;/code&gt; to GitHub actions&lt;/li&gt;
&lt;li&gt;On 471 &lt;code&gt;5.4.0+trunk&lt;/code&gt; workflows were added and removed three 5.1 multicoretests-ci workflows&lt;/li&gt;
&lt;li&gt;On 481 &lt;code&gt;multicoretests-ci&lt;/code&gt; stopped running the 4 remaining 5.2 workflows&lt;/li&gt;
&lt;li&gt;On 482 the ten 5.2 workflows were disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The smaller sub-bars are harder to distinguish with the dominant OK bars in the above figure.  Below we therefore zoom in and display the same plot, including only the different kinds of failures:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tarides.com/blog/images/false-alarms-plot-errors-only-1360w~wOpCubYg66VDTbHXaZMcZw.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/false-alarms-plot-errors-only-170w~5YZPBXgUoTrAIc0KQle6iw.webp 170w, /blog/images/false-alarms-plot-errors-only-340w~5HTdqWao21Ru8BWhPQSXJA.webp 340w, /blog/images/false-alarms-plot-errors-only-680w~ulyPw_CnsHR2OYtym2eM_A.webp 680w, /blog/images/false-alarms-plot-errors-only-1360w~wOpCubYg66VDTbHXaZMcZw.webp 1360w&quot; alt=&quot;A stacked histogram illustrating the outcome of CI workflow runs split, focusing only on the 'ci', 'genuine', and 'other' error categories&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are multiple competing efforts and aspects behind the amount of 'genuine' errors triggered in the above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, OCaml developers have fixed a number of defects and released 5.1.0, 5.2.0, 5.2.1, and 5.3.0~beta2 over this time period&lt;/li&gt;
&lt;li&gt;Second, as new compiler features are added and merged they may accidentally introduce new errors&lt;/li&gt;
&lt;li&gt;Third, we have added tests and 'sharpened the axe' of the existing ones&lt;/li&gt;
&lt;li&gt;Finally, a fix of a bug in 5.1 merged into 5.2 doesn't prevent the bug from continuing to show up on 5.1 CI runs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is clear from the plot that both genuine issues (categorised as 'genuine') and false alarms (categorised as 'other') have decreased over this period. The workflow alarms were initially dominated by false ones, then started being dominated by genuine (and 'ci') ones, and have now settled on a level with zero alarms being a common outcome. Such a noise-free test-suite signal is also central for OCaml compiler developers to utilise the test suite in their own development workflow. Here a test-suite red light should ideally signal a problem with a proposed runtime change, rather than (a) false alarms adding needless noise (&quot;oh, never mind that red light!&quot;) and (b) true alarms adding genuine noise (&quot;actually that's an existing unfixed issue, not your fault&quot;).&lt;/p&gt;
&lt;p&gt;To understand the failures triggering on specific versions, here's first a plot of the outcomes for the weekly CI runs of the upcoming 5.3 release:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://tarides.com/blog/images/workflow-ci-runs-53-1360w~587YI9GedCt9uz4sgqe0YQ.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/workflow-ci-runs-53-170w~MAxMQ7TiFvt_peAArj2ViA.webp 170w, /blog/images/workflow-ci-runs-53-340w~IZK0D8uJo6yhFuDNGNy3NA.webp 340w, /blog/images/workflow-ci-runs-53-680w~6MVyB_rf2AQZJ_1ML-jJCw.webp 680w, /blog/images/workflow-ci-runs-53-1360w~587YI9GedCt9uz4sgqe0YQ.webp 1360w&quot; alt=&quot;A stacked histogram illustrating the outcome of 5.3 CI workflow runs split into 'OK' and 'Fail' categories across 3 months, showing only one failure&quot; width=&quot;45%&quot;&gt;
&lt;/p&gt;
&lt;p&gt;The one failure on October 21 happened during Cygwin installation, and was hence not related to the test suite. In comparison, weekly runs of the 5.0 workflows tend to trigger issues. This below plot covers 8 workflows in contrast to 5.3's 12 workflows, as Cygwin, frame-pointer, and MSVC byte/native workflows were added since. Since the 5.0 release is older than 5.3, we also started recording weekly workflow outcomes for it sooner:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://tarides.com/blog/images/workflow-ci-runs-50-1360w~TBemZzEzBagiSN81hfbXbQ.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/workflow-ci-runs-50-170w~oh9VdUd6FLX7B-YdoJ58_w.webp 170w, /blog/images/workflow-ci-runs-50-340w~glF_vLQa_We6QdlOasRkAg.webp 340w, /blog/images/workflow-ci-runs-50-680w~iJAugUPo8KRE56egXeVXsQ.webp 680w, /blog/images/workflow-ci-runs-50-1360w~TBemZzEzBagiSN81hfbXbQ.webp 1360w&quot; alt=&quot;A stacked histogram illustrating the outcome of 5.0 CI workflow runs split into 'OK' and 'Fail' categories across 3 months, showing failures in all but 4 out of 22 runs&quot; width=&quot;45%&quot;&gt;
&lt;/p&gt;
&lt;p&gt;The plot clearly indicates that issues are triggered on 5.0 workflows more often that not. Ever since starting to note the cause of such failures in late August 2024, the triggered issues are genuine, and typically include &lt;a href=&quot;https://github.com/ocaml/ocaml/issues/12103&quot;&gt;crashes due to parallel access to &lt;code&gt;Buffer&lt;/code&gt;&lt;/a&gt;, pinpointing a case in &lt;code&gt;Buffer.add_string&lt;/code&gt; that flew under the radar of &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/11742&quot;&gt;the first &lt;code&gt;Buffer&lt;/code&gt; fix included in the 5.0.0 release&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Hidden Costs&lt;/h2&gt;
&lt;p&gt;We have worked to reach zero false alarms and now generally achieve it across an array of 31 CI workflows. We apply due diligence and have thus developed a workflow of going over the CI red lights to understand and summarise the failures – both the genuine ones and the false alarms. We then keep track of them as &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/issues&quot;&gt;issues in the multicoretests repo&lt;/a&gt;. This allows us to easily refer to them and spot trends, such as starting to see new failures or noticing that a particular failure no longer occurs.&lt;/p&gt;
&lt;p&gt;When we spot new errors, we work to reproduce them locally to make sure the issue is genuine. If so, we then report the issue upstream to &lt;code&gt;ocaml/ocaml&lt;/code&gt; along with a description of the required steps needed to reproduce it. When understanding the problem well enough, we also contribute with a compiler fix PR. Out of &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests#issues&quot;&gt;the 40 issues currently identified&lt;/a&gt;, Tarides engineers have filed PRs to fix 28 of these, 10 issues have been fixed by others (typically Inria or Jane Street engineers), and 2 issues remain open. Tarides have thus put in a significant effort to resolve errors.&lt;/p&gt;
&lt;h2&gt;Some Issues are Still Hard to Reproduce&lt;/h2&gt;
&lt;p&gt;Despite all our efforts to amplify problems and increase reproducibility, some issues are still hard to trigger. One such case was &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/12707&quot;&gt;ocaml/ocaml#12707&lt;/a&gt; in which we were able to trigger the assertion failure, albeit rarely. This one took some head-scratching until we realised the problem was caused by a small time window between reading the same atomic field twice in an assertion: &lt;code&gt;di-&amp;gt;backup_thread_msg == BT_INIT || di-&amp;gt;backup_thread_msg == BT_TERMINATE&lt;/code&gt;. This was carried out in parallel with a backup-thread transitioning from &lt;code&gt;BT_TERMINATE&lt;/code&gt; to &lt;code&gt;BT_INIT&lt;/code&gt; by an atomic write &lt;code&gt;atomic_store_release(&amp;amp;di-&amp;gt;backup_thread_msg, BT_INIT)&lt;/code&gt;, thus creating a tiny chance of neither of the conditions to be true, if the write would happen just in between the two reads. We could then manually insert a call to &lt;code&gt;sleep&lt;/code&gt; to confirm, and develop an appropriate fix.&lt;/p&gt;
&lt;p&gt;We are currently investigating &lt;a href=&quot;https://github.com/ocaml-multicore/multicoretests/issues/480&quot;&gt;an even rarer issue triggered by the &lt;code&gt;Gc&lt;/code&gt; tests, that causes a rare crash on macOS running on an ARM64 processor&lt;/a&gt; and seems to require just the right OCaml heap conditions to trigger. In both cases, despite not triggering on every PBT run, the randomised tests have nevertheless highlighted genuine issues that would otherwise only show up even more rarely on the ocaml/ocaml test suite or – worse – to end users of OCaml.&lt;/p&gt;
&lt;h2&gt;Lowering the Barrier to Entry for &lt;code&gt;multicoretests&lt;/code&gt; for Compiler Engineers&lt;/h2&gt;
&lt;p&gt;To offer compiler engineers the ability to run the test suite easily, &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13458&quot;&gt;we have made it possible to do so by labeling a PR with a &lt;code&gt;run-multicoretests&lt;/code&gt; tag&lt;/a&gt;. Recently &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13580&quot;&gt;a PR to improve major GC performance with mark-delay&lt;/a&gt; kicked off using the &lt;code&gt;run-multicoretests&lt;/code&gt; tag and is already making good use of the test suite as &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13580#issuecomment-2478454501&quot;&gt;it detected an issue with the GC marking of Ephemerons&lt;/a&gt;. With &lt;a href=&quot;https://github.com/ocaml/ocaml/pull/13616&quot;&gt;the test suite finding an issue in another GC improving PR&lt;/a&gt;, we are confident in the value addition that the test suite brings to compiler developer in helping quality-assure runtime-related PRs.&lt;/p&gt;
&lt;h2&gt;Usage Outside &lt;code&gt;multicoretests&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The usage of &lt;code&gt;STM&lt;/code&gt; has spread outside of &lt;code&gt;multicoretests&lt;/code&gt;. In &lt;a href=&quot;https://github.com/ocaml-multicore/saturn&quot;&gt;Saturn&lt;/a&gt;, a library of lock-free data structures for OCaml 5, both existing and new data structures come with &lt;code&gt;STM&lt;/code&gt; tests to help ensure their correctness. This started in connection with &lt;a href=&quot;https://github.com/ocaml-multicore/saturn/pull/43&quot;&gt;moving experimental tests of &lt;code&gt;ws_deque&lt;/code&gt; out of the &lt;code&gt;multicoretests&lt;/code&gt; suite&lt;/a&gt; and has continued since. In &lt;a href=&quot;https://github.com/ocaml-multicore/picos&quot;&gt;Picos&lt;/a&gt;, a library for composing effect-based schedulers, &lt;code&gt;STM&lt;/code&gt; tests also help ensure correctness of its underlying data structures.&lt;/p&gt;
&lt;p&gt;For the Gospel specification language for OCaml, Tarides has worked to develop &lt;a href=&quot;https://github.com/ocaml-gospel/ortac&quot;&gt;Ortac-QCheck-STM, as a plugin for Ortac&lt;/a&gt;. This is a tool to extract sequential &lt;code&gt;STM&lt;/code&gt; tests from a Gospel specification, thereby putting the strength of PBT in the hands of OCaml developers willing to annotate their interfaces with Gospel specifications. This effort is paying off, as &lt;a href=&quot;https://github.com/ocaml-gospel/ortac?tab=readme-ov-file#found-issues&quot;&gt;the tool is starting to find genuine issues&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using PBT to test OCaml 5 started as a blue-sky project at Tarides. Despite a range of challenges, the test suite has nevertheless reached a point where a run yields a clear signal free of false alarms and worthy of a confidence increase in a compiler code change. Getting here was a team effort with contributions from Charlène Gros, Samuel Hym, Olivier Nicole, Nicolas Osborne, and Naomi Spargo, along with patience and effort from OCaml compiler engineers working to fix our reported findings.&lt;/p&gt;
&lt;p&gt;The PBT approach has successfully pinpointed a range of issues across the &lt;code&gt;Stdlib&lt;/code&gt;, the rewritten OCaml 5 runtime system, and the restored backends. We hope that the test suite can continue to do so and thereby help maintain OCaml's reputation as a rock solid and safe platform.&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-12-23-multicore-property-based-tests-for-ocaml-5-challenges-and-lessons-learned</id><title type="text">Multicore Property-Based Tests for OCaml 5: Challenges and Lessons Learned</title><updated>2024-12-23T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://chshersh.com/atom.xml" rel="alternate"/><id>https://chshersh.com/atom.xml</id><title type="text">Dmitrii Kovanikov</title></source><link href="https://chshersh.com/blog/2024-12-20-pragmatic-category-theory-part-03.html" rel="alternate"/><content type="html">&lt;p&gt;Understanding why associativity matters&lt;/p&gt;
</content><id>https://chshersh.com/blog/2024-12-20-pragmatic-category-theory-part-03.html</id><title type="text">Pragmatic Category Theory | Part 3: Associativity</title><updated>2024-12-20T00:00:00-00:00</updated><author><name>chshersh</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-12-18-learn-ocaml-the-easy-way-including-the-hard-bits" rel="alternate"/><content type="html">&lt;p&gt;OCaml is a valuable and powerful tool, combining performance, security, and reliability. Joining the ecosystem, community, and learning the language can help you make the most of its capabilities if you're not already familiar with it. But how can we make learning OCaml easier? As with other functional programming languages, its visibility can be limited by factors such as your educational and professional environment, or the programmers you follow. Nevertheless, the world of software development is &lt;a href=&quot;https://tarides.com/blog/2024-03-07-a-time-for-change-our-response-to-the-white-house-cybersecurity-press-release/&quot;&gt;increasingly recognising the strengths of functional programming&lt;/a&gt; in general and &lt;a href=&quot;https://tarides.com/blog/2023-12-14-ocaml-memory-safety-and-beyond/&quot;&gt;OCaml in particular&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To meet the growing interest in OCaml, we aim to make learning the language as straightforward as possible. For individuals,  we’re prioritising tutorials, books, and events centred around OCaml. For teams and organisations, we offer &lt;a href=&quot;https://tarides.com/services/training/&quot;&gt;OCaml courses&lt;/a&gt; for both basic and advanced training.&lt;/p&gt;
&lt;h2&gt;The Best Ways to Learn OCaml: For the Independent Learner&lt;/h2&gt;
&lt;h3&gt;OCaml.org&lt;/h3&gt;
&lt;p&gt;Whether you’re looking to write a ‘hello world’ or delve into the details of the OCaml compiler, the central resource is OCaml.org. The &lt;a href=&quot;https://ocaml.org/docs&quot;&gt;Learn&lt;/a&gt; area provides a range of resources, including tutorials, exercises, and a directory of books and academic papers, catering to different skill levels. The page also offers ways to install the &lt;a href=&quot;https://ocaml.org/install#linux_mac_bsd&quot;&gt;latest version of OCaml&lt;/a&gt;, access the &lt;a href=&quot;https://ocaml.org/manual/5.2/index.html&quot;&gt;manual&lt;/a&gt;, and explore  OCaml’s documentation. The team has taken inspiration from other languages like Python, Rust, and the functional language Haskell to improve the resources available to learn OCaml.&lt;/p&gt;
&lt;p&gt;These resources are tailored to the independent learner, using their free time to pursue knowledge, or the &lt;a href=&quot;https://ocaml.org/academic-users&quot;&gt;teacher needing materials&lt;/a&gt; for their students. We support the continuous improvement of tutorials and documentation on OCaml.org, and the accessibility of books, papers, and other learning materials. The pages are also open-source, meaning that everyone is welcome to contribute!&lt;/p&gt;
&lt;h3&gt;Books&lt;/h3&gt;
&lt;p&gt;If you prefer learning from books, there are several available to choose from. For beginners, &lt;a href=&quot;https://cs3110.github.io/textbook/cover.html&quot;&gt;OCaml Programming: Correct + Efficient + Beautiful&lt;/a&gt; and &lt;a href=&quot;https://ocaml-book.com&quot;&gt;OCaml From the Very Beginning&lt;/a&gt; are excellent choices. The former is a textbook used at Cornell University, complete with a video playlist. Intermediate learners may find &lt;a href=&quot;https://dev.realworldocaml.org&quot;&gt;Real World OCaml&lt;/a&gt; and &lt;a href=&quot;https://www.amazon.com/gp/product/0957671113&quot;&gt;More OCaml: Algorithms, Methods, &amp;amp; Diversions&lt;/a&gt; beneficial. &lt;a href=&quot;https://dev.realworldocaml.org&quot;&gt;Real World OCaml&lt;/a&gt; is available for free download thanks to our &lt;a href=&quot;https://tarides.com/blog/2022-10-14-real-world-ocaml-book-giveaway/&quot;&gt;sponsorship of its gold open access release&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Community&lt;/h3&gt;
&lt;p&gt;OCaml’s robust open-source ecosystem significantly aids newcomers by making source code freely available for study, and offering access to experts and feedback through the &lt;a href=&quot;https://github.com/ocaml/ocaml&quot;&gt;GitHub repository&lt;/a&gt; and &lt;a href=&quot;https://discuss.ocaml.org&quot;&gt;Discuss forum&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are many more ways to engage with the OCaml community online:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Social media: head over to the &lt;a href=&quot;https://ocaml.org/community&quot;&gt;Community&lt;/a&gt; page on OCaml.org for an overview of the different places online people meet to discuss, develop, and blog about OCaml. Resources include more or less formal forums, educational text- or video-based content, entertainment, social networking, and live chats.&lt;/li&gt;
&lt;li&gt;Advent of Code: An &lt;a href=&quot;https://adventofcode.com/2024/about&quot;&gt;advent calendar&lt;/a&gt; of programming puzzles, suitable for a variety of skill levels and compatible with all languages, popular with programmers as a way to challenge themselves and learn new languages. This year, Sabine has organised &lt;a href=&quot;https://x.com/i/lists/1846851455384240406&quot;&gt;a leaderboard&lt;/a&gt; for people solving the advent of code in OCaml.&lt;/li&gt;
&lt;li&gt;The OCaml Planet: An &lt;a href=&quot;https://ocaml.org/ocaml-planet&quot;&gt;RSS feed aggregator hosted on OCaml.org&lt;/a&gt; which shares articles and blog posts on everything OCaml. Follow it to stay up-to-date on the latest OCaml news, technical deep dives, open-source project updates, and developer insights.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Events&lt;/h3&gt;
&lt;p&gt;We also host and sponsor community events like &lt;a href=&quot;https://tarides.com/blog/2024-05-01-we-host-our-first-ocaml-retreat-in-india/&quot;&gt;hacking days&lt;/a&gt; and &lt;a href=&quot;https://tarides.com/blog/2024-11-13-the-new-conference-on-the-block-what-is-fun-ocaml/&quot;&gt;conferences&lt;/a&gt; to further support users who want to learn more about OCaml. Upcoming events include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The OCaml Workshop: Continuing in a well-established tradition, the OCaml Workshop will be held at the &lt;a href=&quot;https://icfp25.sigplan.org/&quot;&gt;2025 ICFP Conference&lt;/a&gt;. With a more  academic focus, talks start out as papers which are then presented as part of the OCaml track.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fun-ocaml.com/&quot;&gt;The FUN OCaml Conference&lt;/a&gt;: Returning for a second time in 2025, the new OCaml conference is organised by developers for developers and covers topics big and small.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reason-ocaml.in/&quot;&gt;Reason OCaml India Meetups&lt;/a&gt;: A community of Reason and OCaml enthusiasts that organise regular in person and online meet-ups.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are of course many more events organised outside of those we host and sponsor, and some notable examples are &lt;a href=&quot;https://confengine.com/conferences/functional-conf-2025&quot;&gt;Functional Conf&lt;/a&gt; and &lt;a href=&quot;https://www.meetup.com/ocaml-paris/&quot;&gt;OUPS&lt;/a&gt;. We recommend that you check out the &lt;a href=&quot;https://ocaml.org/events&quot;&gt;Events&lt;/a&gt; page to get an overview of all the great OCaml events you could join!&lt;/p&gt;
&lt;h2&gt;The Best Ways to Learn OCaml: Tailored Training in OCaml&lt;/h2&gt;
&lt;p&gt;In large organisational teams, relying on individual learning or the dissemination of knowledge from a few experts can be time-consuming and inconsistent. Tailored courses enable simultaneous training for the entire group, fostering collaboration in tackling new challenges. To make learning OCaml easier for groups, we’ve created courses to enhance team competence in the language. Here’s what our OCaml courses offer.&lt;/p&gt;
&lt;h3&gt;Our Foundational Course &lt;a href=&quot;https://tarides.com/services/training/&quot;&gt;&lt;em&gt;Starting with OCaml: An Introduction&lt;/em&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This course teaches core OCaml programming concepts through theory and practice, and has been designed to facilitate a smooth transition for engineers from other languages, including imperative programming. It covers OCaml’s history, design choices, and key functional programming concepts such as functions, recursion, and higher-order functions alongside critical concepts like tuples, the OCaml type system, type inference, pattern matching, and polymorphism.&lt;/p&gt;
&lt;p&gt;The bulk of the course comprises several modules that introduce the practical building blocks of programming with OCaml. This includes methods: imperative and modular programming, data structures, error handling, interfacing with C, and command-line parsing. It also offers guidance on tooling: &lt;code&gt;opam&lt;/code&gt;, Dune, VSCode, debugging and profiling, concurrent programming with &lt;code&gt;lwt&lt;/code&gt;, and testing using expect tests and QuickCheck.&lt;/p&gt;
&lt;p&gt;Finally, the course would not be complete without hands-on experience writing OCaml code. The modules on setting up the OCaml environment, building, documenting, and releasing an OCaml project culminate in an entire day spent on building an OCaml application from scratch. Participants will leave with a solid understanding of OCaml, the necessary tools for development, and practical application-building experience. Following this foundational course, your entire team will be familiar with the OCaml language and workflow.&lt;/p&gt;
&lt;h3&gt;Our Higher-Level Course &lt;a href=&quot;https://tarides.com/services/training/&quot;&gt;&lt;em&gt;Mastering OCaml: Advanced Techniques&lt;/em&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The advanced course is designed for experienced users and teams looking to enhance their skills. It consists of three sections: advanced OCaml techniques, advanced methods for common OCaml tools, and advanced tooling.&lt;/p&gt;
&lt;p&gt;Advanced OCaml techniques make up the bulk of the course, including generalised algebraic data types (&lt;a href=&quot;https://ocaml.org/manual/5.2/gadts-tutorial.html&quot;&gt;GADTs&lt;/a&gt;), multicore programming with OCaml 5, and transitioning from OCaml 4 to OCaml 5. Advanced methods for common OCaml tools cover using the  &lt;a href=&quot;https://dune.build&quot;&gt;Dune&lt;/a&gt; build system, and &lt;a href=&quot;https://ocaml.org/docs/metaprogramming&quot;&gt;PPX preprocessors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Topics in advanced tooling include: the &lt;a href=&quot;https://mirage.io&quot;&gt;MirageOS&lt;/a&gt; library operating system for secure, high-performance, network applications; web development with  &lt;a href=&quot;https://ocsigen.org/js_of_ocaml/latest/manual/overview&quot;&gt;JS_of_ocaml&lt;/a&gt;, &lt;a href=&quot;https://github.com/ocaml-wasm/wasm_of_ocaml&quot;&gt;Wasm_of_ocaml&lt;/a&gt;, and &lt;a href=&quot;https://aantron.github.io/dream/&quot;&gt;Dream&lt;/a&gt;; concurrent programming with [Eio], and using &lt;a href=&quot;https://tarides.com/blog/2024-04-24-under-the-hood-developing-multicore-property-based-tests-for-ocaml-5/&quot;&gt;property-based testing&lt;/a&gt; to evaluate programs. This course aims to equip teams already using OCaml with new skills and tools. An advanced course with deep topics, the course is tailored to focus on relevant areas for your team, taking them to the next level.&lt;/p&gt;
&lt;p&gt;Finally, one of the biggest benefits we offer with our courses is &lt;strong&gt;customisation&lt;/strong&gt;. We understand that teams have unique challenges and goals, and offer you the option to create a course that fits your needs. Not only can you pick a selection of topics from the &lt;a href=&quot;https://tarides.com/services/training/&quot;&gt;advanced OCaml&lt;/a&gt; modules, but you can completely tailor the training with a customised itinerary. This is negotiated on a per-instance basis, and you can schedule a &lt;a href=&quot;https://tarides.com/contact/&quot;&gt;free consultation&lt;/a&gt; to discuss what that could look like for your teams.&lt;/p&gt;
&lt;h2&gt;Get in Touch&lt;/h2&gt;
&lt;p&gt;Need help learning OCaml? Reach out to the community on &lt;a href=&quot;https://mastodon.social/@ocaml@discuss.tchncs.de&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://x.com/ocaml_org&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://www.linkedin.com/company/ocaml-org/&quot;&gt;LinkedIn&lt;/a&gt; to get helpful pointers or ask questions on &lt;a href=&quot;https://discuss.ocaml.org&quot;&gt;Discuss&lt;/a&gt;. If you’re interested in our courses for your teams, you can &lt;a href=&quot;https://tarides.com/contact/&quot;&gt;get a free consultation&lt;/a&gt; to discuss whether the curriculum is right for you and sign up to our mailing list to receive the latest news.&lt;/p&gt;
&lt;p&gt;We look forward to hearing from you, please don’t hesitate to get in touch!&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-12-18-learn-ocaml-the-easy-way-including-the-hard-bits</id><title type="text">Learn OCaml the Easy Way - Including the Hard Bits</title><updated>2024-12-18T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.12.17.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.17.html#1&quot;&gt;Opam repository archival, Phase 1: unavailable packages&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.17.html#2&quot;&gt;Proposed Package Archiving Policy for the opam Repository&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.17.html#3&quot;&gt;QCheck 0.23&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.17.html#4&quot;&gt;OCaml's Code of Conduct team - rotation of one team member&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.17.html#5&quot;&gt;qcheck-lin and qcheck-stm 0.2&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.12.17.html</id><title type="text">OCaml Weekly News, 17 Dec 2024</title><updated>2024-12-17T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-12-11-saturn-1-0-data-structures-for-ocaml-multicore" rel="alternate"/><content type="html">&lt;p&gt;The first version of the Saturn library is out! &lt;a href=&quot;https://github.com/ocaml-multicore/saturn&quot;&gt;Saturn&lt;/a&gt; is a new OCaml 5 library available on &lt;a href=&quot;https://opam.ocaml.org/&quot;&gt;opam&lt;/a&gt;, which offers a collection of well-tested, benchmarked, and efficient concurrent data structures ready to be used with OCaml Multicore. Access to concurrent-safe data structures saves developers from the time-consuming and often error-prone process of designing their own.&lt;/p&gt;
&lt;p&gt;This post will give you an overview of the library, its main features, and some use cases. The team encourages you to try the data structures and share your feedback &lt;a href=&quot;https://github.com/ocaml-multicore/saturn&quot;&gt;in the repo&lt;/a&gt; and on &lt;a href=&quot;https://discuss.ocaml.org/&quot;&gt;Discuss&lt;/a&gt;. After you're finished with this post, I also recommend you read the &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/12/Saturn-a-library-of-verified-concurrent-data-structures-for-OCaml-5&quot;&gt;paper on Saturn&lt;/a&gt; from the OCaml workshop at ICFP 2024 if you want more details. Let’s dig in!&lt;/p&gt;
&lt;h2&gt;What is Saturn?&lt;/h2&gt;
&lt;p&gt;The Saturn repository is made up of one package, &lt;code&gt;saturn&lt;/code&gt;, containing all lock-free data structures. You can use Saturn with OCaml 5.2 or later, and it can be installed from &lt;code&gt;opam&lt;/code&gt; with the command &lt;code&gt;opam install saturn&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Saturn covers many use cases, from simple stacks and queues to more complex structures, including skip lists, hash tables, work-stealing deques, and more. All of them have been adapted to be compatible with and take advantage of the OCaml 5 memory model. For example, the team behind Saturn had to rework the Michael-Scott queue to avoid memory leaks.&lt;/p&gt;
&lt;p&gt;To improve performance, they introduced several micro-optimisations, including preventing false sharing, adding fenceless atomic reads where possible (improving performance on ARM processors), and avoiding extra indirection in arrays and atomics to reduce memory consumption. While optimising the library, their work highlighted some missing features in OCaml 5 and led to upstreamed improvements to the language, such as padded atomics and fixing a CSE bug.&lt;/p&gt;
&lt;h2&gt;Why Saturn?&lt;/h2&gt;
&lt;p&gt;Sharing data between multiple threads or cores is a well-known problem in computer science. The most obvious solution is to use a sequential data structure protected by a lock, but this approach can introduce performance overhead due to contention between locks. Liveness issues like deadlock, starvation, and priority inversion are also associated with locks in parallel programming.&lt;/p&gt;
&lt;p&gt;The opposite approach is to use a lock-free implementation, relying on fine-grained synchronisation instead of locks. This approach benefits from higher performance and guarantees system-wide progress. However, it does come with its own set of bugs, including the ABA problem (which is largely mitigated in garbage-collected languages), data races, and unexpected behaviours as a result of non-linearisability.&lt;/p&gt;
&lt;p&gt;In this quagmire of pitfalls, Saturn stands out as a source of reliable, tested data structures that the user can adopt for their own projects. Developers can pick from a variety of structures knowing that they are suitable and safe, saving them time and making OCaml 5 easier to use.&lt;/p&gt;
&lt;h3&gt;Benchmarks&lt;/h3&gt;
&lt;p&gt;The library is still relatively new, so more benchmarks are still to come, but there are some numbers to give you an idea of performance. You can find tables showing the throughput of different queues and stacks on single and multiple domains in the &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/12/Saturn-a-library-of-verified-concurrent-data-structures-for-OCaml-5&quot;&gt;paper from the OCaml Workshop at ICFP 2024&lt;/a&gt; about Saturn. The tests reveal that Saturn implementations either outperform or, in the case of simpler implementations, match the performance of non-Saturn structures. However, the benefit of using Saturn, even in cases where another simple implementation exists, is that the Saturn data structures have built-in optimisations and synchronisation safeguards, and it’s up to each individual developer to decide what that’s worth to them.&lt;/p&gt;
&lt;p&gt;The library also has a benchmarking command &lt;a href=&quot;https://github.com/ocaml-multicore/saturn/blob/main/bench/README.md&quot;&gt;&lt;code&gt;make bench&lt;/code&gt;&lt;/a&gt; that can be run from the root of the repository to run a standard set of benchmarks. It outputs in JSON and is intended to be consumed by &lt;a href=&quot;https://bench.ci.dev/ocaml-multicore/saturn/branch/main/benchmark/default?worker=fermat&amp;amp;image=bench.Dockerfile&quot;&gt;&lt;code&gt;current-bench&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Tests&lt;/h3&gt;
&lt;p&gt;Testing Saturn’s structures was a high priority for the team and crucial to ensure the safety, &lt;a href=&quot;https://dl.acm.org/doi/10.1145/78969.78972#:~:text=Linearizability%20is%20a%20correctness%20condition,techniques%20from%20the%20sequential%20domain.&quot;&gt;linearisability&lt;/a&gt;, and lock-freedom where expected. Saturn was tested using two primary tools: DSCheck and STM. &lt;a href=&quot;https://tarides.com/blog/2024-04-24-under-the-hood-developing-multicore-property-based-tests-for-ocaml-5/&quot;&gt;STM&lt;/a&gt; is used for both unit testing and linearisability. It can automatically generate random full programs using the API provided, and for Saturn, this is a data structure. It then executes the programs in parallel with two domains and checks all the results against the &lt;code&gt;post-conditions&lt;/code&gt; of each function, providing unit testing. STM performs a sequence of random commands in parallel, records the results, and checks whether the observed results can be linearised and reconciled with some sequential execution.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tarides.com/blog/2024-02-14-multicore-testing-tools-dscheck-pt-1/&quot;&gt;DSCheck&lt;/a&gt; is a model checker designed to compute all the possible interleavings of instructions between multiple domains and verify that each one returns the expected result. This method is useful for detecting bugs that only occur in rare circumstances which would otherwise be hard to catch. DSCheck can also be used to verify that a program is lock-free, as it will fail to terminate if any form of blocking is present in the program. The DSCheck implementation has been optimised to make the tests efficient even on Saturn’s more complex data structures.&lt;/p&gt;
&lt;h3&gt;Formal Verification&lt;/h3&gt;
&lt;p&gt;The verification work for Saturn is done at Inria, forming part of Clément Allain's PhD work there. Due to the notoriously finicky behaviours of lock-free algorithms, they have formally verified part of Saturn’s data structures and aim to keep going until they’ve covered the entire library. The main criterion for correctness in concurrent data structures is linearisability, which requires each operation in a data structure to appear to take effect instantaneously at some point during its execution (called the linearisation point) so that all linearisation points from all operations form a coherent, sequential history. The team at Inria are formally verifying that this linearisability is correctly present in Saturn's data structures.&lt;/p&gt;
&lt;p&gt;To verify linearisability, they use the mechanised concurrent separation logic &lt;a href=&quot;https://iris-project.org/&quot;&gt;IRIS&lt;/a&gt;, which has been used in the past to verify realistic data structures. All proofs are formalised in &lt;a href=&quot;https://coq.inria.fr&quot;&gt;Coq&lt;/a&gt; and available on GitHub. They manually translated the original code from Saturn to a deeply embedded language in Coq, hoping to potentially automate the process in the future.  Work continues to verify data structures for Saturn, ensuring safe and predictable behaviours.&lt;/p&gt;
&lt;h2&gt;Stay in Touch!&lt;/h2&gt;
&lt;p&gt;To delve deeper into everything Saturn, I recommend you watch the &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=Z5eUhyNFUjWSI43v&amp;amp;t=24398&quot;&gt;ICFP 2024 presentation&lt;/a&gt; from the OCaml Workshop. You can try Saturn for your projects via the &lt;a href=&quot;https://github.com/ocaml-multicore/saturn&quot;&gt;repo&lt;/a&gt;, and don’t forget to share your feedback with the team!&lt;/p&gt;
&lt;p&gt;Connect with us online on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt; to stay updated on our latest projects.&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-12-11-saturn-1-0-data-structures-for-ocaml-multicore</id><title type="text">Saturn 1.0: Data structures for OCaml Multicore</title><updated>2024-12-11T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-plugins/metacsl.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-plugins/metacsl.html</id><title type="text">MetAcsl v0.8 for Frama-C 30.0 Zinc</title><updated>2024-12-11T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.12.10.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#1&quot;&gt;Release of cppo 1.8.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#2&quot;&gt;New releases of Merlin and OCaml-LSP&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#3&quot;&gt;New release of baby&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#4&quot;&gt;Release of Saturn 1.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#5&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#6&quot;&gt;Dune 3.17&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#7&quot;&gt;Spec and interface to declare dependencies in an OCaml script&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.10.html#8&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.12.10.html</id><title type="text">OCaml Weekly News, 10 Dec 2024</title><updated>2024-12-10T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-plugins/frama-clang.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-plugins/frama-clang.html</id><title type="text">Frama-Clang v0.0.17 for Frama-C 30.0~ Zinc</title><updated>2024-12-09T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-versions/zinc.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-versions/zinc.html</id><title type="text">Release of Frama-C 30.0 (Zinc)</title><updated>2024-12-05T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/2024-12-04-github-sponsor.html" rel="alternate"/><content type="html">&lt;p&gt;A new way to sponsor our cooperative&lt;/p&gt;
</content><id>https://blog.robur.coop/articles/2024-12-04-github-sponsor.html</id><title type="text">Sponsor us via GitHub</title><updated>2024-12-04T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-12-04-irmin-on-mirageos-under-the-hood-with-notafs" rel="alternate"/><content type="html">&lt;p&gt;In part one of our series on Notafs, we outlined the motivations and challenges behind creating a file system for &lt;a href=&quot;https://mirage.io/docs/overview-of-mirage&quot;&gt;MirageOS&lt;/a&gt; tailored to the &lt;a href=&quot;https://tarides.com/blog/2023-07-31-ocaml-in-space-welcome-spaceos/&quot;&gt;SpaceOS&lt;/a&gt; use case of storing large files on disk in satellites. Furthermore, Notafs can be used to run the &lt;code&gt;irmin-pack&lt;/code&gt; backend of Irmin, which, in turn, provides users with an amazing file system for their MirageOS projects.&lt;/p&gt;
&lt;p&gt;In this post, we delve into more detail concerning the design choices behind &lt;code&gt;Notafs&lt;/code&gt; and provide some benchmarks and visualisations to give you a better understanding of the file system. Let's dig in!&lt;/p&gt;
&lt;h2&gt;Big Design Decisions: Copy-on-Write&lt;/h2&gt;
&lt;h3&gt;The Limits of File Systems&lt;/h3&gt;
&lt;p&gt;In our previous post, we mentioned some of the expectations of file systems to resist corruption and data loss. Ensuring these conditions are met can be challenging since hardware exhibits many failure modes. For example, it is impossible to check if a disk has physically persisted a write operation. In order to improve performance, disks use RAM buffering to quickly store the write requests before committing them persistently (which is a much slower process).  Short of unplugging the disk and waiting for its internal battery and RAM to clear before rebooting, we can't know that the writes were executed and must be ready for the worst-case scenario: unwritten, partially written, or corrupted data regions on the disk! Writes could also be reordered, leading to consistency issues.&lt;/p&gt;
&lt;p&gt;Furthermore, the way that disks are structured presents its challenges. Disks are organised into blocks of fixed sizes, like 512 bytes, 1 kb, or 4kb, depending on the model. A block is the smallest unit we can read or write, so even if we only want to update a few bytes on the disk, we still need to re-write the full surrounding block data. Different disk models can have additional constraints, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hard drives that favour contiguous block reads and writes to ensure high performance since spinning their head to random locations is expensive.&lt;/li&gt;
&lt;li&gt;SSD exhibits faster wear if one block is updated more often than the others, leading to a shorter device life than what's expected under uniform usage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;All About Copy-on-Write&lt;/h3&gt;
&lt;p&gt;Considering these factors, Notafs was designed as a copy-on-write file system. This strong design principle means that when we update the file system, we never override a block in place but rather write the new data block in a new, unused location on the disk. Preserving the old block means that Notafs maintains access to a backup until the block is reused. If a write operation fails, for example, in case of a power outage in the middle of multiple block updates, Notafs can recover information from the old block.&lt;/p&gt;
&lt;p&gt;This strategy does not impact performance since writing the new data in a new location or in place is virtually indistinguishable from the disk's perspective. From an implementation standpoint, copy-on-write is very similar to how purely functional data structures work to provide fast updates. Only modified data needs to be copied, so fast updates are still possible, as sharing unmodified content between revisions is free.&lt;/p&gt;
&lt;p&gt;To implement the copy-on-write design, we needed to keep track of the unused free blocks where writes can be performed. This is akin to the challenge of implementing a memory allocator, but it is made slightly simpler because we only need to allocate fixed-size blocks. Our block allocator is backed by a queue containing the list of free block identifiers. After formatting a disk, the queue is (virtually) initialised to contain all block locations except a couple of reserved blocks. When a new block needs to be written to the disk, we pick a free block location from the queue where the write should take place. Filesystem operations are carefully implemented to free old blocks which aren't reachable anymore by pushing their block identifier location back onto the queue. Once the disk is full, these free blocks will eventually be reused to store new data. At the same time, old data is kept intact and can be used as a backup if needed. To avoid complications in determining if and when a block can be freed, we enforce strict linear ownership of blocks so that we do not need any reference counting or advanced garbage collection.&lt;/p&gt;
&lt;p&gt;The queue of free blocks is part of the file system state and must be recoverable when we boot it. This is why the queue state itself is backed by a copy-on-write data structure, which is stored on disk! To ensure good performance, the block locations that have been freed during a single file system operation are sorted before being pushed to the queue. This process favours the allocation of contiguous block locations, leading to faster writes on hard drives. Consecutive block locations are also compressed in the queue by representing them as intervals instead of listing all the intermediate block locations.&lt;/p&gt;
&lt;p&gt;When the disk gets full, the previously freed blocks will eventually be reused, storing new data. When that happens, the queue guarantees that we use the blocks that have been free for the longest time so that only the oldest backups become unrecoverable. However, since reusing blocks can lead to data consistency issues if some writes fail to be committed on disk, we needed a way to detect whether a block contains the expected latest data or the old partially-written/corrupted data. To this end, we use a checksum of the stored data to validate each block. We could have stored the checksum in the block next to the data it validates, but it would only have allowed for the detection of corrupted data and not for detecting whether the block's data is old since the old checksum remains valid. So, instead, we store the checksum in the parent block next to the block location it validates. When a parent block needs to read one of its child block's data, it uses its checksum to verify that its child data is as expected.&lt;/p&gt;
&lt;h3&gt;Final Considerations&lt;/h3&gt;
&lt;p&gt;There was one last issue for our team to tackle regarding the design: if everything is copy-on-write and the new filesystem root is written to a new block every time, how do we find the location of the latest root when the unikernel boots? We wanted to avoid scanning the whole disk in search of the latest root. Hence, when formatting a new disk, we reserved a fixed number of blocks at known locations. After each file system update, one of these blocks is updated in place to point to the latest root allocation in a round-robin fashion, with a generation counter to help our application determine which one was written last.&lt;/p&gt;
&lt;p&gt;When the filesystem starts, it identifies the latest root from the reserved blocks and traverses all of its reachable data to check each block checksum and validate that the whole filesystem is in a consistent state. If an issue is detected, implying the disk wasn't shut down properly, Notafs will attempt to use the previous filesystem root found in the previous reserved block. This procedure effectively rolls back the failed write operation until a valid filesystem state is found (or the disk is deemed unrecoverable!). To avoid SSD wear from updating the reserved root blocks too often compared to other blocks, we have added an extra, dynamically allocated level of indirection to the previous explanations, which amortises the number of updates to the reserved blocks.&lt;/p&gt;
&lt;p&gt;Finally, we implemented several optimisations to the above scheme to best use runtime resources. For example, we save disk space by representing block locations with as few bytes as possible, depending on the disk size. A 'Least-Recently-Used (LRU) cache keeps track of the latest reads and bufferises the incoming writes. This LRU enables Notafs to run with very low memory overhead, even when a file system operation would have required more RAM if another system had performed it.&lt;/p&gt;
&lt;p&gt;We are very happy with this design because it combines simplicity with high data consistency guarantees. We used the setup to implement a copy-on-write rope data structure to represent files of any size with fast updates and read operations. We added a rudimentary hierarchical structure of folders and files on top to enable multiple file usage. This last part has not been optimised and is only suitable for applications with a low number of files. It will get slower the more files you have, but there are no hard limits.&lt;/p&gt;
&lt;h2&gt;Benchmarks and Tests&lt;/h2&gt;
&lt;p&gt;Since filesystem correctness is critical, we tested all steps of the project to validate our solution. Of special interest is the final verification that our team realised with the &lt;a href=&quot;https://github.com/ocaml-gospel/gospel&quot;&gt;Gospel specification language&lt;/a&gt; using &lt;a href=&quot;https://github.com/ocaml-gospel/ortac&quot;&gt;Ortac&lt;/a&gt; to translate the specification into a &lt;a href=&quot;https://ocaml-multicore.github.io/multicoretests/&quot;&gt;QCheck test suite&lt;/a&gt; from the formal spec. By comparing the behaviour of our Notafs file system with an obviously correct in-memory reference model, we could stress-test our system by simulating a huge number of tests to detect discrepancies. Our experience with Gospel made a strong case for it being the future of documentation, with high-level specifications that are both mechanically verifiable and readable by end-users.&lt;/p&gt;
&lt;p&gt;Ultimately, correctness alone was not enough to justify using Notafs in production if it did not meet our performance goals. We benchmarked Notafs in comparison to existing MirageOS file systems for the features that were critical for SpaceOS and Irmin support. Those features were the ability to write large files, to read back those files, and to read only a sub-range in those files (a feature required by &lt;code&gt;irmin-pack&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tarides.com/blog/images/Notafs-benchmarks-1360w~uaILlPW6mfmLZu5wyaqOxw.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/Notafs-benchmarks-170w~GGy6BO7qAC8VTQTNcR6sBA.webp 170w, /blog/images/Notafs-benchmarks-340w~0CPnOJbfrX12fyRVpRu5dQ.webp 340w, /blog/images/Notafs-benchmarks-680w~tTRunmNYMr9Du8jyZ6oBtA.webp 680w, /blog/images/Notafs-benchmarks-1360w~uaILlPW6mfmLZu5wyaqOxw.webp 1360w&quot; alt=&quot;Notafs benchmarks in three graphs, comparing the speed of different file systems when it comes to writing and reading large files.&quot;&gt;&lt;/p&gt;
&lt;p&gt;While the TAR file format will always be the fastest thanks to its simplicity, it comes with the limitation that files can't be removed or updated (hence eventually running out of disk space!). In general, the benchmarks confirm that Notafs meets its goal of handling large files efficiently. In particular, its careful use of memory with LRU enables Notafs to run the benchmarks on larger files than the alternative – for unikernels with restricted max memory usage. Note that benchmarks are always subject to interpretation; we did not benchmark the scenarios where we expect Notafs to perform the worst, e.g. with a large number of small files!&lt;/p&gt;
&lt;p&gt;Our next image is actually a video showing Irmin running on top of a small Notafs disk. It combines everything we have explained so far: (epilepsy warning: the video displays flashing lights)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The disk is divided into blocks of 1024-sized bytes, conveniently represented by 32x 32px squares. The blocks in colour contain live data used by the Irmin store, while the grey crossed-out ones are the free blocks. The colourful blocks create distinctive patterns that indicate the different types of data that Irmin uses to store the database.&lt;/li&gt;
&lt;li&gt;As more Irmin operations occur during the test's execution, you can see the Notafs block allocator in action, circling around the disk to write new data to the oldest freed block.&lt;/li&gt;
&lt;li&gt;At the bottom of the video, two histograms show the distribution of the reads and writes on each block to check that fair usage of each block is achieved.&lt;/li&gt;
&lt;li&gt;Finally, we can see the Irmin garbage collector clearing its old history on a frequent basis to avoid running out of disk space.&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;
&lt;video autoplay=&quot;&quot; loop=&quot;&quot; style=&quot;width: 100%&quot;&gt;
  &lt;source src=&quot;/blog/images/notafs-video~zJZ_zyxrLnCkQb_phqW_0w.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/center&gt; 
&lt;p&gt;This visualisation was made possible because MirageOS libraries can also be used on standard operating systems without requiring special compilation by a unikernel. In other words, Notafs also enables Irmin to be used as a single-file database (like &lt;a href=&quot;https://www.sqlite.org/onefile.html&quot;&gt;sqlite&lt;/a&gt;, which might be of interest for some applications' workflows.  We want to do more work on this front to lift the limitation that the database size must currently be fixed in advance when formatting the disk.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Developing a new filesystem has been a very exciting project, and we hope that you'll enjoy the new options when developing unikernels with MirageOS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Notafs:&lt;/strong&gt; for applications which require a limited number of very large files (e.g. SpaceOS satellite pictures).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Irmin on Notafs:&lt;/strong&gt; for a general-purpose file system, including optimisations to support a large number of arbitrarily sized files and many advanced features (with the caveat that this version of Irmin requires OCaml 5, which is still experimental on MirageOS).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We invite you to participate and try Notafs yourself! Please consult the &lt;a href=&quot;https://github.com/tarides/notafs&quot;&gt;open-source Notafs repository&lt;/a&gt; for further information, and share your experience (good and bad!) with the team. You can stay in touch with us on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-12-04-irmin-on-mirageos-under-the-hood-with-notafs</id><title type="text">Irmin on MirageOS: Under-the-Hood With Notafs</title><updated>2024-12-04T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.12.03.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#1&quot;&gt;Good example of handwritten Lexer + Recursive Descent Parser?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#2&quot;&gt;Boulder Dash in OCaml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#3&quot;&gt;Js_of_ocaml 5.9.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#4&quot;&gt;Liquidsoap 2.3.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#5&quot;&gt;Bytesrw 0.1.0 – Composable byte stream readers and writers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#6&quot;&gt;dream-html and pure-html 3.5.2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#7&quot;&gt;Second beta release of OCaml 5.3.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#8&quot;&gt;New release of Monolith&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#9&quot;&gt;Jsont 0.1.0 – Declarative JSON data manipulation for OCaml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#10&quot;&gt;Tiny educational concurrent I/O and promises library&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#11&quot;&gt;Eliom 11.1: Towards Web Assembly support&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#12&quot;&gt;Areas and Adversaries&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#13&quot;&gt;MariaDB 1.2.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#14&quot;&gt;Proposed Package Archiving Policy for the opam Repository&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#15&quot;&gt;capnp-rpc 2.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.12.03.html#16&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.12.03.html</id><title type="text">OCaml Weekly News, 03 Dec 2024</title><updated>2024-12-03T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-11-27-irmin-on-mirageos-introducing-the-notafs-file-system" rel="alternate"/><content type="html">&lt;p&gt;We are pleased to announce one (or two) new filesystems for MirageOS! The motivation behind creating them is an exciting new use case requiring the system to store data on disk.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mirage.io/docs/overview-of-mirage&quot;&gt;MirageOS&lt;/a&gt; allows you to compile OCaml applications into unikernels. By selecting the operating system functionalities required, a unikernel can be constructed for your application using only the necessary components. This process reduces the attack surface and increases the hardware efficiency of your final application. MirageOS unikernels can be deployed to various cloud and mobile platforms.&lt;/p&gt;
&lt;p&gt;As a case in point, Tarides is developing &lt;a href=&quot;https://tarides.com/blog/2023-07-31-ocaml-in-space-welcome-spaceos/&quot;&gt;SpaceOS&lt;/a&gt; to run unikernels on satellites, an industry where both security and performance are critical. While MirageOS has a long history of cloud usage and comes with advanced network capabilities, SpaceOS provides an interesting use case for disk storage. A possible use case for satellites is to take pictures from space and send them to Earth for analysis, and since satellites may not be able to send the pictures right away (due to their location, for example), storing high-resolution images on a disk is a must.&lt;/p&gt;
&lt;p&gt;So, naturally, the question we posed ourselves was what MirageOS file system could we use for SpaceOS? And that started us down the journey to the new file system Notafs, that even lets you use Irmin on top!&lt;/p&gt;
&lt;h2&gt;Why Create a New Filesystem?&lt;/h2&gt;
&lt;p&gt;At the start, there were a couple of existing filesystems available for MirageOS that we needed to evaluate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/yomimono/chamelon&quot;&gt;Chamelon&lt;/a&gt; filesystem is designed to efficiently support a large number of very small files on a disk.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/dinosaure/docteur&quot;&gt;Docteur&lt;/a&gt; filesystem which provides read-only file compression.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/mirage/ocaml-fat/tree/main&quot;&gt;FAT&lt;/a&gt; filesystem, which provides support for the (old) standard &lt;a href=&quot;https://en.wikipedia.org/wiki/File_Allocation_Table&quot;&gt;FAT16&lt;/a&gt; filesystem with restrictions on file sizes.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/mirage/ocaml-tar&quot;&gt;TAR&lt;/a&gt; file format, which has the limitation that users can only add new files: deletion or modification of existing files is not supported.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of the above libraries are well-tested and highly recommended if they fit your application needs! But, as we will illustrate in part 2 with our benchmarks, none of these systems satisfied the SpaceOS requirement to store large files.&lt;/p&gt;
&lt;p&gt;Furthermore, using a conventional file system like the ones available on traditional operating systems like Linux and Windows was also not an option since they are closely tied to large operating system functionalities. This is because to ensure higher security, MirageOS libraries are built using the &lt;a href=&quot;https://ocaml.org&quot;&gt;OCaml programming language&lt;/a&gt;, which provides &lt;a href=&quot;https://tarides.com/blog/2023-12-14-ocaml-memory-safety-and-beyond/&quot;&gt;strong memory-safety guarantees&lt;/a&gt;. While this is a fantastic design choice for the &lt;a href=&quot;https://tarides.com/blog/2024-03-07-a-time-for-change-our-response-to-the-white-house-cybersecurity-press-release/&quot;&gt;future of computing&lt;/a&gt;, it is harder to support a conventional filesystem (programmed in C/C++) natively on MirageOS. A unikernel based on that kind of technology would lose the appeal of a small software stack with high-security guarantees. In comparison, the Notafs solution fits into four thousand lines, which is a much more ‘human-sized’ project to review.&lt;/p&gt;
&lt;h2&gt;The Challenges With Filesystems&lt;/h2&gt;
&lt;p&gt;Implementing a filesystem is a complex task requiring compromises, which explains the lack of options for storing large files with the SpaceOS project. For example, even though file systems may appear simple, with an interface that everyone is familiar with, their critical main function is to protect applications from (recoverable) hardware defects. Unless the disk dies, the user should never lose or see corrupted data, even if a power outage was to interrupt the filesystem in the middle of an operation. In other words, a filesystem update should have transactional semantics: either the operation succeeds, or it does not, but applications should never observe an in-between broken state. Without this property, software built on top of the file system would be vulnerable to experiencing faults.&lt;/p&gt;
&lt;p&gt;Tarides is well aware of the challenges involved with implementing a filesystem – we maintain the &lt;a href=&quot;https://irmin.org&quot;&gt;Irmin database&lt;/a&gt;, which provides a hierarchical key-value store with high data consistency guarantees, git-inspired history for rollbacks, and distributed replication over the network. While the Irmin API resembles a filesystem, it is a full-blown database and provides many more functionalities. So rather than starting a new filesystem implementation from scratch, we asked ourselves whether we could reuse the Irmin database as a filesystem for MirageOS.&lt;/p&gt;
&lt;h2&gt;Irmin as a Filesystem?&lt;/h2&gt;
&lt;p&gt;Using Irmin for this purpose is not a new idea, and Irmin already provides multiple backends that allow users to run its databases on various platforms with different constraints. The &lt;code&gt;irmin-pack&lt;/code&gt; backend was of particular interest for MirageOS support. We have spent years optimising its performance since this backend is notably used to &lt;a href=&quot;https://tarides.com/blog/2022-04-26-lightning-fast-with-irmin-tezos-storage-is-6x-faster-with-1000-tps-surpassed/&quot;&gt;store the Tezos blockchain&lt;/a&gt;, and it enables the freeing of disk space by &lt;a href=&quot;https://tarides.com/blog/2023-05-05-optimising-archive-node-storage-for-tezos/&quot;&gt;truncating the database history to get rid of unnecessary old backups&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the low-level implementation of &lt;code&gt;irmin-pack&lt;/code&gt; is especially suited for a port to MirageOS, as it only requires support for a few large (append-only) files from the operating system.&lt;/p&gt;
&lt;p&gt;This last technical requirement was especially relevant to the SpaceOS use case, where the satellite needs to be able to store high-resolution pictures on disk. If our custom-made file system supported large files, then we would be able to support SpaceOS and enable the general-purpose use of Irmin for MirageOS unikernels. This realisation still left us with the task of developing the foundations of a file system, but even just the bare functionalities would satisfy our use cases.&lt;/p&gt;
&lt;h2&gt;Notafs is Born&lt;/h2&gt;
&lt;p&gt;We called this new file system ‘Notafs’, which, as the name suggests, is not a general-purpose file system due to its focus on handling a few large files. It is designed to handle a small number of large files for Mirage block devices. It can, however, be used to run the &lt;code&gt;irmin-pack&lt;/code&gt; backend, which gives users all the benefits of an Irmin filesystem for MirageOS. Together, running &lt;code&gt;irmin-pack&lt;/code&gt; on Notafs lifts the limitations of the latter, and supports many file names, is optimised for small and large files, and includes a git-like history with branching and merging &lt;a href=&quot;https://irmin.org/&quot;&gt;just to name a few features&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Navigating design restrictions is an excellent way to focus the efforts of a project. In our case, that meant directing them towards the correctness and performance of the few selected operations of &lt;code&gt;Notafs&lt;/code&gt;. We didn’t need to implement advanced filesystem operations since missing functionalities could be provided by &lt;code&gt;irmin-pack&lt;/code&gt;, including optimised management of many small files. As long as our underlying file system could provide fast operations on large files, the rest was taken care of!&lt;/p&gt;
&lt;p&gt;From a unikernel developer’s perspective, &lt;code&gt;Notafs&lt;/code&gt; provides an implementation of the &lt;a href=&quot;https://ocaml.org/p/mirage-kv/latest/doc/Mirage_kv/index.html&quot;&gt;&lt;code&gt;Mirage_kv&lt;/code&gt;&lt;/a&gt; interfaces. This is the standard API for filesystem usage on MirageOS, so any existing unikernel can use it without needing to change its application code. We also provide a &lt;a href=&quot;https://github.com/tarides/notafs?tab=readme-ov-file#notafs-cli&quot;&gt;command-line interface&lt;/a&gt; to simplify the formatting of disks and to allow for external inspection of file system contents of the unikernel. Check out this example of a &lt;a href=&quot;https://github.com/tarides/notafs/tree/master/unikernel-kv&quot;&gt;minimal setup of Notafs in MirageOS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While Notafs has restricted functionalities, it provides a nice developer experience and a useful alternative in the file system design space for MirageOS. We are especially proud of the safety guarantees that Notafs provides for your data stored on disk.&lt;/p&gt;
&lt;h2&gt;Using Irmin on Notafs&lt;/h2&gt;
&lt;p&gt;To enable users to run Irmin on MirageOS, we provide an implementation of the syscalls required by the &lt;code&gt;irmin-pack&lt;/code&gt; backend on top of Notafs. This was straightforward as Notafs provides the functionalities required to run &lt;code&gt;irmin-pack&lt;/code&gt;. Thanks to &lt;a href=&quot;https://dev.realworldocaml.org/functors.html&quot;&gt;OCaml functors&lt;/a&gt;, the &lt;code&gt;irmin-pack&lt;/code&gt; codebase was already structured for alternative syscall implementations.&lt;/p&gt;
&lt;p&gt;One OCaml subtlety we encountered was handling asynchronous I/O: MirageOS and Notafs use the &lt;code&gt;Lwt&lt;/code&gt; monad, while the Irmin syscalls expected a direct-style I/O implementation. This would have been an issue in the past, but we could bridge that gap using the &lt;a href=&quot;https://ocaml.org/manual/5.2/effects.html&quot;&gt;effect handlers that came with OCaml 5&lt;/a&gt;. Note that OCaml 5 support is still experimental on MirageOS at the time of writing!&lt;/p&gt;
&lt;p&gt;From a user perspective, using Irmin addresses the design limitations of Notafs (while keeping its desirable consistency properties) by adding efficient support for managing a large number of small files. Irmin is a much more general-purpose file system; on top of that, a user can build a unikernel application with many additional features. We’re hopeful that applications already using Irmin will consider this new bridge for targeting MirageOS unikernels.&lt;/p&gt;
&lt;h2&gt;Until Next Time!&lt;/h2&gt;
&lt;p&gt;Thank you for checking out the first part of this two-part series where we’ve introduced Notafs, the motivation behind the file system, the challenges, and its use cases. Look out for part two where we delve into the details behind Notafs’ design alongside benchmarks and visualisations of the file system in action.&lt;/p&gt;
&lt;p&gt;Connect with us online on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt; to stay updated on our latest projects. We also invite you to consult the &lt;a href=&quot;https://github.com/tarides/notafs&quot;&gt;open-source Notafs repository&lt;/a&gt; for more information or to test out the file system yourself!&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-11-27-irmin-on-mirageos-introducing-the-notafs-file-system</id><title type="text">Irmin on MirageOS: Introducing the Notafs File System</title><updated>2024-11-27T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.11.26.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#1&quot;&gt;OCaml 5.2.1 released&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#2&quot;&gt;smaws preview release, an AWS SDK for OCaml using eio&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#3&quot;&gt;ppx_deriving_ezjsonm&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#4&quot;&gt;FUN OCaml now has a YouTube Channel&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#5&quot;&gt;Terrateam's open source Ocaml repository&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#6&quot;&gt;OUPS december 2024&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#7&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.26.html#8&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.11.26.html</id><title type="text">OCaml Weekly News, 26 Nov 2024</title><updated>2024-11-26T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/Oy3lZl2kE-0?version=3" rel="alternate"/><content type="text">David Sancho Moreno's FUN OCaml 2024 talk recording!

Overview by David:


server-reason-react implements react-dom/server and some of React's internals in OCaml. Its purpose is to natively render HTML markup from the server for a Reason React application. This pushes the idea for universal code (sharing code between the browser and native) and this talk is the story of all of this, and what are the solutions we applied at Ahrefs.


server-reason-react: https://github.com/ml-in-barcelona/server-reason-react

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/Oy3lZl2kE-0?version=3</id><title type="text">Universal React in OCaml - David Sancho Moreno - FUN OCaml 2024</title><updated>2024-11-20T09:24:00-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/Qzf_ZB1TKLQ?version=3" rel="alternate"/><content type="text">Paul-Elliot Anglès d'Auriac's FUN OCaml 2024 talk recording!

Overview by Paul-Elliot:

This talk is a gentle introduction to the documenting part of the OCaml ecosystem. We will see how to use `odoc` to build nice documentation for your `dune`-based project, from a bare repository to a documentation with both API pages and documentation pages.

odoc: https://github.com/ocaml/odoc

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/Qzf_ZB1TKLQ?version=3</id><title type="text">Using odoc to Write Documentation - Paul-Elliot Anglès d'Auriac - FUN OCaml 2024</title><updated>2024-11-20T09:24:00-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/fgdB_9DcJj4?version=3" rel="alternate"/><content type="text">Sudha's FUN OCaml 2024 talk recording!

Overview by Sudha:

In a first, OCaml 5.0 shipped with native support for parallelism and concurrency. This was a multi-year effort by the Multicore team and the OCaml development team that culminated in OCaml 5.0. The most challenging aspect was designing a multicore-capable garbage collector that remains backwards compatible in terms of features, performance, and latency. This is described in the paper 'Retrofitting Parallelism onto OCaml,' which appeared in ICFP 2020. In this talk, we will explore the ideas presented in the paper through doodle illustrations and zines.


Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/fgdB_9DcJj4?version=3</id><title type="text">How the Multicore Garbage Collector works - Sudha Parimala - FUN OCaml 2024</title><updated>2024-11-20T09:24:00-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/g7Kl5mRDCDo?version=3" rel="alternate"/><content type="text">Hannes Mehnert's FUN OCaml 2024 talk recording!

Overview by Hannes:


OCaml is a great systems programming language. We use it since more than a decade to develop MirageOS unikernels: run OCaml as a virtual machine, no Linux kernel involved. Since OCaml is statically typed (and type safe), and memory safe, we use a single address space, and avoided a lot of complexity of general purpose operating systems. Security-wise this is excellent: less attack surface, fewer attack vectors. Also less resource-heavy than contemporary OS. The result are tiny unikernels (e.g. a firewall with ~3MB as the full virtual machine image) that only contain the code really needed. I'll present what MirageOS is today and where it is used, its future, and our learnings so far.

https://mirage.io

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/g7Kl5mRDCDo?version=3</id><title type="text">MirageOS - Developing Operating Systems in OCaml - Hannes Mehnert - FUN OCaml 2024</title><updated>2024-11-20T09:24:00-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/hTAvAKolWd8?version=3" rel="alternate"/><content type="text">Dmitriy Kovalenko's FUN OCaml 2024 talk recording!

Overview by Dmitriy:

I am the author of dmtrKovalenko/odiff which claims to be and it is the fastest in the world (on my banchmarks lol) implementation of the pixel-by-pixel image comparison library which is written in OCaml (and a little bit of C). This is a story about standing the project. Why not to write it in C or Rust? How do we tune the garbage collector to avoid major collections at all? All this and much more like the hidden superpower of unboxed floats and some challenges about naked pointers and upgrading to OCaml v5 in my talk!

odiff: https://github.com/dmtrKovalenko/odiff

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/hTAvAKolWd8?version=3</id><title type="text">The Story Behind the Fastest Image Comparison Library  - Dmitriy Kovalenko - FUN OCaml 2024</title><updated>2024-11-20T09:24:00-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/-XYO_ILJG2M?version=3" rel="alternate"/><content type="text">Eduardo Rafael's FUN OCaml 2024 talk recording!

Overview by Eduardo:


Traditionally GADT's are used for lightweight tasks as the code complexity increases quite. I will be arguing that this is mostly a lack of common &quot;design patterns&quot; and maybe some tools. The talk will go through describing what you can fundamentally do with GADT's, how you should think about them and a general technique to mimic dependent types in OCaml, Hopefully by the end, a show case of a proposal for the Michelson interpreter.


Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/-XYO_ILJG2M?version=3</id><title type="text">Easy GADTs by Repeating Yourself - Eduardo Rafael - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/1HlIHPa38gY?version=3" rel="alternate"/><content type="text">Dillon Mulroy's FUN OCaml 2024 talk recording!

Overview by Dillon:

In my decade-long journey as a software engineer, the most transformative experience came from an unexpected source: learning and using OCaml. I will share how this functional programming language reshaped my approach to problem-solving, coding practices, and overall mindset about programming. Beyond the technical skills, OCaml connected me with incredibly bright, kind, and talented mentors and peers who have become lifelong friends. This journey also paved the way for my success on Twitch, allowing me to share my passion and knowledge with a broader audience. Join me as I explore the personal and professional growth that OCaml facilitated and celebrate the community that made it possible.

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/1HlIHPa38gY?version=3</id><title type="text">Maybe OCaml Was the Friends We Made Along the Way - Dillon Mulroy - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/1J2XyHLb2J0?version=3" rel="alternate"/><content type="text">Lukasz Stafiniak's FUN OCaml 2024 talk recording!

Overview by Lukasz:


Using OCANNL, we will build a toy feed forward network, we will train it, visualize its outputs. We will take a peek at the actual computation generated at various levels of abstraction: tensor node assignments, optimized C-language-like programs, translations to actual C (for CPUs, or CUDA but it is still work-in-progress). Lastly, we will parallelize training across multiple devices.

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/1J2XyHLb2J0?version=3</id><title type="text">OCANNL, the `neural_nets_lib` - Lukasz Stafiniak - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/2ZswyN4aP2o?version=3" rel="alternate"/><content type="text">Michael Dales's FUN OCaml 2024 talk recording!

Overview by Michael:


Each year the demo community run Tiny Code Xmas - using fantasy consoles like TIC-80 and PICO-8 to teach people to do retro graphics effects and size coding. But I didn't want to learn size coding, I wanted to learn OCaml! So for my first OCaml project I built a small fantasy console engine using OCaml, and implemented both Tiny Code Xmas and most of Genuary (generate art January) in it. In this talk I'll show why demo coding suits functional programming, look at how I built my first OCaml library, and show some examples of generative art using it.


Tiny Code Xmas: https://tcc.lovebyte.party/

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/2ZswyN4aP2o?version=3</id><title type="text">Learning OCaml with Tiny Code Xmas - Michael Dales - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/34bceAuSRXE?version=3" rel="alternate"/><content type="text">Rizo Isrof's FunOCaml 2024 talk recording!

Overview by Rizo:

Signals and fine-grained reactivity are two popular concepts in the JavaScript world, thanks to their simplicity, ergonomics and excellent performance. What are signals? What is their type? How can we use them in OCaml? Let's build a reactive UI library from scratch to find out! From simple callbacks, layers are added to support batching, resource cleanup and `let` syntax for composing effects. DOM interactions are implemented to allow predictable, minimal and efficient UI updates. Finally, helix is revealed, a library based on the presented reactivity principles. I review some examples and discuss how helix is used to implement real-world applications at Hyper and Tarides.

Find speaker: https://github.com/rizo, https://x.com/rizo_isrof


Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/34bceAuSRXE?version=3</id><title type="text">Let Signals in OCaml - Rizo Isrof - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/3oCXT-ycHHs?version=3" rel="alternate"/><content type="text">Antonio Monteiro's FUN OCaml 2024 talk recording!


Overview by Antonio:

Melange is a new compiler for OCaml targeting JavaScript. Melange codebases integrate with the OCaml Platform: install from OPAM, build with Dune, preprocess with `ppxlib`, format with OCamlformat and publish with `dune-release`. Merlin, OCaml-LSP and `odoc` work out of the box. In this talk, I will tell you about the Melange success story and give a glimpse of what's coming in the near future.


Melange: https://melange.re

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/3oCXT-ycHHs?version=3</id><title type="text">A 'Melange' of Tooling Coming Together - Antonio Monteiro - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/6mxx2j1jmhE?version=3" rel="alternate"/><content type="text">Patrick Ferris's FUN OCaml 2024 talk recording!

Overview by Patrick:

We present the good and the bad of building a dataflow engine in OCaml. The engine underpins a complex ecological analysis of avoided deforestation projects in tropical moist rainforests. We will discuss: Onboarding experienced developers who are new to OCaml. - Building an operating system in OCaml to run Python/R code.Developing geospatial libraries and how this benefited from Outreachy internships and the compiler's backwards compatibility. Managing a transition from monadic, asynchronous libraries to direct-style code. This work is part of a multi-year collaboration between the departments of Computer Science, Ecology, Zoology and Geography at the University of Cambridge.


Full title: Building Incremental and Reproducible Data Pipelines for Tackling Climate Change

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/6mxx2j1jmhE?version=3</id><title type="text">Building Incremental and Reproducible Data Pipelines  - Patrick Ferris - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA" rel="alternate"/><id>https://www.youtube.com/feeds/videos.xml?channel_id=UC3TI-fmhJ_g3_n9fHaXGZKA</id><title type="text">FUN OCaml</title></source><link href="https://www.youtube.com/watch/7_bv3EvQANY?version=3" rel="alternate"/><content type="text">Leandro Ostera's FUN OCaml 2024 talk recording!

Overview by Leandro:


In this talk Leandro Ostera, the PM of OCaml Build System team at Tarides shows the future of developer experience in dune. He will present the new features that are being developed and the roadmap for the next releases.

Try the Dune Developer Preview: https://preview.dune.build

Connect with us
Website: https://fun-ocaml.com/
Twitter: https://x.com/FunOCaml
Bluesky: https://bsky.app/profile/fun-ocaml.com</content><id>https://www.youtube.com/watch/7_bv3EvQANY?version=3</id><title type="text">The Future of Dune - Leandro Ostera - FUN OCaml 2024</title><updated>2024-11-20T09:23:58-00:00</updated><author><uri>https://www.youtube.com/channel/UC3TI-fmhJ_g3_n9fHaXGZKA</uri><name>FUN OCaml</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-11-20-advanced-code-navigation-in-ocaml-lsp" rel="alternate"/><content type="html">&lt;p&gt;&lt;h2&gt;1. Introduction&lt;/h2&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;Language Server Protocol (LSP)&lt;/a&gt; defines a standardised protocol that facilitates communication between an editor (client) and a language server. It is developed by Microsoft and was designed to simplify the process of adding language support to different code editors by abstracting away the most common implementation details of the programming language.&lt;/p&gt;
&lt;p&gt;OCaml-LSP, which relies on Merlin's amazing engine, is an implementation of the LSP protocol for the OCaml programming language.&lt;/p&gt;
&lt;h2&gt;2. Code Navigation in LSP&lt;/h2&gt;
&lt;p&gt;When it comes to navigating through code, the LSP protocol provides four ways to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_declaration&quot;&gt;Goto Declaration Request&lt;/a&gt;: Resolves the declaration location of a symbol.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition&quot;&gt;Goto Definition Request&lt;/a&gt;: Resolves the definition location of a symbol.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_typeDefinition&quot;&gt;Goto Type Definition Request&lt;/a&gt;: Resolves the type definition location of a symbol.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation&quot;&gt;Goto Implementation Request&lt;/a&gt;: Resolves the implementation location of a symbol. Not supported in &lt;code&gt;ocaml-lsp&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these goto requests provide general navigation for most programming languages, it falls short when dealing with complex syntax such as OCaml's modules, functors, etc., and being able to navigate to specific sections.&lt;/p&gt;
&lt;h2&gt;3. Code Navigation in Merlin&lt;/h2&gt;
&lt;p&gt;Merlin offers a range of commands that allow precise code navigation, including the ability to jump to specific constructs like functions, module definitions, and match cases. As earlier seen, generalised navigation in LSP is insufficient for precise movements. To solve this, Merlin introduces a specialised &lt;a href=&quot;https://github.com/ocaml/merlin/blob/main/doc/dev/PROTOCOL.md#jump--target-string--position-position&quot;&gt;Jump command&lt;/a&gt; that allows users to jump to specific targets within their OCaml code.&lt;/p&gt;
&lt;p&gt;The key targets include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fun&lt;/code&gt;: Jumps to a function definition.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;let&lt;/code&gt;: Jumps to a let binding.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;module&lt;/code&gt;: Jumps to a module.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;module-type&lt;/code&gt;: Jumps to a module type definition.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match&lt;/code&gt;: Jumps to a match construct.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;match-next-case&lt;/code&gt;: Jumps to the next case in a match statement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match-prev-case&lt;/code&gt;: Jumps to the previous case in a match statement.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. Custom Requests in LSP&lt;/h2&gt;
&lt;p&gt;When standard LSP requests are insufficient, we have the possibility of writing custom requests to implement the functionality we want. The downside to this approach is that we lose native support in the various editors or clients and will also have to write the resulting client implementations for each custom request. Implementing precise navigation in OCaml-LSP using Merlin's Jump is a typical use for a custom request.&lt;/p&gt;
&lt;p&gt;For this implementation, our focus was on being able to use the requests already available on LSP and use them in non-typical, innovative ways. With this in mind, our solution works by using a &lt;code&gt;CodeAction&lt;/code&gt; in combination with a &lt;code&gt;ShowDocument&lt;/code&gt; request to move the cursor to our desired position.&lt;/p&gt;
&lt;h2&gt;5. Implementing Merlin's Jump in OCaml-LSP&lt;/h2&gt;
&lt;p&gt;Code action requests are used to execute commands for a given text document and range. These commands are typically used to change the state of a document, such as code fixes and beautifying or refactoring code. In general, we use code actions to perform quick edits in code. It's a bit unusual to think of code actions as a navigation tool, but with some smart workarounds, we can indeed use code actions to move through code.
Here's how the feature works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;a) Document Detection&lt;/strong&gt;: When a source file is opened in the editor, the OCaml-LSP server checks if it is an OCaml file supported by Merlin. If the document is incompatible, the &quot;Jump to Target&quot; functionality is not provided.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;b) Client Capability Check&lt;/strong&gt;: The LSP allows servers to query what features a client (the editor) supports. For &quot;Jump to Target&quot; to work, the server checks if the client supports the &lt;code&gt;ShowDocument&lt;/code&gt; capability, which is necessary to move the cursor to the correct location.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;c) Generating CodeActions&lt;/strong&gt;: The client begins by sending a request for all valid code actions:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Trace&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;06&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Sending&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-storage-type&quot;&gt;'textDocument&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;codeAction&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;71&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Params&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;textDocument&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;file:///.../test.ml&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;diagnostics&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-list&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;triggerKind&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server receives this request, and for each target (fun, let, match, module, etc.), it asynchronously queries Merlin's Jump command. It assembles the possible jumps into a list of code actions and returns this list back to the client.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Trace&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;06&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-storage-type&quot;&gt;'textDocument&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;codeAction&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;71&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;' &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; 7ms&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;file:///.../test.ml&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;ocamllsp/merlin-jump-to-target&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;Let jump&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;merlin-jump-let&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;Let jump&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given that code actions are a standard LSP functionality, we benefit from an already existing pool of client implementations, so the clients display the codeactions returned by the server without any coding or customisation.
Each CodeAction includes a:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Title&lt;/em&gt; (e.g., &quot;Jump to function&quot;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Command&lt;/em&gt; (ocamllsp/merlin-jump-to-target): A command that is executed when the code action is selected.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Kind&lt;/em&gt;: This parameter distinguishes various code actions and can be used to group similar code actions or differentiate them especially for use with keybindings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;d) Executing CodeAction Commands&lt;/strong&gt;: When a user clicks or selects a specific code action, the command associated with that code action is executed.
The client sends an &lt;code&gt;executeCommand&lt;/code&gt; request to the server.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Trace&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;06&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Sending&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-storage-type&quot;&gt;'workspace&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;executeCommand&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;73&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Params&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;ocamllsp/merlin-jump-to-target&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;file:///.../test.ml&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;executeCommand&lt;/code&gt; request triggers the server to ask the client to perform a &lt;code&gt;showDocument&lt;/code&gt; request using the document URI and location range received from the code action.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;ocaml-source&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Trace&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;06&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-storage-type&quot;&gt;'window&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;showDocument&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-keyword-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-capital-identifier&quot;&gt;Params&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;character&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-numeric-decimal-integer&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;takeFocus&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-constant-language-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-comma punctuation-separator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-keyword-other-ocaml punctuation-other-colon punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;file:///.../test.ml&lt;/span&gt;&lt;span class=&quot;ocaml-string-quoted-double&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ocaml-source&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both &lt;code&gt;executeCommand&lt;/code&gt; and &lt;code&gt;showDocument&lt;/code&gt; are standard LSP requests, meaning all clients that already have LSP support can automatically perform this jump.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;e) Error Handling&lt;/strong&gt;: In cases where Merlin fails to find a valid target (e.g., the target does not exist or the position is invalid), the server gracefully handles the error by omitting the specific code action for that particular target, ensuring a smooth user experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. Feature Demonstration&lt;/h2&gt;
&lt;p&gt;Below is a demonstration showing the various code actions which perform navigation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tarides.com/blog/images/merlin-jump~3HRsyh_aCuRziN8_3Uil4g.gif&quot; alt=&quot;Merlin Jump code actions&quot;&gt;&lt;/p&gt;
&lt;h2&gt;7. Conclusion&lt;/h2&gt;
&lt;p&gt;Set for release in early December 2024, Merlin's Jump functionality in OCaml-LSP brings precision navigation into the LSP world, enhancing OCaml code navigation capabilities across various editors. By using a combination of existing LSP requests, the feature is implemented in a way that is client-agnostic and fully compatible with LSP, ensuring a smooth and efficient user experience for developers working with OCaml.&lt;/p&gt;
&lt;p&gt;For developers working on large or complex OCaml projects, this precise navigation capability will significantly streamline workflows, enabling quick and direct navigation to relevant parts of their code.&lt;/p&gt;
&lt;p&gt;While implementing Merlin's Jump as a code action offers a broad client-agnostic solution, there are several drawbacks to this approach. First, it can clutter the code action lists, making them noisy for users who are used to a more streamlined selection. Since code actions are typically used for refactoring or fixing issues, adding navigation-related actions here is not an expected usage of code actions. Additionally, binding these navigation actions to shortcuts becomes harder, as code actions do not naturally lend themselves to precise key bindings for navigation.&lt;/p&gt;
&lt;p&gt;To overcome these limitations, one potential solution is to use a custom LSP request, which would allow navigation functionality without overloading the code action list. We have a an &lt;a href=&quot;https://github.com/ocaml/ocaml-lsp/pull/1374&quot;&gt;open PR&lt;/a&gt; on &lt;code&gt;ocaml-lsp&lt;/code&gt; repository which introduces this custom request. Alternatively, a client-side tool like &lt;code&gt;tree-sitter&lt;/code&gt; could be used to parse the code and generate the necessary jump targets directly in the client, offering a more flexible and efficient solution tailored to the user's editor setup.&lt;/p&gt;
&lt;h2&gt;8. Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument&quot;&gt;LSP Protocol - ShowDocument Request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_executeCommand&quot;&gt;LSP Protocol - ExecuteCommand Request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction&quot;&gt;LSP Protocol - CodeActions Request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/merlin/issues&quot;&gt;Issue Tacker - Merlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml-lsp/issues&quot;&gt;Issue Tacker - OCaml-LSP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><id>https://tarides.com/blog/2024-11-20-advanced-code-navigation-in-ocaml-lsp</id><title type="text">Advanced Code Navigation in OCaml-LSP</title><updated>2024-11-20T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.11.19.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#1&quot;&gt;Boulder Dash in OCaml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#2&quot;&gt;Jane Street OCaml extensions – now with developer tooling!&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#3&quot;&gt;opam 2.3.0 is out!&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#4&quot;&gt;Installing Developer Tools with Dune&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#5&quot;&gt;Dune Developer Preview Updates&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#6&quot;&gt;First release of cmdlang&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#7&quot;&gt;findlib-1.9.8&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#8&quot;&gt;Testo 0.1.0 - a new testing framework for OCaml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.19.html#9&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.11.19.html</id><title type="text">OCaml Weekly News, 19 Nov 2024</title><updated>2024-11-19T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-plugins/frama-clang.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-plugins/frama-clang.html</id><title type="text">frama-clang v0.0.17~beta for Frama-C 30.0~ Zinc</title><updated>2024-11-15T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-plugins/metacsl.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-plugins/metacsl.html</id><title type="text">MetAcsl v0.8~beta for Frama-C 30.0~ Zinc</title><updated>2024-11-15T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="https://ocamlpro.com/blog/feed" rel="alternate"/><id>https://ocamlpro.com/blog/feed</id><title type="text">OCamlPro</title></source><link href="https://ocamlpro.com/blog/2024_11_13_opam_2_3_0_releases" rel="alternate"/><content type="html">&lt;p&gt;&lt;em&gt;Feedback on this post is welcomed on &lt;a href=&quot;https://discuss.ocaml.org/t/ann-opam-2-3-0-is-out/15609&quot;&gt;Discuss&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As mentioned in &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/10/Opam-2-2-and-beyond&quot;&gt;our talk at the OCaml Workshop 2024&lt;/a&gt;,
we decided to switch to a time-based release cycle (every 6 months), starting with opam 2.3.&lt;/p&gt;
&lt;p&gt;As promised, we are very pleased to announce the release of opam 2.3.0, and encourage all users to upgrade. Please read on for installation and upgrade instructions.&lt;/p&gt;
&lt;h2&gt;Try it!&lt;/h2&gt;
&lt;p&gt;In case you plan a possible rollback, you may want to first backup your
&lt;code&gt;~/.opam&lt;/code&gt; or &lt;code&gt;$env:LOCALAPPDATAopam&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;The upgrade instructions are unchanged:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Either from binaries: run
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For Unix systems&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell-session&quot;&gt;$ bash -c &quot;sh &amp;lt;(curl -fsSL https://opam.ocaml.org/install.sh)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or from PowerShell for Windows systems&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell-session&quot;&gt;Invoke-Expression &quot;&amp;amp; { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) }&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or download manually from &lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0&quot;&gt;the Github &quot;Releases&quot; page&lt;/a&gt; to your PATH.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Or from source, manually: see the instructions in the &lt;a href=&quot;https://github.com/ocaml/opam/tree/2.3.0#compiling-this-repo&quot;&gt;README&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should then run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell-session&quot;&gt;opam init --reinit -ni
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Major breaking change: extra-files&lt;/h2&gt;
&lt;p&gt;When loading a repository, opam now ignores files in packages' &lt;code&gt;files/&lt;/code&gt; directories which aren't listed in the &lt;code&gt;extra-files&lt;/code&gt; field of the opam file.
This was done to simplify the opam specification where we hope the opam file to be the only thing that you have to look at when reading a package specification. It being optional to list all files in the &lt;code&gt;extra-files:&lt;/code&gt; field went against that principle. This change also reduces the surface area for potential file corruption as all extra-files must have checksums.&lt;/p&gt;
&lt;p&gt;This is a breaking change and means that if you are using the &lt;code&gt;files/&lt;/code&gt; directory without listing them in the &lt;code&gt;extra-files:&lt;/code&gt; field, you need to make sure that all files in that directory are included in the &lt;code&gt;extra-files&lt;/code&gt; field.
The resulting opam file remains compatible with all previous opam 2.x releases.&lt;/p&gt;
&lt;p&gt;If you have an opam repository, you should make sure all files are listed so every packages continues to work without any issue, which can be done automatically using the &lt;code&gt;opam admin update-extrafiles&lt;/code&gt; command.&lt;/p&gt;
&lt;h2&gt;Major changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Packages requiring an unsupported version of opam are now marked unavailable, instead of causing a repository error. This means an opam repository can now allow smoother upgrade in the future where some packages can require a newer version of opam without having to fork the repository to upgrade every package to that version as was done for the upgrade from opam 1.2 to 2.0&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a new &lt;code&gt;opam list --latests-only&lt;/code&gt; option to list only the latest versions of packages. Note that this option respects the order options were given on the command line. For example: &lt;code&gt;--available --latests-only&lt;/code&gt; will first list all the available packages, then choose only the latest packages in that set; while &lt;code&gt;--latests-only --available&lt;/code&gt; will first list all the latest packages, then only show the ones that are available in that set&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fix and improve &lt;code&gt;opam install --check&lt;/code&gt;, which now checks if the whole dependency tree of the package is installed instead of only the root dependencies&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a new &lt;code&gt;--verbose-on&lt;/code&gt; option to enable verbose output for specified package names. &lt;em&gt;Thanks to &lt;a href=&quot;https://github.com/desumn&quot;&gt;@desumn&lt;/a&gt; for this contribution&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a new &lt;code&gt;opam switch import --deps-only&lt;/code&gt; option to install only the dependencies of the root packages listed in the opam switch export file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;opam switch list-available&lt;/code&gt; no longer displays compilers flagged with &lt;code&gt;avoid-version&lt;/code&gt;/&lt;code&gt;deprecated&lt;/code&gt; unless &lt;code&gt;--all&lt;/code&gt; is given, meaning that pre-release or unreleased OCaml packages no longer appear to be the latest version&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;opam switch create --repositories&lt;/code&gt; now correctly infers &lt;code&gt;--kind=git&lt;/code&gt; for URLs ending with &lt;code&gt;.git&lt;/code&gt; rather than requiring the &lt;code&gt;git+https://&lt;/code&gt; protocol. This is consistant with other commands such as &lt;code&gt;opam repository add&lt;/code&gt;. &lt;em&gt;Thanks to &lt;a href=&quot;https://github.com/Keryan-dev&quot;&gt;@Keryan-dev&lt;/a&gt; for this contribution&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;opam switch set-invariant&lt;/code&gt; now displays the switch invariant using the same syntax as the &lt;code&gt;--formula&lt;/code&gt; flag&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;builtin-0install&lt;/code&gt; solver was improved and should now be capable of being your default solver instead of &lt;code&gt;builtin-mccs+glpk&lt;/code&gt;. It was previously mostly only suited for automated tasks such as Continuous Integration. If you wish to give it a try, simply calling &lt;code&gt;opam option solver=builtin-0install&lt;/code&gt; (call &lt;code&gt;opam option solver=&lt;/code&gt; restores the default)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most of the unhelpful conflict messages were fixed. (&lt;a href=&quot;https://github.com/ocaml/opam/issues/4373&quot;&gt;#4373&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fix an opam 2.1 regression where the initial pin of a local VCS directory would store untracked and ignored files.
Those files would usually be cleaned before building the package, however git submodules would not be cleaned and would cause issues when paired with the new behaviour added in 2.3.0~alpha1 which makes opam error when git submodules fail to update (it was previously a warning). (&lt;a href=&quot;https://github.com/ocaml/opam/issues/5809&quot;&gt;#5809&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fix the value of the &lt;code&gt;arch&lt;/code&gt; variable when the current OS is 32bit on a 64bit machine (e.g. Raspberry Pi OS). (&lt;a href=&quot;https://github.com/ocaml/opam/issues/5949&quot;&gt;#5949&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;opam now fails when git submodules fail to update instead of ignoring the error and just showing a warning&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;opam's libraries now compile with OCaml &amp;gt;= 5.0 on Windows&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fix the installed packages internal cache, which was storing the wrong version of the opam file after a build failure.
This could be triggered easily for users with custom repositories with non-populated extra-files. (&lt;a href=&quot;https://github.com/ocaml/opam/pull/6213&quot;&gt;#6213&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Several improvements to the pre-built release binaries were made:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Linux binaries are now built on Alpine 3.20
&lt;/li&gt;
&lt;li&gt;The FreeBSD binary is now built on FreeBSD 14.1
&lt;/li&gt;
&lt;li&gt;The OpenBSD binary is now built on OpenBSD 7.6 and loses support for OpenBSD 7.5 and earlier
&lt;/li&gt;
&lt;li&gt;Linux/riscv64 and NetBSD/x86_64 binaries are now available
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And many other general, performance and UI improvements were made and bugs were fixed.
You can take a look to previous blog posts.
API changes and a more detailed description of the changes are listed in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0-alpha1&quot;&gt;the release note for 2.3.0~alpha1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0-beta1&quot;&gt;the release note for 2.3.0~beta1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0-beta2&quot;&gt;the release note for 2.3.0~beta2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0-rc1&quot;&gt;the release note for 2.3.0~rc1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/opam/releases/tag/2.3.0&quot;&gt;the release note for 2.3.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This release also includes PRs improving the documentation and improving
and extending the tests.&lt;/p&gt;
&lt;p&gt;Please report any issues to &lt;a href=&quot;https://github.com/ocaml/opam/issues&quot;&gt;the bug-tracker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We hope you will enjoy the new features of opam 2.3!&lt;/p&gt;
</content><id>https://ocamlpro.com/blog/2024_11_13_opam_2_3_0_releases</id><title type="text">opam 2.3.0 release!</title><updated>2024-11-13T13:56:36-00:00</updated><author><name>
    Raja Boujbel - OCamlPro
  </name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-11-13-the-new-conference-on-the-block-what-is-fun-ocaml" rel="alternate"/><content type="html">&lt;p&gt;&lt;a href=&quot;https://fun-ocaml.com/#event&quot;&gt;FUN OCaml&lt;/a&gt; describes itself as a &quot;two day open-source hacking event dedicated to OCaml enthusiasts and developers around the globe&quot;. This past September, 70+ developers descended upon a coworking space in Berlin, Germany, to learn, socialise, and hack together. Some had travelled from as far away as Brazil and the United States to join the event in person, and some combined it with &lt;a href=&quot;https://reactalicante.es&quot;&gt;React Alicante&lt;/a&gt; and &lt;a href=&quot;https://icfp24.sigplan.org&quot;&gt;ICFP&lt;/a&gt; for a summer filled with functional programming.&lt;/p&gt;
&lt;p&gt;As a key sponsor - alongside &lt;a href=&quot;https://ahrefs.com&quot;&gt;Ahrefs&lt;/a&gt; and &lt;a href=&quot;https://www.janestreet.com&quot;&gt;Jane Street&lt;/a&gt; - Tarides is striving to make OCaml more accessible. I have had the pleasure of interviewing one of the head organisers Sabine Schmaltz about her experience at the conference, the aim of the event, and the future of FUN OCaml. Let's dive in!&lt;/p&gt;
&lt;h2&gt;Why FUN OCaml&lt;/h2&gt;
&lt;p&gt;FUN OCaml began as a small idea that grew thanks to great teamwork; as Sabine recalled, &quot;David Sancho from Ahrefs was thinking of organising an online conference and had already done a lot of work to plan the talks, etc. I reached out to explain that I was planning an in-person conference and asked whether we could join forces – and he immediately said yes!&quot; It's great to see collaboration across companies benefiting the community, and several additional volunteers would join the organising committee as the conference drew nearer.&lt;/p&gt;
&lt;p&gt;I was curious to know what motivated Sabine to organise the event, and she explained that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;I felt that OCaml was missing a bigger event that was targeted uniquely at developers. FUN OCaml gives them a space to teach each other things and build connections, bringing maintainers and contributors together. FUN OCaml is organised by developers for developers, covering things they are interested in on a peer-to-peer basis&quot;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;She was inspired by &lt;a href=&quot;https://zfoh.ch/zurihac2024/&quot;&gt;ZuriHac&lt;/a&gt;, a yearly Haskell event in Zurich that she has attended before. &quot;It has a great spirit; people go there to hack and hang out by the lake. It's a very social event with many different rooms and discussions&quot;. Inspired by ZuriHac, Sabine adopted an alternative approach to scheduling for FUN OCaml in comparison to traditional conferences.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;We didn't stop at 6 pm and send everyone away! We stayed open until midnight on the first day and at 10 pm on the second. I wanted to provide a space in the evenings for activities, whether OCaml-related or not. Letting people put faces to names and form relationships was important because it's crucial for collaboration&quot;.
The evenings were filled with board games, hacking, hanging out, and karaoke – creating a space for socialisation and mingling. Sabine pointed out that when conferences close after their programs end, people generally stick to groups of people they already know to hang out with in the evening, which prevents them from making new connections. Consequently, creating a social space for everyone was a high priority for the organisers and a core motivation behind the conference.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Talks, Workshops, and More!&lt;/h2&gt;
&lt;p&gt;So, what was a day at FUN OCaml like? Both days ran a track for talks and a track for workshops. The talks were live streamed and are [available online for you to watch]. Recording them meant that participants who went to a workshop could catch up on the ones they missed after the fact. The talks covered &lt;a href=&quot;https://mirage.io&quot;&gt;MirageOS&lt;/a&gt;, GADTs, &lt;a href=&quot;https://github.com/ocaml/odoc&quot;&gt;&lt;code&gt;odoc&lt;/code&gt;&lt;/a&gt;, type engineering, &lt;a href=&quot;https://github.com/ahrefs/ocannl&quot;&gt;OCANNL&lt;/a&gt;, and learning OCaml with Tiny Code Xmas, just to name a few! It was a great mix of more and less technical topics. Sabine recalled the software engineer and popular live streamer Dillon Mulroy's presentation: &quot;Dillon gave a great talk on his journey to OCaml, like a personal account of how he came to OCaml and the OCaml community. It was great to see a Typescript developer and public figure in the developer community talk about OCaml&quot;.&lt;/p&gt;
&lt;p&gt;Dillon commented on his time at FUN OCaml:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“FUN OCaml was my favorite conference I've ever attended. I finally got to meet so many friends and peers that I've met online over the past year or two and got so much value out of collaborating with them in person. I can't wait for next year 🐫”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The workshop track was a valuable addition that gave participants an alternative way of engaging with OCaml: &quot;People really enjoy the hands-on approach and the rare opportunity to hack on projects together with their maintainers&quot;. The workshops covered a range of topics such as a beginner's introduction to OCaml, building reliable actor systems, concurrency and parallelism, &lt;a href=&quot;https://mirage.io&quot;&gt;MirageOS&lt;/a&gt;, creating 2D games in OCaml, and web and mobile app development. &quot;I heard from the host of the game engine workshop, Emile, that people built many cool things, including a minesweeper game!&quot;&lt;/p&gt;
&lt;h2&gt;FUN OCaml 2025 and Running Your own OCaml Meet-up or Conference&lt;/h2&gt;
&lt;p&gt;To wrap up, I was curious about what Sabine had planned for the future, including whether there were any plans for another FUN OCaml. I am pleased to report that FUN OCaml is on track for 2025!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;This year, we had to schedule FUN OCaml for September to make it work with ICFP and REACT Alicante, but that meant it fell outside of the semester break for many students. Next time, we would like to schedule FUN OCaml between semesters so as many students as possible can come. They would benefit from hacking on open-source projects and getting feedback from maintainers. Software in the wild!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We want to see more OCaml events around the world! You can organise your own conference or meet-up to bring more OCaml to your neck of the woods. Sabine is happy to help any member of the OCaml community looking to create events, and you can contact &lt;a href=&quot;https://x.com/sabine_s_&quot;&gt;Sabine on X&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/sabine.sh&quot;&gt;on Bluesky&lt;/a&gt;, or &lt;a href=&quot;mailto:sabine(at)tarides.com&quot;&gt;by email&lt;/a&gt;.```&lt;/p&gt;
&lt;h2&gt;Until Next Time!&lt;/h2&gt;
&lt;p&gt;Connect with us online on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt;. We look forward to hearing from you and hope to see you around the OCaml universe!&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-11-13-the-new-conference-on-the-block-what-is-fun-ocaml</id><title type="text">The New Conference on the Block: What is FUN OCaml?</title><updated>2024-11-13T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.11.12.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#1&quot;&gt;Picos — Interoperable effects based concurrency&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#2&quot;&gt;findlib-1.9.7&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#3&quot;&gt;First release candidate for OCaml 5.2.1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#4&quot;&gt;mirage-swapfs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#5&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.12.html#6&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.11.12.html</id><title type="text">OCaml Weekly News, 12 Nov 2024</title><updated>2024-11-12T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="http://frama-c.com/rss.xml" rel="alternate"/><id>http://frama-c.com/rss.xml</id><title type="text">Frama-C</title></source><link href="https://frama-c.com/fc-versions/zinc.html" rel="alternate"/><content type="html"></content><id>https://frama-c.com/fc-versions/zinc.html</id><title type="text">Beta release of Frama-C 30.0~beta (Zinc)</title><updated>2024-11-07T00:00:00-00:00</updated><author><name>Frama-C</name></author></entry><entry><source><link href="https://ocamlpro.com/blog/feed" rel="alternate"/><id>https://ocamlpro.com/blog/feed</id><title type="text">OCamlPro</title></source><link href="https://ocamlpro.com/blog/2024_11_06_short_news_archeologie_de_la_genealogie" rel="alternate"/><content type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;&lt;div class=&quot;figure&quot;&gt;
  &lt;p&gt;
    &lt;a href=&quot;https://ocamlpro.com/blog/assets/img/figure_genealogy_tree.jpeg&quot;&gt;
      &lt;img src=&quot;https://ocamlpro.com/blog/assets/img/figure_genealogy_tree.jpeg&quot; alt=&quot;Un bonsaï sous sa cloche de verre. De nos jours, l'accès à la généalogie grand public est préservé surtout grâce à la maintenance de codes patrimoniaux.&quot;&gt;
    &lt;/a&gt;
    &lt;/p&gt;&lt;div class=&quot;caption&quot;&gt;
      Un bonsaï sous sa cloche de verre. De nos jours, l'accès à la généalogie grand public est préservé surtout grâce à la maintenance de codes patrimoniaux.
    &lt;/div&gt;
  &lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;L’équipe d’OCamlPro a récemment été sollicitée par &lt;a href=&quot;https://asso.roglo.eu/page/350795-accueil&quot;&gt;l’association Roglo&lt;/a&gt;, une
association française de généalogie qui gère une base de plus de 10 millions de
personnes connectées dans un même arbre généalogique, et dont la base
s'accroît d’environ 500 000 nouvelles contributions tous les ans. L’association
s’appuie sur le logiciel libre &lt;a href=&quot;https://geneweb.tuxfamily.org/wiki/GeneWeb/fr&quot;&gt;Geneweb&lt;/a&gt;, l’un des plus puissants logiciels
du domaine, créé en 1997 à l’Inria, permettant de partager sur le web des
arbres généalogiques, et utilisé aussi bien par des particuliers que par des
leaders du secteur, comme la société française &lt;a href=&quot;https://en.geneanet.org/legal/geneanet/&quot;&gt;Geneanet&lt;/a&gt;, acquise en 2021 par
l’Américain Ancestry.&lt;/p&gt;
&lt;p&gt;Notre mission s’est d’abord concentrée sur l'optimisation des performances,
pour ramener le traitement de certaines requêtes sur la base gargantuesque de
Roglo à des temps raisonnables. Après avoir rapidement survolé le code de plus
de 80 000 lignes et profilé les requêtes les plus coûteuses, nous avons pu
proposer une solution, l’implanter et l’intégrer dans la branche principale.
Pour l’une des requêtes, le temps passe ainsi de 77s à 4s, soit 18 fois plus
rapide ! Nous travaillons maintenant à enrichir Geneweb de nouvelles
fonctionnalités pour ces utilisateurs, mais aussi pour ses contributeurs et les
mainteneurs de la plateforme !&lt;/p&gt;
&lt;p&gt;Cette mission, fragmentée en sprints de développement, s'inscrit dans une
démarche continue visant à faire évoluer Geneweb pour qu'il puisse gérer des
volumes de données encore plus importants.
Nous sommes ravis de contribuer à cette évolution, en apportant notre expertise
en optimisation et en développement logiciel pour faire grandir cette
plateforme de référence.&lt;/p&gt;
</content><id>https://ocamlpro.com/blog/2024_11_06_short_news_archeologie_de_la_genealogie</id><title type="text">Optimisation de Geneweb, 1er logiciel français de Généalogie depuis près de 30 ans</title><updated>2024-11-06T16:50:25-00:00</updated><author><name>
    Fabrice Le Fessant
  </name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-11-06-making-ocaml-mainstream-support-our-open-source-work-on-github" rel="alternate"/><content type="html">&lt;p&gt;We are steadfast OCaml advocates, &lt;a href=&quot;https://tarides.com/blog/2024-06-19-keeping-up-with-the-compiler-how-we-help-maintain-the-ocaml-language/&quot;&gt;providing core maintenance&lt;/a&gt;, &lt;a href=&quot;https://tarides.com/blog/2024-05-01-we-host-our-first-ocaml-retreat-in-india/&quot;&gt;hosting community events&lt;/a&gt;, and &lt;a href=&quot;https://tarides.com/blog/2023-03-02-the-journey-to-ocaml-multicore-bringing-big-ideas-to-life/&quot;&gt;bringing groundbreaking new features to the language&lt;/a&gt;. We choose OCaml because it is a programming language with a unique combination of strengths, combining an expressive syntax and strong type system with memory safety and the power of multicore programming.&lt;/p&gt;
&lt;p&gt;OCaml’s success is the combined result of the innovative efforts, passion, and dedication of the people who contribute to its development. The open-source community behind OCaml works collaboratively and transparently to make OCaml faster, safer, and easier. This community is made up of individual developers, research groups, and companies all working together. Tarides is one of the companies that contribute extensively to OCaml, and several of our team members are core OCaml developers.&lt;/p&gt;
&lt;p&gt;You can support our open-source work by becoming a sponsor on our &lt;a href=&quot;https://github.com/sponsors/tarides&quot;&gt;GitHub page&lt;/a&gt;. Your contribution will have a direct impact on the OCaml ecosystem by helping us maintain, develop, and improve its libraries, features, and tools. Let’s show you what we’re working on and what your support will help us achieve for OCaml!&lt;/p&gt;
&lt;h2&gt;What Does Tarides do?&lt;/h2&gt;
&lt;p&gt;At Tarides, we’re passionate about making OCaml even more powerful and accessible for developers. Our work focuses on three key areas: Enhancements, maintenance, and accessibility. Your contribution will support our work in all these areas.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enhancing the Language: We bring new features to OCaml by resolving developer pain points and testing performance on different platforms. We want OCaml to be a competitive programming language with developer experience a high priority.&lt;/li&gt;
&lt;li&gt;Maintaining Core Tools and Libraries: We ensure that OCaml developers have a reliable foundation for their projects by keeping the tools and libraries they depend on up to date.&lt;/li&gt;
&lt;li&gt;Community Support and Outreach: We prioritise clear documentation and tutorials to improve accessibility and regularly organise events that allow community members to meet and exchange ideas.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What Projects Does Tarides Work on?&lt;/h2&gt;
&lt;p&gt;We work on a broad range of projects targeting different parts of the OCaml ecosystem: Compiler and language tools, development tools, community and infrastructure, and advanced projects.&lt;/p&gt;
&lt;h3&gt;Compiler and Language Tools&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml&quot;&gt;OCaml Compiler&lt;/a&gt;: Long-term maintenance of the compiler alongside several collaborators, as well as feature development and enhancements.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tarides.com/blog/2023-07-07-making-ocaml-5-succeed-for-developers-and-organisations/&quot;&gt;OCaml 5&lt;/a&gt;: We’re driving the OCaml 5 release with Multicore support and effect handlers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocsigen/js_of_ocaml&quot;&gt;Js_of_ocaml&lt;/a&gt; and &lt;a href=&quot;https://github.com/ocaml-wasm/wasm_of_ocaml&quot;&gt;wasm_of_ocaml&lt;/a&gt;: Run OCaml code in your browser!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Development Tools&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ocaml.org/docs/platform&quot;&gt;OCaml Platform&lt;/a&gt;: Core OCaml tools, ensuring availability and compatibility with new compiler releases.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ocamllabs.ocaml-platform&quot;&gt;VSCode extension&lt;/a&gt;: Support and development of the OCaml Platform VSCode editor extension.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ocaml.org/packages&quot;&gt;&lt;code&gt;opam&lt;/code&gt;&lt;/a&gt;: The OCaml package manager, its tools, and plugins.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/dune&quot;&gt;Dune&lt;/a&gt; and &lt;a href=&quot;https://preview.dune.build/&quot;&gt;Dune Developer Preview&lt;/a&gt;: The OCaml build system.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/merlin&quot;&gt;Merlin&lt;/a&gt; and &lt;a href=&quot;https://github.com/ocaml/ocaml-lsp&quot;&gt;OCaml-LSP&lt;/a&gt;: A modern IDE for OCaml.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/odoc&quot;&gt;&lt;code&gt;odoc&lt;/code&gt;&lt;/a&gt;: A documentation generator for OCaml.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml-ppx/ocamlformat&quot;&gt;OCamlFormat&lt;/a&gt;: Formatting for OCaml code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Community and Infrastructure&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://ocaml.org/&quot;&gt;OCaml.org&lt;/a&gt;: OCaml’s home on the web, the central knowledge base where the community can connect, access resources, and get the latest OCaml news.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/infrastructure&quot;&gt;OCaml Infrastructure&lt;/a&gt;: Maintenance of various parts of the &lt;a href=&quot;http://ocaml.org/&quot;&gt;OCaml.org&lt;/a&gt; and &lt;code&gt;opam&lt;/code&gt; infrastructures.&lt;/li&gt;
&lt;li&gt;OCaml Package Ecosystem: Maintenance of the growth and quality of the OCaml package ecosystem, including &lt;a href=&quot;https://github.com/ocaml/opam-repository&quot;&gt;opam-repository&lt;/a&gt; and related &lt;a href=&quot;https://github.com/ocurrent/opam-repo-ci&quot;&gt;opam-repo-ci&lt;/a&gt;, &lt;a href=&quot;https://github.com/ocurrent/ocaml-ci&quot;&gt;OCaml-CI&lt;/a&gt; as well as &lt;a href=&quot;https://github.com/ocurrent/opam-health-check&quot;&gt;opam-health-check&lt;/a&gt; for &lt;a href=&quot;https://tarides.com/check.ci.ocaml.org&quot;&gt;Linux&lt;/a&gt;, &lt;a href=&quot;https://freebsd.check.ci.dev/&quot;&gt;FreeBSD&lt;/a&gt; and &lt;a href=&quot;https://windows.check.ci.dev/&quot;&gt;Windows&lt;/a&gt; as public services.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Advanced Projects&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml-multicore/eio&quot;&gt;Eio&lt;/a&gt;: A modern, effect-based I/O library for OCaml designed to provide a high-level, structured concurrency model.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mirage.io/&quot;&gt;MirageOS&lt;/a&gt;: An operating system that constructs unikernels for secure, high-performance applications across various cloud computing and mobile platforms.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irmin.org/&quot;&gt;Irmin&lt;/a&gt;: Distributed data stores based on distributed version-control systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Contribute to the Future of OCaml Today!&lt;/h2&gt;
&lt;p&gt;By sponsoring us, you’ll support the maintenance of these essential OCaml tools and libraries and contribute to the growth of a diverse, dynamic community. Your contribution will have a direct impact on our projects and the OCaml ecosystem as a whole.&lt;/p&gt;
&lt;p&gt;Please reach out to us on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt;. We look forward to hearing from you!&lt;/p&gt;
</content><id>https://tarides.com/blog/2024-11-06-making-ocaml-mainstream-support-our-open-source-work-on-github</id><title type="text">Making OCaml Mainstream: Support Our Open Source Work on GitHub</title><updated>2024-11-06T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.11.05.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#1&quot;&gt;GPTar 1.0.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#2&quot;&gt;opam 2.3.0~rc1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#3&quot;&gt;Call for Contributions: BOB 2025 (Berlin, March 14 - Deadline Nov 15)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#4&quot;&gt;First beta release for OCaml 5.3.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#5&quot;&gt;dune 3.16&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.11.05.html#6&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.11.05.html</id><title type="text">OCaml Weekly News, 05 Nov 2024</title><updated>2024-11-05T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-10-30-making-crypto-safer-introducing-the-argos-project" rel="alternate"/><content type="html">&lt;p&gt;The world of cryptocurrency and internet transactions is constantly evolving, and the changing landscape of technologies that support this growing industry often means that legislation struggles to keep up, allowing cybercriminals to exploit loopholes and take advantage of the lack of oversight. Concerted effort across the sector is necessary to address this vulnerability.&lt;/p&gt;
&lt;p&gt;To that end, we are thrilled to announce the ARGOS project, which stands for &lt;em&gt;Analyse et Représentation des Graphes des Opérations Suspectes&lt;/em&gt; or the analysis and graphical representation of suspicious operations. Together with &lt;a href=&quot;https://www.functori.com&quot;&gt;Functori&lt;/a&gt;, the LMF lab at &lt;a href=&quot;http://www.universite-paris-saclay.fr/en&quot;&gt;Paris Saclay&lt;/a&gt;, and the Lip6 lab at &lt;a href=&quot;https://www.sorbonne-universite.fr/en&quot;&gt;Sorbonne University&lt;/a&gt;, Tarides is creating a platform for blockchain analysis. ARGOS will facilitate the monitoring and tracing of individual transactions on the blockchain, helping both nations enforce the law and organisations comply with regulation.&lt;/p&gt;
&lt;h2&gt;The Current Problem&lt;/h2&gt;
&lt;p&gt;The financial crimes perpetrated using crypto and the blockchain include money laundering, extortion, fraud, and funnelling funding for terrorist activities. Cybercriminals take advantage of the same traits that make crypto attractive to legitimate users: its decentralisation, dynamism, and versatility. Further exacerbating the problem, these same traits make it difficult for nations to legislate and enforce rules like they do for traditional financial institutions like banks.&lt;/p&gt;
&lt;p&gt;This is because, rather than employing the traditional methods of identification, authentication, and document verification by a central authority, the blockchain uses an 'authorisation by default' approach to validate the exchange itself. Even though existing safeguards exist, which make it possible for police and the judicial system to determine the individuals associated with a transaction, they are not enough to effectively tackle cybercrime. What is needed is a way to identify, analyse, and track suspicious transactions and their sources at scale.&lt;/p&gt;
&lt;h2&gt;The ARGOS Solution&lt;/h2&gt;
&lt;p&gt;Several proposed solutions to the situation exist, but many of them can't address the breadth and complexity of the challenge or/and are not designed for the European financial system. Existing regulations empower nations to pose specific requirements on cryptocurrency providers and traders, and in Europe, that includes &lt;a href=&quot;https://www.adan.eu/en/publication/the-french-regulatory-framework-for-markets-in-crypto-assets/&quot;&gt;PACTE&lt;/a&gt; for France. Regulations impose rules on how the blockchain interacts with traditional financial institutions and the obligations that providers have including authenticating clients, freezing accounts, and identifying cryptographic addresses.&lt;/p&gt;
&lt;p&gt;ARGOS will be a French solution custom-created to comply with and enforce French and European financial market regulations. It is designed to be &lt;strong&gt;lightweight&lt;/strong&gt; enough not to place an undue burden on the existing ecosystem of blockchain developers, cryptocurrency providers, and traders, &lt;strong&gt;efficient&lt;/strong&gt; enough to handle large codebases and complex systems, and &lt;strong&gt;robust&lt;/strong&gt; enough to catch criminal activity reliably.&lt;/p&gt;
&lt;h3&gt;What is ARGOS?&lt;/h3&gt;
&lt;p&gt;The success of ARGOS rests on the key characteristics of the project, which are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data Sovereignty:&lt;/strong&gt; ARGOS will be designed and developed in France in compliance with the best practices and regulations of the European market. This provides additional security and privacy guarantees for European users, who do not have to share sensitive information with non-European parties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; Being open-source is a fundamental aspect of the ARGOS project, providing the high levels of transparency required to facilitate collaboration across organisations. The project will be published under an open-source licence, ensuring the entire platform (including the source code, tests, etc.) is available to the community for validation and scrutiny. This transparency is important for users to feel confident in the platform and to encourage innovation in its ecosystem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Large-Scale Data Management:&lt;/strong&gt; We will use and adapt large-scale data management techniques to handle the big datasets characteristic of the blockchain sector. Our focus will be on efficiency and performance, and we will undertake R&amp;amp;D work to develop new solutions that suit our use case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suspicious-Pattern Detection:&lt;/strong&gt; We plan to implement data processing mechanisms like &lt;a href=&quot;https://www.sonarsource.com/blog/what-is-taint-analysis/#:~:text=Taint%20analysis%20identifies%20every%20source,you%20do%20anything%20with%20it.&quot;&gt;taint analysis&lt;/a&gt; and pattern detection to track the movement of digital assets. Machine learning should also greatly improve the quality of the results we obtain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In-Depth Analysis of Smart Contracts:&lt;/strong&gt; Our system will be able to analyse smart contracts and understand the paths of nested interactions to determine whether a series of operations is suspicious.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockchain Bridge Traceability:&lt;/strong&gt; Our solution will account for transactions across blockchain bridges, ensuring that ARGOS doesn't lose track of them and remains global (not limited to one blockchain).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customisable User Interface:&lt;/strong&gt; ARGOS will provide an intuitive and customisable user interface that can be adapted to suit each user's individual needs. It's important that our solution is accessible, easy to use, and integrates with existing tools. For example, a Digital Asset Service Provider (DASP) (so-called Prestataire de Service sur Actifs Numériques or PSAN in French) should be able to use it in combination with the monitoring and cryptographic tools they already employ with their own users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapted Analysis Reports:&lt;/strong&gt; ARGOS will offer several analytics report tools tailored to the specific needs of different users.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What Does the Future Hold?&lt;/h2&gt;
&lt;p&gt;The project is in its early stages, and we are collaborating closely with our partners to research and develop new approaches and technologies. Look out for more updates on the project coming in the future!&lt;/p&gt;
&lt;p&gt;Connect with us online on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt; to stay updated on our latest projects.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tarides champions open-source development. We create and maintain key features of the OCaml language in collaboration with the OCaml community. To learn more about how you can support our open-source work, discover our &lt;a href=&quot;https://github.com/sponsors/tarides&quot;&gt;page on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We are always happy to discuss commercial opportunities around OCaml. We provide core services, including training, tailor-made tools, and secure solutions. &lt;a href=&quot;https://tarides.com/contact/&quot;&gt;Contact us today&lt;/a&gt; to learn more about how Tarides can help your team.&lt;/p&gt;
&lt;/blockquote&gt;
</content><id>https://tarides.com/blog/2024-10-30-making-crypto-safer-introducing-the-argos-project</id><title type="text">Making Crypto Safer: Introducing the ARGOS Project</title><updated>2024-10-30T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.10.29.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#1&quot;&gt;HOL Light released to OPAM&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#2&quot;&gt;Could we add a tiny OCaml interpreter to Numworks graphical calculators?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#3&quot;&gt;opam 2.3.0~beta2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#4&quot;&gt;Editors dev-meeting #4, Thu. 31th: Search by type à la Sherlodoc 🕵️&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#5&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#6&quot;&gt;Shell Completions in Dune Developer Preview&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.29.html#7&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.10.29.html</id><title type="text">OCaml Weekly News, 29 Oct 2024</title><updated>2024-10-29T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/2024-10-29-ptt.html" rel="alternate"/><content type="html">&lt;p&gt;An update of our email stack&lt;/p&gt;
</content><id>https://blog.robur.coop/articles/2024-10-29-ptt.html</id><title type="text">Postes, télégraphes et téléphones, next steps</title><updated>2024-10-29T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/gptar-update.html" rel="alternate"/><content type="html">&lt;p&gt;libarchive vs hybrid GUID partition table and GNU tar volume header&lt;/p&gt;
</content><id>https://blog.robur.coop/articles/gptar-update.html</id><title type="text">GPTar (update)</title><updated>2024-10-28T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/dnsvizor01.html" rel="alternate"/><content type="html">&lt;pre&gt;&lt;code&gt;    The NGI-funded DNSvizor provides core network services on your network; DNS resolution and DHCP.
&lt;/code&gt;&lt;/pre&gt;
</content><id>https://blog.robur.coop/articles/dnsvizor01.html</id><title type="text">Meet DNSvizor: run your own DHCP and DNS MirageOS unikernel</title><updated>2024-10-25T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-10-23-looking-back-on-our-experience-at-icfp" rel="alternate"/><content type="html">&lt;p&gt;It has been a while since the biggest functional programming event of the year: &lt;a href=&quot;https://icfp24.sigplan.org/&quot;&gt;ICFP 2024&lt;/a&gt; in Milan, Italy. The annual conference, sponsored by &lt;a href=&quot;http://www.acm.org/&quot;&gt;ACM&lt;/a&gt; &lt;a href=&quot;https://www.sigplan.org/&quot;&gt;SIGPLAN&lt;/a&gt;, combines world-class talks with workshops on some of the biggest functional programming languages, including &lt;a href=&quot;https://en.wikipedia.org/wiki/ML_(programming_language)&quot;&gt;ML&lt;/a&gt;, &lt;a href=&quot;https://www.haskell.org/&quot;&gt;Haskell&lt;/a&gt;, &lt;a href=&quot;https://ocaml.org/&quot;&gt;OCaml&lt;/a&gt;, &lt;a href=&quot;https://www.scheme.org&quot;&gt;Scheme&lt;/a&gt; and &lt;a href=&quot;https://www.erlang.org/&quot;&gt;Erlang&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tarides are co-sponsors of ICFP, and several of our team members attend each year. This year, we had &lt;a href=&quot;https://tarides.com/blog/2024-08-30-the-biggest-functional-programming-conference-of-the-year-what-are-we-bringing-to-icfp/&quot;&gt;six talks given by Tarides team members&lt;/a&gt; and many of our colleagues chose to go as participants. I have asked several of them to share their experience at this year's ICFP to give you a taste of what the conference is all about.&lt;/p&gt;
&lt;h2&gt;The Tarides Talks&lt;/h2&gt;
&lt;p&gt;Before we get started, let's recap the talks created or co-created by Tarides engineers and where you can watch them after the fact.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/14/First-Class-Windows-Building-a-Roadmap-for-OCaml-on-Windows&quot;&gt;First-Class Windows: Building a Roadmap for OCaml on Windows&lt;/a&gt;:&lt;/strong&gt; A talk introducing the project seeking to make the developer experience with OCaml on Windows as good as it is on macOS and Linux. &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=wWzuSV9uJQkdbIq0&amp;amp;t=13527&quot;&gt;Watch the first-class Windows talk here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/10/Opam-2-2-and-beyond&quot;&gt;Opam 2.2 and Beyond&lt;/a&gt;:&lt;/strong&gt; Gives background to the recent &lt;code&gt;opam&lt;/code&gt; release from the perspective of its core maintainers.  &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=sre5ToVH0uuCPkUK&amp;amp;t=27421&quot;&gt;Watch the &lt;code&gt;opam&lt;/code&gt; 2.2 talk here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/5/Picos-Interoperable-effects-based-concurrency&quot;&gt;Picos – Interoperable Effects-Based Concurrency&lt;/a&gt;:&lt;/strong&gt; Covers the ongoing project to create an interface between effects-based schedulers and concurrency abstractions. &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=jUaZixua3xb6k7jz&amp;amp;t=20132&quot;&gt;Watch the Picos talk here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/3/Project-wide-occurrences-for-OCaml-a-progress-report&quot;&gt;Project-Wide Occurrences for OCaml, a Progress Report&lt;/a&gt;:&lt;/strong&gt; Describes the design behind the first iteration of improved search features available with editor tooling. &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=tCBfDNU4sBctXpv9&amp;amp;t=10943&quot;&gt;Watch the project-wide occurrences talk here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/12/Saturn-a-library-of-verified-concurrent-data-structures-for-OCaml-5&quot;&gt;Saturn: A Library of Verified Concurrent Data Structures for OCaml 5&lt;/a&gt;:&lt;/strong&gt; This talk covers Saturn, a new library of well-tested, benchmarked, partially verified concurrent data structures. &lt;a href=&quot;https://www.youtube.com/live/OuQqblCxJ2Y?si=4ztpRF2kl-9dcDs2&amp;amp;t=24397&quot;&gt;Watch the Saturn talk here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/mlworkshop-2024-papers/10/Wasm_of_ocaml&quot;&gt;Wasm_of_ocaml&lt;/a&gt;:&lt;/strong&gt; Presents the work done on the Js_of_ocaml fork which translates OCaml bytecode to Webassembly. &lt;a href=&quot;https://www.youtube.com/live/KLWiEf3x3kc?si=IssHoUK5TdvBXzbd&amp;amp;t=26981&quot;&gt;Watch the Wasm_of_OCaml talk here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Experience Reports&lt;/h2&gt;
&lt;p&gt;Curious about ICFP or missing the action already? I've asked several of my fellow Tarides team members who attended ICFP this year to share their thoughts and experiences. Let's dive in!&lt;/p&gt;
&lt;h3&gt;Why ICFP?&lt;/h3&gt;
&lt;p&gt;ICFP stands out to functional programming enthusiasts for gathering a large community of like-minded people in one place. As Jan Midtgaard puts it, &quot;it's a wonderful mix of academics and industry people gathering due to a common interest in functional programming&quot;. KC Sivaramakrishnan has been attending ICFP since 2009 when he was a PhD student, and &quot;ICFP is now the place where I meet friends and collaborators as well as the future generation of functional programming researchers&quot;.&lt;/p&gt;
&lt;p&gt;Another reason behind the conference's enduring popularity is, of course, the packed schedule full of high-quality talks. The different tracks offer a variety of opportunities for participants to explore topics that interest them. Ambre Suhamy comments, &quot;I know for sure that I will learn something new and come back from ICFP with more knowledge&quot;.&lt;/p&gt;
&lt;h3&gt;Stand-Out Talks&lt;/h3&gt;
&lt;p&gt;On the topic of talks, my colleagues attended several across different days and tracks. I asked them to share some of their impressions from the presentations, and while there were far too many to include them all, let's check out some of what they had to share!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/icfp-2024-papers/10/Functional-Programming-in-Financial-Markets-Experience-Report-&quot;&gt;ICFP Paper: Functional Programming in Financial Markets&lt;/a&gt; and &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/11/Recursion-schemes-in-OCaml-An-experience-report&quot;&gt;OCaml Workshop: Recursion schemes in OCaml&lt;/a&gt;: Tim McGilchrist really enjoyed these two talks, the first focussing on Standard Chartered Bank's use of typed functional programming (Haskell) across their entire tech stack, and the second on Bloomberg's use of OCaml modelling bilateral financial contracts. &quot;An interesting experience report for recursion schemes at Bloomberg. I'd previously only seen this implemented in Haskell!&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/icfp-2024-papers/19/Oxidizing-OCaml-with-Modal-Memory-Management&quot;&gt;ICFP Papers: Oxidising OCaml&lt;/a&gt;:  To KC Sivaramakrishnan, this was a &quot;technical highlight&quot;. The talk centred around the potential for optimising heap allocations in OCaml without compromising on safety guarantees. &quot;The modal types work aims to get the best of Rust into OCaml without letting go of what OCaml is good for – and the Rust-like features are opt-in!&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/10/Opam-2-2-and-beyond&quot;&gt;OCaml Workshop: Opam 2.2&lt;/a&gt;: Both Riku Silvola and Jan were impressed by this presentation on the new &lt;code&gt;opam 2.2&lt;/code&gt; release which notably brought native Windows support among other features. &quot;That Raja Boujbel from OCamlPro, Kate Deplaix from Ahrefs (and the OCaml Software Foundation), and David Allsopp from Tarides gave a joint &lt;code&gt;opam 2.2&lt;/code&gt; talk sent a wonderful signal that three OCaml companies have worked together to do good for the community&quot;, said Jan. Riku agreed, commenting &quot;David, Kate, and Raja presenting the long-awaited &lt;code&gt;opam 2.2&lt;/code&gt; release with its plethora of new features highlighted a longstanding and fruitful open-source collaboration across companies&quot;. They also announced &lt;code&gt;opam&lt;/code&gt;'s new time-based release cycle of updates every six months, check out the &lt;a href=&quot;https://opam.ocaml.org/blog/opam-2-3-0-alpha1/&quot;&gt;&lt;code&gt;opam 2.3.0&lt;/code&gt; blog post&lt;/a&gt; to learn more!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/mlworkshop-2024-papers/8/Pattern-matching-on-mutable-values-danger-&quot;&gt;ML Track: Pattern Matching on Mutable Values&lt;/a&gt;: This one stood out in Ambre's mind for its memorable demonstration of an obscure corner case where the pattern-matching compiler would generate incorrect code. &quot;It was insane; here's this five-line snippet of OCaml code; you can run it &lt;strong&gt;today&lt;/strong&gt;, and it segfaults!&quot; The original bug was found in 2016, and work has been ongoing to narrow down and fix the problem. Even though it was a rare edge case, the community rallied to address it, and a bug fix will be included in OCaml 5.3.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/3/Project-wide-occurrences-for-OCaml-a-progress-report&quot;&gt;OCaml Workshop: Project-Wide Occurrences&lt;/a&gt;: Riku highlighted the work presented by Ulysse Gérard, introducing a new feature of editor tooling that lets users query all occurrences of a selected value anywhere in their project. &quot;This work also highlights the important part the compiler plays in the overall platform vision, as the feature required work to be done not only in &lt;code&gt;ocaml-lsp-server&lt;/code&gt; and &lt;code&gt;merlin&lt;/code&gt; but also in &lt;code&gt;dune&lt;/code&gt; and the compiler&quot;, Riku said.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Memorable Moments&lt;/h3&gt;
&lt;p&gt;My colleagues' week in Milan created several lasting memories. Ambre chaired her first session, the 3rd OCaml Workshop session, which included presentations on &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/15/Priodomainslib-Prioritized-Fine-grained-Parallelism-for-Multicore-OCaml&quot;&gt;Priodomainslib&lt;/a&gt;, &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/12/Saturn-a-library-of-verified-concurrent-data-structures-for-OCaml-5&quot;&gt;Saturn&lt;/a&gt;, &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/5/Picos-Interoperable-effects-based-concurrency&quot;&gt;Picos&lt;/a&gt;, and &lt;a href=&quot;https://icfp24.sigplan.org/details/ocaml-2024-papers/9/Distributed-Actors-in-OCaml&quot;&gt;Distributed Actors&lt;/a&gt;. She also attended &lt;a href=&quot;https://icfp24.sigplan.org/home/farm-2024&quot;&gt;FARM&lt;/a&gt;, the international workshop on Functional Art, Music, Modelling, and Design. Ambre remembers the workshop as &quot;people using music to make programs, or programming to make music, and understanding music through programming&quot;.&lt;/p&gt;
&lt;p&gt;Tim recalls his many conversations between events with a variety of people in the hallways. It might not be the first thing that springs to mind, but the conversations that strike up organically at ICFP can be enlightening. For example, &quot;people generally didn't know that you could use GBD/LLDB on OCaml binaries and, once they knew that, they were very excited about using those tools on their OCaml programs&quot;.&lt;/p&gt;
&lt;p&gt;KC is focussed on the future, highlighting the feedback he received from participants and how it will help Tarides going forward. &quot;I got some really nice feedback from folks, building on OCaml, about the work that Tarides is doing. I also got lots of honest feedback on what's not working. At the end of the day, our user community matters, and we need to solve their pain points so that OCaml becomes an advantage and not a technical risk for their engineering teams.&quot; He also wants to keep contributing to the future of ICFP: &quot;I'm on the ICFP steering committee and will be a DEI co-chair for the next ICFP. I want to ensure that ICFP remains a thriving, friendly, and inclusive place for all of our attendees&quot;.&lt;/p&gt;
&lt;p&gt;Finally, we can't discuss a Milan conference without mentioning the food! All the participants enjoyed sampling the local Italian fare and just look at this delicious pizza!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tarides.com/blog/images/pizza-square-1360w~jhmPm2AA1oR5wR0bWzoHUA.webp&quot; sizes=&quot;(min-width: 1360px) 1360px, (min-width: 680px) 680px, 100vw&quot; srcset=&quot;/blog/images/pizza-square-170w~obqFAzpvi1Cmkw_xZsTP6w.webp 170w, /blog/images/pizza-square-340w~na6j04mV77cnis2HB2qVgg.webp 340w, /blog/images/pizza-square-680w~tDFhzaFXllfippFscfYclg.webp 680w, /blog/images/pizza-square-1360w~jhmPm2AA1oR5wR0bWzoHUA.webp 1360w&quot; alt=&quot;An Italian-style pizza with a whole piece of mozzarella cheese in the middle, surrounded by sliced meat.&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Join us Next Year!&lt;/h2&gt;
&lt;p&gt;The good news is that ICFP happens every year, so if you didn't attend this year, you can always set your sights on next year. The conference also moves around, alternating locations to make it easier for participants around the globe to join. We look forward to seeing you at &lt;a href=&quot;https://icfp25.sigplan.org/&quot;&gt;ICFP 2025 in Singapore&lt;/a&gt;, so come find us if you're going!&lt;/p&gt;
&lt;p&gt;You can reach out to us on &lt;a href=&quot;https://twitter.com/tarides_&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@tarides&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://www.threads.net/@taridesltd&quot;&gt;Threads&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/company/tarides&quot;&gt;LinkedIn&lt;/a&gt;. Stay in touch!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tarides champions open-source development. We create and maintain key features of the OCaml language in collaboration with the OCaml community. To learn more about how you can support our open-source work, discover our &lt;a href=&quot;https://github.com/sponsors/tarides&quot;&gt;page on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We are always happy to discuss commercial opportunities around OCaml. We provide core services, including training, tailor-made tools, and secure solutions. &lt;a href=&quot;https://tarides.com/contact/&quot;&gt;Contact us today&lt;/a&gt; to learn more about how Tarides can help your teams realise their vision.&lt;/p&gt;
&lt;/blockquote&gt;
</content><id>https://tarides.com/blog/2024-10-23-looking-back-on-our-experience-at-icfp</id><title type="text">Looking Back on our Experience at ICFP!</title><updated>2024-10-23T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.10.22.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#1&quot;&gt;opam 2.3.0~beta1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#2&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#3&quot;&gt;Wildcard expansion on Windows&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#4&quot;&gt;OCamlformat and GitHub actions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#5&quot;&gt;New vs. Old OCaml Academic Users Page Survey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#6&quot;&gt;New vs. Old OCaml Industrial Users Page&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#7&quot;&gt;Eliom 11 and Ocsigen Start 7&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.22.html#8&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.10.22.html</id><title type="text">OCaml Weekly News, 22 Oct 2024</title><updated>2024-10-22T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/arguments.html" rel="alternate"/><content type="html">&lt;p&gt;The history of runtime arguments to a MirageOS unikernel&lt;/p&gt;
</content><id>https://blog.robur.coop/articles/arguments.html</id><title type="text">Runtime arguments in MirageOS</title><updated>2024-10-22T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://blog.robur.coop/feed.xml" rel="alternate"/><id>https://blog.robur.coop/feed.xml</id><title type="text">Robur Cooperative</title></source><link href="https://blog.robur.coop/articles/finances.html" rel="alternate"/><content type="html">&lt;p&gt;How we organise as a collective, and why we're doing that.&lt;/p&gt;
</content><id>https://blog.robur.coop/articles/finances.html</id><title type="text">How has robur financially been doing since 2018?</title><updated>2024-10-21T00:00:00-00:00</updated><author><name>Robur Cooperative</name></author></entry><entry><source><link href="https://tarides.com/feed.xml" rel="alternate"/><id>https://tarides.com/feed.xml</id><title type="text">Tarides</title></source><link href="https://tarides.com/blog/2024-10-16-dune-developer-preview-installing-the-ocaml-compiler-with-dune-package-management" rel="alternate"/><content type="html">&lt;p&gt;We’re excited to share a significant update to Dune's Package Management system, particularly one that will be of great interest to OCaml developers. For those who have been exploring Dune’s experimental package management capabilities over the past six months, you’ll be pleased to know that we've recently merged a feature allowing OCaml compiler packages to be installed directly through Dune.&lt;/p&gt;
&lt;p&gt;Until now, Dune’s Package Management has supported the installation of various packages from the opam repository. However, it lacked the ability to install a functioning OCaml compiler — a crucial component for most Dune projects. This limitation meant that early adopters of Dune’s Package Management had to rely on external tools to manage their OCaml compiler installations, which wasn’t ideal. The good news is that this obstacle has been removed, making Dune Package Management far more robust and ready for testing by early adopters.&lt;/p&gt;
&lt;p&gt;The challenge we faced in integrating the OCaml compiler installation stemmed from a conflict between the compiler’s build system and the way Dune handles package builds. Dune builds packages in what’s known as a “sandbox” — a temporary directory where a package is initially constructed and installed. Once the build is complete, the package's installed components are moved to their final destination within Dune’s build directory. However, the OCaml compiler assumes that its installation location will be its permanent home. Moving the installed files after installation caused the compiler to malfunction, making this an intractable problem for Dune’s package management.&lt;/p&gt;
&lt;p&gt;While work is underway to make the OCaml compiler more flexible in terms of its installation location, we didn’t want to delay Dune’s package management features until this work was completed. Instead, we’ve introduced a workaround that allows compiler packages to be installed in a way that maintains their functionality.&lt;/p&gt;
&lt;p&gt;The solution involves installing OCaml compiler packages to a global, per-user directory, rather than within the project’s sandbox. By default, the compiler is installed in a directory such as &lt;code&gt;~/.cache/dune/toolchains/ocaml-base-compiler.&amp;lt;version&amp;gt;-&amp;lt;hash&amp;gt;&lt;/code&gt;. This ensures that the compiler remains in its expected location and operates correctly without the need for relocation.&lt;/p&gt;
&lt;p&gt;Moreover, this approach offers an added benefit: compilers installed through Dune can be shared across multiple projects. If two projects use the same version of the compiler, the installation step can be skipped in the second project, significantly speeding up the build process. Given that installing an OCaml compiler can take several minutes, this optimisation will save developers considerable time.&lt;/p&gt;
&lt;p&gt;In summary, this new feature represents a substantial improvement to Dune’s Package Management system, making it easier and faster for developers to set up and manage their OCaml projects. By enabling direct installation of OCaml compilers through Dune, we’re removing a major barrier to adoption and enhancing the overall development experience.&lt;/p&gt;
&lt;p&gt;Since it works transparently whenever you build a project with the Developer Preview, we'd love for you to test it out! Try out the new &lt;a href=&quot;https://tarides.com/blog/2024-10-03-introducing-the-dune-developer-preview-a-new-era-for-ocaml-development/&quot;&gt;Dune Developer Preview&lt;/a&gt; today, and let us know how it goes on &lt;a href=&quot;https://discuss.ocaml.org/&quot;&gt;Discuss&lt;/a&gt;. We’re eager to see how the community leverages this new capability and look forward to your feedback as we continue to refine and expand Dune’s Package Management features.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tarides is an open-source company with a strong focus on building and supporting the OCaml community. We're equally committed to the ongoing &lt;a href=&quot;https://github.com/sponsors/tarides&quot;&gt;development of the OCaml language&lt;/a&gt;, collaborating with industry partners and engineers to enhance its performance and features.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We invite you to join the OCaml community, explore the language and tools, and contribute to its evolution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Tarides is also open to discussing commercial opportunities related to OCaml. We offer services such as training, support, and custom development to help businesses adopt OCaml 5 efficiently. Please &lt;a href=&quot;https://tarides.com/contact/&quot;&gt;reach out to us&lt;/a&gt; to explore how we can assist with your needs.&lt;/p&gt;
&lt;/blockquote&gt;
</content><id>https://tarides.com/blog/2024-10-16-dune-developer-preview-installing-the-ocaml-compiler-with-dune-package-management</id><title type="text">Dune Developer Preview: Installing The OCaml Compiler With Dune Package Management</title><updated>2024-10-16T00:00:00-00:00</updated><author><name>Tarides</name></author></entry><entry><source><link href="https://alan.petitepomme.net/cwn/cwn.rss" rel="alternate"/><id>https://alan.petitepomme.net/cwn/cwn.rss</id><title type="text">Caml Weekly News</title></source><link href="https://alan.petitepomme.net/cwn/2024.10.15.html" rel="alternate"/><content type="html">&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#1&quot;&gt;grep_cmt: structural search of OCaml code&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#2&quot;&gt;Mutaml 0.1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#3&quot;&gt;ocaml-activitypub&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#4&quot;&gt;Ortac/QCheck-STM 0.4.0 Dynamic formal verification beyond one system under test&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#5&quot;&gt;Openbsd 1.0&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#6&quot;&gt;Compsort - reorder files in archives for improved compression&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#7&quot;&gt;Dune dev meeting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://alan.petitepomme.net/cwn/2024.10.15.html#8&quot;&gt;Other OCaml News&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
</content><id>https://alan.petitepomme.net/cwn/2024.10.15.html</id><title type="text">OCaml Weekly News, 15 Oct 2024</title><updated>2024-10-15T12:00:00-00:00</updated><author><name>Caml Weekly News</name></author></entry></feed>
