<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://lpil.uk//feed.xml" rel="self" type="application/atom+xml" /><link href="https://lpil.uk//" rel="alternate" type="text/html" /><updated>2026-07-29T22:27:32+00:00</updated><id>https://lpil.uk//feed.xml</id><title type="html">lpil</title><subtitle>Code monkey monorepo trinkets</subtitle><entry><title type="html">Studying Gaeilge with Anki</title><link href="https://lpil.uk//blog/studying-gaeilge-with-anki/" rel="alternate" type="text/html" title="Studying Gaeilge with Anki" /><published>2026-02-04T00:00:00+00:00</published><updated>2026-02-04T00:00:00+00:00</updated><id>https://lpil.uk//blog/studying-gaeilge-with-anki</id><content type="html" xml:base="https://lpil.uk//blog/studying-gaeilge-with-anki/"><![CDATA[<p>Fáilte!</p>

<p>Recently I’ve been taking <a href="https://en.wikipedia.org/wiki/Irish_language">Gaeilge</a>
lessons at <a href="https://www.citylit.ac.uk/">City Lit</a>. It’s wonderful, but boy it
is hard to remember everything.</p>

<p>One thing I’ve found fantastically useful is the app <a href="https://apps.ankiweb.net/">Anki</a>.
It’s a spaced-repetition flash-card app, similar to Duolingo, except you can
make your own cards about anything you like, and it’s not trying to squeeze
money out of you with a subscription. It works on both computers and on mobile.
To use the iPhone version you have to buy it first, but all the other versions
are completely free.</p>

<p>The app doesn’t come with any built-in content, you have to add that yourself.
After each lesson at City Lit I’ve been making my own Anki cards from my notes,
and using these to revise. Once I have a little free time I’ll clean them up
and publish them for others to use too.</p>

<p>Anki also has an <a href="https://ankiweb.net/shared/decks">online library</a> of decks
made and kindly shared by other people, again free-of-charge. If you create an
account and log-in you can download them and add them to your app. There’s
decks for all manner of topics. Here’s some of my favourite Gaeilge decks that
I have tried:</p>

<ul>
  <li><a href="https://ankiweb.net/shared/info/1905813620">Irish 1k</a>, based on the classic
soap opera Ros na Rún! It has audio for (almost) all the words, which is very
helpful.</li>
  <li><a href="https://ankiweb.net/shared/info/455229124">Na hÉin (Birds in Irish/As Gaeilge)</a>,
with which I’m learning English names for birds too.</li>
  <li><a href="https://ankiweb.net/shared/info/1698744689">Na Plandaí (Plants in Irish/As Gaeilge)</a>,
which is the same but for plants.</li>
  <li><a href="https://ankiweb.net/shared/info/2121823779">Irish sentences bank (Pota Focal House Glossary)</a>.</li>
  <li><a href="https://ankiweb.net/shared/info/786132817">Irish CEFR B1 Phrases, Vocabulary, and Pronunciation</a>,
which has audio, so long as you have an internet connection. It also has a
particularly egregious lime green background, for some reason.</li>
</ul>

<p>My favourite is Irish 1k, for the audio pronunciation.</p>

<h2 id="tips">Tips</h2>

<p>I like to add cards on my laptop and study on my phone, typically while on the
train or such. If you’re logged in on multiple devices it’ll keep your notes
and your progress in sync between them.</p>

<p>You can change how many never-before-seen cards it adds to your to-view pile
each day. If it’s too easy or too hard you can turn this number up or down. If
you’ve got totally overwhelmed by the workload you can “reset” cards to take
them out pile altogether. For the deck I’ve made with my revision notes I keep
the number very high, while for the decks from the internet I keep the number
very low, and try to aim for 80-90% accuracy (which is visible in the “stats”
tab of the app).</p>

<p>In Anki’s settings you can enable the “FSRS algorithm”. I don’t really
understand how it works, but it changes how Anki decides how often to show you
each card, supposedly to improve memorisation. It was recommended to me by
someone from the Anki community online, and it does seem to help.</p>

<p>Ádh mór!</p>]]></content><author><name></name></author><category term="Gaeilge" /><summary type="html"><![CDATA[Fáilte!]]></summary></entry><entry><title type="html">What I use, 2024 edition</title><link href="https://lpil.uk//blog/what-i-use-2024/" rel="alternate" type="text/html" title="What I use, 2024 edition" /><published>2024-12-28T00:00:00+00:00</published><updated>2024-12-28T00:00:00+00:00</updated><id>https://lpil.uk//blog/what-i-use-2024</id><content type="html" xml:base="https://lpil.uk//blog/what-i-use-2024/"><![CDATA[<p>Hi, I’m Louis! I’m the <a href="https://gleam.run">the Gleam programming language</a> guy.</p>

<p>This is my nostalgic record of the tech stuff I’ve been using over the last
year. Check out <a href="/blog/what-i-use-2023">2023’s post</a> to see where I started the
year. Alright, thumbs up, let’s do this.</p>

<h2 id="my-workstation">My workstation</h2>

<p>I’m still using the 2020 Macbook Air with 16GB RAM, hostnamed “Diglett”. It’s
doing me well, though I’m somewhat hitting the limits of what it can handle.
Streaming to Youtube and Twitch and also trying to compile Rust makes it
overheat and throttle the CPU, at which point everything slows down a ton. My
solution has largely been to not do Rust streams. Ah well.</p>

<p>I’ve upgraded my 1440p monitor to a 27 inch 5K LG display that I got a good deal
on second hand. It has some issues with temporary image burn-in (is that the
right term?) and a couple dead pixels, but it works and I enjoy the extra screen
space, and it’s nice to give other people’s old electronics a new life that
isn’t landfill.</p>

<p>Hey, so you know how Apple sells 5K displays? MacOS must have great 5K display
support then! Scaling the UI appropriately rather than making everything
unusably small, right? Yeah, turns out MacOS is <em>terrible</em> with 5K displays. Far
worse than Windows or Linux. I had an absolutely miserable day fruitlessly
trying to find a solution and thinking I’d wasted my money. In the end <a href="https://x.com/TheITGeekCom">Magic
Mike Marshall</a> came to the rescue and recommended
<a href="https://github.com/waydabber/BetterDisplay">BetterDisplay</a>. Thank you Mike!</p>

<p>I’m honestly stunned that MacOS is so bad at this. It’s supposed to be the “just
works” operating system that puts loads of work and polish into their UI, and
they’re selling 5K displays for it!</p>

<p>Oh, and I bought a pair of AirPods Pro 2, mostly motivated by their transparency
mode where you can still hear the outside world wearing them. They’re a lot
better than I expected, I’m rather fond of them now, and I use them for pretty
much everything. Except the microphone, the microphone is pants.</p>

<p>I’ve continued to enjoy Neovim, but I’m growing irritated with the Lazyvim
distribution. The maintainers have a different idea about backwards
compatibility than I do, and multiple times in recent months I’ve turned on my
computer to discover that they’ve radically changed something and completely
broken my workflow. I’m in the process of making my own custom setup based off
of <a href="https://github.com/nvim-lua/kickstart.nvim">kickstart.nvim</a> and <a href="https://github.com/echasnovski/mini.nvim">mini.nvim</a>,
but I’ve not found the time to figure out all the details. I’ve stubbornly been
using kickstart as inspiration and writing my own config, but realistically I
should probably just clone kickstart and use that as the start-point.</p>

<p>Oh, and I switched from Iterm2 to <a href="https://github.com/ghostty-org/ghostty">Ghostty</a>
after getting irritated with the silly AI stuff being added. I’d felt Iterm2 was
a bit bloated and rubbish, so it was enough to prompt me to try alternatively.
Ghostty is very nice! I like that it’s cross platform, so I can use it on Linux
etc too.</p>

<h2 id="homelab">Homelab</h2>

<p>Last year I said I wanted to try replacing Google Photos with something else,
and this year Google forced by hand by adding a bunch of adverts and constantly
nagging me to buy more storage from them.</p>

<p>Side note: Irritation really does seem to be the most powerful motivator to me.
I’ve grown less and less happy with how companies and such will just decide to
change something that makes my life worse in some small irritating way. It may
be that you get less polish making your own tools or using open source ones,
but it’s predictable, you don’t need to worry about suits making changes
motivated by their annual performance review, and you get that nice IKEA effect
fuzzy feeling.</p>

<p>Anyway- I went for <a href="https://github.com/immich-app/immich">Immich</a> which is an
unabashed open source Google Photos clone. I really like it! The user experience
is very slick and easily competes with products from large companies.</p>

<p>It’s engineered like some sort of enterprise product though, being a collection
of microservices. I’ve no idea why, given it’s intended mostly for self-hosting.
Luckily some folks made an <a href="https://github.com/imagegenius/docker-immich/">all-in-one container image</a>
that I could deploy instead of managing multiple different ones, so I can
mostly ignore the internal architecture now.</p>

<p>Compared to anything I was hosting previously it’s quite resource hungry. Idle
it uses about a gig of memory between all the services and the two databases
(why does a self-hosted application need two databases?), so my Raspberry Pi
felt a bit underpowered. It especially wasn’t happy at all when running computer vision
and transcoding newly uploaded media. I found a used 2021 N5105 Mini PC with 16GB RAM
and 1.5 TB of storage in Cash Converters for a good price, so I’ve made that my
new home server.</p>

<p>I decided to be bold and use not-Debian for the first time in a million years,
and after a lot of shopping around I opted for OpenSUSE MicroOS, an immutable
Linux distro with a transactional package management system. This means that to
make changes it takes a btrfs snapshot and installs the updates into that
snapshot instead of the live system, and then that gets used after the machine
reboots. I love this design! No more do you have skew between the intended state
of the system and the actual one, and if it doesn’t work you can rollback to the
old one. I’ve got it updating and rebooting early Sunday morning, as I thought
the default of daily wasn’t what I needed.</p>

<p>As for deploying software to the server, I decided to use containers this time,
as I like how they keep everything nicely wrapped up and I don’t need to worry
installing language runtimes and their dependencies. I was reluctantly
considering using something like docker-compose, but then I discovered <a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html">Quadlet,
Podman’s systemd integration</a>.
I love it! It’s one of the nicest little bits of tech I’ve used in ages.</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">Gleam Developer Survey container</span>
<span class="py">After</span><span class="p">=</span><span class="s">local-fs.target</span>

<span class="nn">[Container]</span>
<span class="py">Image</span><span class="p">=</span><span class="s">ghcr.io/gleam-lang/developer-survey:1.13.0</span>
<span class="py">Volume</span><span class="p">=</span><span class="s">/mnt/data/gleam-developer-survey:/app/data:rw,z</span>
<span class="py">PublishPort</span><span class="p">=</span><span class="s">8000:8000</span>

<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target default.target</span>
</code></pre></div></div>

<p>Pop that in <code class="language-plaintext highlighter-rouge">/etc/containers/systemd/gleam-developer-survey.container</code>, run
<code class="language-plaintext highlighter-rouge">systemctl daemon-reload</code>, and then you have a container as a systemd unit that
works with <code class="language-plaintext highlighter-rouge">systemctl start</code>, etc. Delightful!</p>

<h3 id="the-future">The Future</h3>

<p>The next steps for me are to finish organising all my photos and moving them
over to Immich. There’s an import script for Google Photos, but I’m taking the
opportunity to sort through all the junk and categorise everything instead of
replicating the same huge mess in a new place.</p>

<p>And also to figure out a new nvim config. Only time will tell if I finish this,
or if I’ll give up and use kickstart. Either way it’ll be grand, I’m sure.</p>

<p>Happy new year folks. Best of luck to you in 2025, I’ll see you there. x</p>]]></content><author><name></name></author><category term="Uses" /><summary type="html"><![CDATA[Hi, I’m Louis! I’m the the Gleam programming language guy.]]></summary></entry><entry><title type="html">How to add metaprogramming to Gleam</title><link href="https://lpil.uk//blog/how-to-add-metaprogramming-to-gleam/" rel="alternate" type="text/html" title="How to add metaprogramming to Gleam" /><published>2024-12-26T00:00:00+00:00</published><updated>2024-12-26T00:00:00+00:00</updated><id>https://lpil.uk//blog/how-to-add-metaprogramming-to-gleam</id><content type="html" xml:base="https://lpil.uk//blog/how-to-add-metaprogramming-to-gleam/"><![CDATA[<p>It’s common for people to ask “will Gleam get metaprogramming?”. It’s a good
question! A question that has a relatively complex answer, one that takes time
to give. This post is an attempt to condense this conversation I’ve had many
times into one document so that folks can get their answer more easily.</p>

<h2 id="what-is-metaprogramming-anyway">What is metaprogramming anyway?</h2>

<p>The word “metaprogramming” is not like “parametric polymorphism” or “tail call
optimisation”. It is not the name of a feature, it is the name of a family of
features. Two people could both use the word “metaprogramming” and be talking
about two entirely different features that behave very differently.</p>

<ul>
  <li>A C++ programmer may think of <a href="https://en.cppreference.com/w/cpp/language/templates">template classes and template functions</a>.</li>
  <li>An Elixir programmer may think of <a href="https://hexdocs.pm/elixir/main/macros.html">compile time code execution that manipulates code as data</a>.</li>
  <li>A Java programmer may think of <a href="https://docs.oracle.com/javase/tutorial/reflect/index.html">runtime reflection</a>.</li>
  <li>A Ruby programmer may think of dynamic metaprogramming with <a href="https://apidock.com/ruby/BasicObject/method_missing"><code class="language-plaintext highlighter-rouge">#method_missing</code></a>.</li>
  <li>A Zig programmer may think of <a href="https://zig.guide/language-basics/comptime/"><code class="language-plaintext highlighter-rouge">comptime</code></a>.</li>
</ul>

<p>And more!</p>

<p>To meaningfully discuss any addition everyone has to have a shared
understanding of precisely what the proposed addition is. Avoiding ambiguity
and jargon is vital, and there may be a more useful word than “metaprogramming”.</p>

<h2 id="evolving-a-programming-language">Evolving a programming language</h2>

<p>Language design is hard. One can’t evaluate a feature without taking into
account the context of the rest of the language and the ecosystem around it.
Every feature will interplay with every other feature in some way, and it could
be that a seemingly straightforward change has dire consequences elsewhere. For
example, if Gleam were to get an early-return feature with an explicit <code class="language-plaintext highlighter-rouge">return</code>
keyword then its much-loved <a href="https://tour.gleam.run/advanced-features/use/"><code class="language-plaintext highlighter-rouge">use</code> feature</a>
would no longer work (as <code class="language-plaintext highlighter-rouge">use</code> depends on everything being an expression).</p>

<p>The impact on the users of the language needs to also be carefully considered.
How could a programmer misunderstand a feature? How could a programmer misuse
feature? What sort of a pickle could a programmer get themselves into with this
feature when having an especially unlucky or stressful day? We’ve all had days
where we look at the code we’ve written and regret the approach we’ve taken to
a problem. That sucks. We want that to happen as infrequently as possible.</p>

<p>The more context, the better. Figure out the wider consequences of the feature
are. See how it could change how Gleam is written, read, and learnt. Write a
bunch of (uncompilable) programs using this future-Gleam and see how it feels to
use. Update existing programs and libraries and see how it changes them.</p>

<p>Gleam aims to be predictable, reliable, easy to learn, and easy to read, so any
change to the language should not work against these goals.</p>

<h2 id="putting-the-cart-before-the-horse">Putting the cart before the horse</h2>

<p>Features are solutions to problems, and we cannot judge whether a solution is
good if we don’t know what the problem being solved is.</p>

<p>Identity real problems and deficiencies with reading and writing Gleam today.
Not <em>predicted</em> problems or <em>suspected</em> problems. Actual concrete pains that are
present in real-world Gleam today. Persistent problems that cannot be
reasonably solved with any of the options we have today in Gleam.</p>

<p>List the current approaches folks take to solve these problems and put into
words why they are not enough. This will involve talking to other community
members to discover how they’re approaching these problems, or even if they
consider them problem at all. It may be that during this process you discover a
satisfactory solution that you were not aware of!</p>

<p>And no, “I like this feature in this other language” and “it would be cool” are
not valid problems! 😁</p>

<h2 id="compilers-are-just-programs">Compilers are just programs</h2>

<p>If you want to add a feature to a program then you need to have an idea of how
it would be implemented, and in that respect compilers are no different from
any other program. Consider what the compiler is going to need to do when it
encounters code using the new feature. How it will analyse the code? What
information will it need to figure out? What code it will generate? Without
answers to these questions a feature cannot be implemented, no matter how
attractive and desirable it might be.</p>

<p>For example, if the feature is Lisp-style macros then the compiler is going to
need a way to evaluate Gleam code at compiler time. Currently the compiler
can’t do this, so how would that be added? There’s a great deal of complexity
there, especially since we want compilation to be fast!</p>

<h2 id="make-a-proposal">Make a proposal</h2>

<p>Once you have done this research and design work then write it up into a
document. Detail all of the above, along with what you see as the pros and cons
of the design. Share it with the community for discussion, so they can evaluate
it and find anything that can be improved or any roadblocks that you hadn’t
discovered. If the proposal is good then there will likely be a lot of
back-and-forth and iteration on the idea, so all the little details can be
figured out.</p>

<p>Be aware that the likelihood of any particular proposal being accepted is slim,
so try not to get too emotionally invested in any specific design. Even a
proposal that did not make it into the language is valuable as it will
influence and inform future work. This is especially true for the analysis of
problems with current-day Gleam; It is from out shortcomings in which we have
the most opportunity to learn and improve.</p>

<p>Thanks for reading 💖</p>]]></content><author><name></name></author><category term="gleam" /><summary type="html"><![CDATA[It’s common for people to ask “will Gleam get metaprogramming?”. It’s a good question! A question that has a relatively complex answer, one that takes time to give. This post is an attempt to condense this conversation I’ve had many times into one document so that folks can get their answer more easily.]]></summary></entry><entry><title type="html">What I use, 2023 edition</title><link href="https://lpil.uk//blog/what-i-use-2023/" rel="alternate" type="text/html" title="What I use, 2023 edition" /><published>2023-12-26T00:00:00+00:00</published><updated>2023-12-26T00:00:00+00:00</updated><id>https://lpil.uk//blog/what-i-use-2023</id><content type="html" xml:base="https://lpil.uk//blog/what-i-use-2023/"><![CDATA[<p>Hi, I’m Louis! Creator of <a href="https://gleam.run">the Gleam programming language</a>
and software tinker.</p>

<p>I’m a sucker for nostalgia, and I enjoy talking and thinking about technology,
so I’ve decided to join the <code class="language-plaintext highlighter-rouge">/uses</code> train and write about what tech I use and
why. Let’s go!</p>

<h2 id="my-workstation">My workstation</h2>

<p>I use a 2020 Macbook Air with 16GB RAM, the hostname for which is Diglett. I
name all my devices after Pokemon as I’m very mature and enjoy occasionally
forgetting exactly which one is Bellsprout and which is one Chansey.</p>

<p>I’m rather indifferent to macOS and in may ways prefer Linux, but they’re
reliable machines and I was quite excited to use an ARM processor. I think my
ideal would be an ARM Linux laptop that’s design to be repairable and
upgradable, but as I’ve got this I’m going to try and make it last as long as
possible, to avoid creating electrical waste.</p>

<p>For years I’ve struggled with RSI. I’m largely OK these days, but it means I
have to be mindful of how I use computers. I almost never use my laptop as a
laptop, instead using it as a desktop computer. Perhaps an Apple mini desktop
would have been better, but it’s nice to have a computer I can take with on the
infrequent occasions I travel and might want to do some work.</p>

<p>My monitor is some cheap 1440p ultrawide AOC thing that a friend gave to me.
I’m not in love with it, but I can’t afford a lovely 5k replacement, so it’ll
do for now. My mouse is a Razer brand bluetooth mouse which works well enough
for me.</p>

<p>My keyboard! I got into mechanical keyboards in part because of my RSI, but it
has become something I really enjoy. I use an ortholinear 40% <a href="https://drop.com/buy/planck-mechanical-keyboard?defaultSelectionIds=976713">Planck v7</a>
with a gorgeous green aluminium case and green <a href="https://drop.com/buy/massdrop-x-olkb-acute-ortholinear-keycap-set">Acute keycaps</a>. I use
Cherry MX Silent Red switches, which are linear and the quietest switches I’ve
ever heard. They feel fab to me, and they’re much less antisocial than other
switches I’ve used.</p>

<p><img src="/img/planck.webp" alt="A tiny ortholinear mechanical keyboard of 4 rows and 12 columns" /></p>

<p>I’d really like to experiment with low profile split bluetooth keyboards like
the <a href="https://github.com/GEIGEIGEIST/TOTEM#photos">GEIST TOTEM</a>. You could do all sorts of intereting things with them
like attach them to your thighs or mount them vertically on a desk, two
positions which seem to fit the human body much more naturally than the
standard horizontal keyboard position. It’s a lot of money for an experiment
though, so it’ll have to wait.</p>

<p>Every desk chair ever seems to give me back ache, including the expensive ones
everyone recommends. These days I’m using the rather bizarre looking Varier
Variable Balans kneeling chair, which I bought second hand as an experiment.
Most the time I kneel on it not the way it’s intended, but it’s working much
better for me than a regular chair. I think part of that is that I move around
on it so much and rotate through a bunch of different positions while using it.</p>

<p><img src="/img/varier-variable-balans.png" alt="A rocking kneeling stool in all black" /></p>

<p>The majority of the time I don’t sit on anything at all, thanks to my <a href="https://www.ikea.com/gb/en/p/trotten-desk-sit-stand-white-s99429578/">IKEA
hand-cranked standing desk</a>. This is maybe one the best things I’ve bought when
it comes to my health, I’d recommend it to everyone, especially with how cheap
the hand-cranked one is. Standing and moving around has made such a difference
for me.</p>

<p>On my desk I’ve got a pair of comically named <a href="https://www.sony.co.uk/electronics/headband-headphones/wh-1000xm3">Sony WH-1000XM3</a>
headphones. I bought them years back for when I worked in a noisy office, but
they still get use for playing rave music letting me rapidly alternate between
programming and dancing around the room like a nutter. Perhaps controversially,
I don’t think they sound very good, and something without isolation (or with a
pass-through feature) would be much better for me. ANC can be tiring on the
ears, and I’ve missed the doorbell on a few occasions.</p>

<h2 id="software">Software</h2>

<p>I’ve been a Linux terminal person for longer than I’ve been a programmer, but
in recent years I’ve been using Visual Studio Code, after being impressed with
how easy it was to get going with. However, a week ago I tried my old love
<a href="https://neovim.io/">Neovim</a> again after having not used it since before Lua support landed.
Wow, the configuration and out-of-the-box experience is really good these days!
I’ve been using the <a href="https://www.lazyvim.org/">LazyVim</a> starter template and I think Neovim is
going to become my daily driver once again.</p>

<p>Some other software I use on my workstation:</p>

<ul>
  <li><a href="https://www.mozilla.org/en-GB/firefox/">Firefox</a>, a browser.</li>
  <li><a href="https://fishshell.com/">Fish</a>, a shell with excellent UX.</li>
  <li><a href="https://www.migadu.com/">Migadu</a> and the Apple Mail app, for email.</li>
  <li><a href="https://github.com/MusicPlayerDaemon/MPD/">mpd</a> with <a href="https://github.com/MusicPlayerDaemon/mpc">mpc</a> and <a href="https://github.com/danbee/persephone">Persephone</a>, a hacker’s music player.</li>
  <li><a href="https://github.com/watchexec/watchexec">watchexec</a>, for running commands when files change.</li>
  <li><a href="https://github.com/ducaale/xh">xh</a>, an ergonomic terminal HTTP client.</li>
  <li><a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a>, a video downloader.</li>
  <li><a href="https://joeyh.name/code/moreutils/">moreutils</a>, some niche but useful scripts.</li>
  <li><a href="https://github.com/ajeetdsouza/zoxide">zoxide</a>, a fast directory jumper.</li>
  <li><a href="https://asdf-vm.com/">asdf</a>, a software version manager and installer.</li>
  <li><a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, grep but a little easier to use.</li>
</ul>

<h2 id="personal-devices">Personal devices</h2>

<p>Over the last few years I seem to have accumulated a lot of Apple stuff. I’m a
little worried about how annoying it’ll be to move away from them, but that
seems to be an unavoidable problem with any brand these days.</p>

<p>My phone is an iPhone Pro 13 Pro (named Machop), which I bought refurbished
this year to replace a very broken Android phone. I quite like a lot about the
iOS experience and lots of the built-in features and apps are very clever. It
has made me much more interested in phones as a programming platform. This is
immensely frustrating as the yearly fee you have to pay Apple to run your own
software on the computer <em>you bought from them</em> is straight up robbery and should
be illegal as far as I’m concerned. I’m very jealous of how Android users can
run a full Linux development environment on their phones. A phone and a
bluetooth keyboard would be an amazing portable setup and I could forget about
laptops entirely!</p>

<p>I have an iPad Air 4 (named Hoothoot) which I mostly use for watching videos
when cooking or in bed, and this year I splashed out on the cheapest Apple
watch (named Kabuto) when I found one heavily discounted. It’s a too
underpowered for most apps to be usable, but I’ve found the “rings” that nag
you to exercise surprisingly motivating. It even got me to buy an Apple
Fitness+ subscription, which I do make use of. Well done to the Apple marketing
team I suppose!</p>

<h2 id="homelab">Homelab</h2>

<p>Now here’s some computing which brings me a lot joy! Since going full time on
Gleam (rather than working 2 jobs at once as I have for many years) I’ve had
time for engaging with technology as a hobby again. One thing I’ve been doing
is winding down my use of proprietary services and instead running my own
software. It has been tremendously enjoyable.</p>

<p>I may be slightly misusing the term “homelab” as my philosophy is quite
different to what I see on homelab social media, but I’m not sure what else to
call it. I really like trying to do as much as possible with as little as
possible so big powerful computers running lots of docker containers of
resource hungry programs isn’t very appealing to me. Inefficient software
causes more polution, and it’s so much more fun to solve a problem when
you have constraints! So many of these systems are designed for large
enterprises, but I’m not a large enterprise, I’m some guy with a bad moustache.</p>

<p>I’ve a 4GB Raspberry Pi 4 (named Cubone) which is my server. It boots from an
SSD connected via a USB cable, which is a bit silly but the novelty PlayStation
case I found was so cute I completely abandoned any logical thinking and bought
it immediately. It doesn’t have a place for an SSD inside.</p>

<p><img src="/img/pistation.png" alt="A Raspberry Pi case that looks like an original PlayStation" /></p>

<p>I’ve used configuration management tools like Ansible and Chef a lot in the
past, but I don’t enjoy using any of them. The idea behind them is great, but
they’re all so slow and overly complex. For my personal stuff I just write
<a href="https://github.com/lpil/lpil/tree/7041104aa36ed5178007ef85f6d4617f5f144e9a/home/server">some POSIX shell scripts</a> which I <code class="language-plaintext highlighter-rouge">scp</code> to the server and run.</p>

<p>If you’re a comfortable programmer and have experience with Ansible or similar
then I think using shell scripts will be easier to work with, and can can run
in a couple seconds rather than possibly minutes. Give it a try!</p>

<h3 id="vpn-and-backups">VPN and backups</h3>

<p>The server isn’t publicly accessible, so I use <a href="https://tailscale.com/">Tailscale</a> to create a VPN
for my devices. Tailscale rules. I love it. It makes a lot of boring stuff
really easy.</p>

<p>Backing up and replicating data is really important! There’s few things I would
be more upset about losing than family photos. I use <a href="https://syncthing.net/">Syncthing</a> to
replicate my personal data beteen the server and my laptop, and
<a href="https://rclone.org/">rclone</a> to back it up to Backblaze B2 and my dad’s home server. I
think I’d like to replace B2 and get an offline backup next year. I’m also
curious about btrfs.</p>

<h3 id="media">Media</h3>

<p>The server has SFTP enabled and my phone pushes photos and videos to it over
that. I’m using the app FTPManager, but it’ll probably get replaced as it
doesn’t seem to support not pushing photos that have already been uploaded,
even if they are no longer in the SFTP receive directory.</p>

<p>For a while I ran Plex, but it sucked! It used monstrous amounts of resources
and was constantly nagging to give them money. I replaced it by enabling NTFS
on the server and using the Android VLC app to stream videos from it on my TV.
It uses pretty much no resources, and the only downside so far is that I have
to navigate the file system rather than having some fancy UI. That’s fine, it’s
my file system. I know where I put the video I want to watch.</p>

<h3 id="web">Web</h3>

<p>I slightly lied when I said the server isn’t publicly accessible. I’m using
<a href="https://www.cloudflare.com/en-gb/products/tunnel/">Cloudflare Tunnel</a> to <strike>man in the middle attack myself</strike>
proxy HTTP traffic from the public internet to the server. It gets received by
<a href="https://caddyserver.com/">Caddy</a>, a fantastic web proxy that finally ended my many year
relationship with Nginx.</p>

<p>I’ve got Caddy serving two directories of files, one public, one with HTTP
basic authentication, as well as a couple static sites. I’ve also added a
<a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a> module to Caddy and use it to run some scripts, presumably because
I’m completely mad.</p>

<p>OK! Hear me out! CGI is good, actually!</p>

<p>It may be kinda slow and awkward, but CGI scripts only uses system resources
when they are actually handling a request. There’s so many sites on the
internet that serve practically no traffic, and so much energy and compute
could be saved by not running those programs all the time, taking up space
doing nothing. If we called it <em>POSIX serverless</em> or something I bet we could
get some VC funding for it.</p>

<h3 id="monitoring">Monitoring</h3>

<p>I have a handful of sites that I want to know about any outages for, and for
this I use <a href="https://github.com/TwiN/gatus/">gatus</a>. It’s a simple little program that will take a yaml
file of URLs and periodically send them HTTP requests, and then sending you a
notification if they fail however many times in a row.</p>

<p>It’s written in Go. I tend to opt for software written in Go as there’s a lot
of it, it’s typically a single statically linked binary, and often these
programs use relatively small amounts of resources. It’s not always the case,
but Gatus ticks those boxes.</p>

<p>For notifications I use <a href="https://pushover.net/">Pushover</a>, which a service that lets you
send a HTTP request to them and they’ll send a push notification to your phone.
That’s it. Super straightforward. What’s even cooler is that it’s a single $5
purchase for the app, and then you can use it forever. I hope the developer is
making enough money because it seems like a really generous deal to me.</p>

<p>The last bit of software I run is <a href="https://github.com/arp242/goatcounter">Goatcounter</a>, a basic analytics
service, again written in Go. Ideally I’d just parse access logs to get visitor
numbers, but some of these sites are hosted on GitHub pages so I don’t have
access to that data.</p>

<p>It’s an OK bit of software. The UI is nice, but by default it tracks more than
I want (I don’t use their default JavaScript for this reason), and it has this
really silly thing where you need to use a new DNS name for each site you want
to track. I’m not going to duplicate a DNS record once per site, it’s not
practical. I’m hacking around the problem by prepending the hostname to the
path when registering events. It works, if unsatisfyingly.</p>

<p>All the alternatives seem to be more invasive or use more resources, so I’m
sticking with it for now. Let me know if you have an alternative to suggest.</p>

<h3 id="networking">Networking</h3>

<p>Confession time: I don’t understand my home network well enough to explain it.
When I moved into this flat I mentioned what I was aiming for to my dad and he
turned up later with a bunch of stuff and installed it while giving me one of
those “engineer with decades of experience in this field” explanations that’s
just a bit too much to absorb in one go. He’s also more of a
configure-things-with-interactive-tools than an configure-with-code kinda guy,
so there’s a bit of a mismatch there too.</p>

<p>He knows his stuff and it’s been rock solid so I’ve largely ignored it,
depriving myself of any opportunity to learn. Don’t worry, it’s there on my
never-ending todo list. Somewhere…</p>

<h3 id="the-future">The Future</h3>

<p>I add to this little project every now and again as the mood takes me. Other
than the additions mentioned above I’d like to try a minimalistic approach to
<a href="https://prometheus.io/">Prometheus</a>. It seems promising, didn’t use too much memory in my brief
test, and it has Pushover support. Perhaps it could replace Gatus too.</p>

<p><a href="https://github.com/photoprism/photoprism">PhotoPrism</a> looks like very nice photo library software. I’m
hoping <a href="https://github.com/gristlabs/grist-core">Grist</a> could break my Google Sheets dependence. I use Apple
notes for Journaling and some basic data collection, I’d like to export that
data and keep my own copy somehow. DNS that blocks adverts like <a href="https://github.com/AdguardTeam/AdGuardHome">AdGuard
Home</a> could be good for my phone’s battery life and surfing
experience.</p>

<p>So yes, lots more fun to be had with this tiny little computer. :)</p>

<p>Happy new year folks. I hope you had a good 2023, and hope this next one is
even better. x</p>]]></content><author><name></name></author><category term="Uses" /><summary type="html"><![CDATA[Hi, I’m Louis! Creator of the Gleam programming language and software tinker.]]></summary></entry><entry><title type="html">An Exercism Syllabus for Gleam</title><link href="https://lpil.uk//blog/an-exercism-syllabus-for-gleam/" rel="alternate" type="text/html" title="An Exercism Syllabus for Gleam" /><published>2023-09-28T00:00:00+00:00</published><updated>2023-09-28T00:00:00+00:00</updated><id>https://lpil.uk//blog/an-exercism-syllabus-for-gleam</id><content type="html" xml:base="https://lpil.uk//blog/an-exercism-syllabus-for-gleam/"><![CDATA[<p>For the last few months I’ve been working with the Erlang Ecosystem Foundation and the Exercism team to create a Gleam syllabus for the Exercism platform, and as of this morning the final lesson has gone live!</p>

<h2 id="what-does-that-mean">What does that mean?</h2>

<p>Just to make sure we’ll all on the same page: <a href="https://gleam.run/">Gleam</a> is a type safe functional programming language that runs on the Erlang virtual machine alongside languages like Erlang, Elixir, and LFE. It was created by me, Louis Pilfold, and is also maintained by the Gleam core team.</p>

<p><a href="https://erlef.org/">The Erlang Ecosystem Foundation</a> is a non-profit organisation that supports the BEAM ecosystem, including all languages and techonologies built upon the BEAM.</p>

<p><a href="https://exercism.org/">Exercism</a> is a delighful platform for learning and practicing programming languages, incorporating lessons, automated challenges, and the option of feedback from mentors who know the language well. It has tracks for 67 languages, including 4 BEAM languages: Erlang, Elixir, LFE, and Gleam. Exercism is also entirely free to use!</p>

<p>Tracks on Exercism comprise a series of exercises students can use to practice a language, however some tracks also have a syllabus of lessons teach the language. Each language concept is carefully introduced and explained in an order that builds upon previous lessons, and each lesson has a set of exercises to practice the concept, taking the student from beginner to proficient and ready to solve real world problems.</p>

<p><img src="/img/blog/gleam-exercism-syllabus/tree.png" alt="The Gleam concepts page on the Exercism website. The title reads 'Your journey through Gleam', and below it has language concepts such as 'basics', 'bools', 'ints', 'modules', and 'anonymous functions' arranged into a tree, similar to a skill tree in a video game." /></p>

<h2 id="what-did-we-build">What did we build?</h2>

<p>Elixir’s syllabus is largely regarded as the gold standard for Exercism tracks, and it is the yard-stick we used when building the Gleam syllabus. The goal was to create a syllabus that would be as good as Elixir’s, and I think we’ve done it.</p>

<p>Before we could start working on the educational content we had to create a <a href="https://github.com/exercism/gleam-test-runner">custom Gleam test runner</a> which implements version 2 of the Exercism test runner interface. The creation of this runner also produced a set of libraries and significant improvements to existing Gleam libraries, including:</p>

<ul>
  <li><a href="https://github.com/DanielleMaywood/glexer/">Glexer, a Gleam tokenizer</a></li>
  <li><a href="https://github.com/lpil/glance/">Glance, a Gleam parser</a></li>
  <li><a href="https://github.com/JohnBjrk/gap">Gap, a diffing library</a></li>
  <li><a href="https://github.com/bcpeinhardt/simplifile">Simplifile, a file system library</a></li>
  <li><a href="https://github.com/gleam-lang/erlang">gleam_erlang, bindings to standard Erlang APIs</a></li>
</ul>

<p>Work has also begun two other static analysis tools for Gleam, which will be later incorporated into Exercism to provide further automated feedback. They would also make a strong foundation for a generic Gleam linter that can be used by software development teams using Gleam.</p>

<p>Afterwards the language was divided into a set of concepts, and the concepts arranged into dependency order such that each concept builds upon the previous. The existing exercises were then assigned to the concepts, depending on what language features the student would likely need to know to solve the exercise.</p>

<p>As each concept lesson was written a corresponding exercise would be created alongside it. This exercise would be especially focused on the concept being taught, and is intended to solidify the student’s understanding, while not being too time consuming to solve.</p>

<p>The initial set of 12 concepts and 12 accompanying exercises were launched on the 4th of July, alongside the existing 92 exercises. Further concepts and exercises were added over the following months with the final being merged this morning on the 28th of September, bringing the track up to <a href="https://exercism.org/tracks/gleam/concepts">36 concepts</a> and <a href="https://exercism.org/tracks/gleam/exercises">122 exercises</a>. Gleam is now one of the most comprehensive tracks on Exercism, standing alongside Elixir!</p>

<p>Throughout the development period I and the other Gleam mentors have been offering feedback to students on their exercise submissions, and we will continue to do so into the future.</p>

<h2 id="how-was-it-received">How was it received?</h2>

<p>I am rather fond of keeping some stats, so I wrote a small Gleam script that scrapes the Exercism website once a day and records how much activity there was on the track for that day. Here’s a couple of charts:</p>

<h3 id="number-of-students">Number of students</h3>

<p><img src="/img/blog/gleam-exercism-syllabus/students.svg" alt="A line chart. It rises steadily, the rate of change increasing when the first concepts were launched." /></p>

<p>When the first concepts were launched the track had 474 students, and today there are 627 students. The rate at which students joined the course increased after the first concepts were launched in late July.</p>

<h3 id="number-of-exercise-submissions">Number of exercise submissions</h3>

<p><img src="/img/blog/gleam-exercism-syllabus/submissions.svg" alt="A line chart. It rises steadily, the rate of change greatly increasing when the first concepts were launched." /></p>

<p>The impact of the concepts exercise is much more striking when looking at the number of exercise submissions by Gleam students, the rate greatly increasing after the first concepts were launched. Students are much more engaged with the material than before, spending more time with the track. Since the first concepts were launched there have been 6,936 exercise submissions.</p>

<h3 id="testimonials">Testimonials</h3>

<p>As well as quantitative data I have asked students for feedback on the track, here are some of their responses:</p>

<blockquote>
  <p>I’m working through [the syllabus] at the moment and I’ve personally found them incredibly helpful for helping me focus on solidifying my understanding of specific concepts. The use of multiple examples for each concept helps round out my understanding of how things work a lot better than just reading docs, and having guided exercises that are focused purely on learning a concept at a time helps me gain traction a lot better than coding blindly</p>
</blockquote>

<blockquote>
  <p>Exercism’s Gleam track helps make the language concepts stick, and therefore complements the official documentation really well. I love the fact that I can practice at my own pace and make a bit of progress every day. Even though I’ve barely scratched the surface of Gleam and the BEAM in general, I love where this is heading.</p>
</blockquote>

<blockquote>
  <p>I’ve solved all of the concept exercises, and they were very enjoyable. I like that they’re not too challenging, so I didn’t get stuck on tiny details, but solving them still required me to know specific aspects of the language. I think the Gleam Exercism track will be crucial in the adoption of the language, and will bring more people with different programming backgrounds to the BEAM ecosystem</p>
</blockquote>

<p>And here are some comments from students on their mentoring sessions:</p>

<blockquote>
  <p>Thanks for being really friendly and helpful, and taking the time to write out some really great explanation.</p>
</blockquote>

<blockquote>
  <p>Thanks gain Louis, Gleam has excellent documentation, an excellent Exercism course and… a special mentor!</p>
</blockquote>

<blockquote>
  <p>Thank you for taking time too help out. It was really helpful and a good explanation as to why a particular operator worked as it did.</p>
</blockquote>

<blockquote>
  <p>This was my first mentoring session on Exercism. It will have a special place in my heart having had the creator stopping by and leave a nice word! Thanks 💜</p>
</blockquote>

<blockquote>
  <p>He helped me to understand the language’s idiomatic expressions look like, and recommended ways to rethink the problem to fit more naturally in Gleam.</p>
</blockquote>

<blockquote>
  <p>Yeah… thanks a lot.. I’ll be applying my learnings to the other exercises. I think it was good for me to study a bigger code example to get a feel for gleam !</p>
</blockquote>

<blockquote>
  <p>He pointed out where I could improve the solution with built-in functions that expressed the code’s intent perfectly. This is helping me learn Gleam’s toolkit.</p>
</blockquote>

<p>All the feedback has been very positive, and I’m very happy to see so many folks learning and joining the BEAM community.</p>

<h2 id="thanks">Thanks!</h2>

<p>I would like to thank the Erlang Ecosystem Foundation for supporting me in this work, the Exercism team for their guidance and help, and all <a href="https://exercism.org/contributing/contributors?track_slug=gleam&amp;page=1">the contributors</a> who helped build the Gleam track. And extra special thank you goes to <a href="https://exercism.org/profiles/jiegillet">Jeremie Gillet</a>, <a href="https://exercism.org/profiles/ErikSchierboom">Erik Schierboom</a>, and <a href="https://github.com/giacomocavalieri">Giacomo Cavalieri</a> for their outstanding contributions.</p>

<p>If you appreciate Gleam or Exercism please consider supporting the projects. Both are open source, entirely free to use, and rely on community sponsorship to continue development. Thank you.</p>

<ul>
  <li><a href="https://exercism.org/donate">Exercism’s donation page</a></li>
  <li><a href="https://github.com/sponsors/exercism">Exercism on GitHub sponsors</a></li>
  <li><a href="https://github.com/sponsors/lpil">Gleam on GitHub sponsors</a></li>
</ul>]]></content><author><name></name></author><category term="Gleam" /><category term="Exercism" /><summary type="html"><![CDATA[For the last few months I’ve been working with the Erlang Ecosystem Foundation and the Exercism team to create a Gleam syllabus for the Exercism platform, and as of this morning the final lesson has gone live!]]></summary></entry><entry><title type="html">Using Gleam JS with Elixir’s Phoenix</title><link href="https://lpil.uk//blog/using-gleam-js-with-elixir-phoenix/" rel="alternate" type="text/html" title="Using Gleam JS with Elixir’s Phoenix" /><published>2023-01-11T00:00:00+00:00</published><updated>2023-01-11T00:00:00+00:00</updated><id>https://lpil.uk//blog/using-gleam-js-with-elixir-phoenix</id><content type="html" xml:base="https://lpil.uk//blog/using-gleam-js-with-elixir-phoenix/"><![CDATA[<p>I was talking to <a href="https://github.com/joshprice">Josh</a> over at
<a href="https://alembic.com.au/">Alembic</a> and he mentioned that he was interested in
taking advantage of Gleam’s compile-to-JavaScript capability and using it to
make a front end for an Elixir Phoenix app. What a great idea!</p>

<p>I’m going to show you how to set these two up together, so you can try it too. :)</p>

<p>I’ll assume you already have Elixir and Gleam installed. If you’ve not check out
the documentation for <a href="https://elixir-lang.org/install.html">Elixir</a> and
<a href="https://gleam.run/getting-started/installing/">Gleam</a> respectively. I’m using
Elixir 1.14.2, Gleam 0.25.3, and Phoenix 1.6.15. If you have issues with any
later versions let me know and I’ll update this post.</p>

<h2 id="hello-joe">Hello, Joe!</h2>

<p>First off we’re going to need a Phoenix application. You can use an existing one
if you have one, but I’m going to create a new one.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create a new Phoenix app named `my_app`</span>
mix archive.install hex phx_new
mix phx.new my_app

<span class="c"># Enter the project and setup the database</span>
<span class="nb">cd </span>my_app
mix ecto.create
</code></pre></div></div>

<p>Next up we’re going to need a Gleam project too. In Phoenix projects the front
end lives in the <code class="language-plaintext highlighter-rouge">./assets</code> directory, so we’ll create it in there.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create a new Gleam project named `app`</span>
gleam new assets/app
</code></pre></div></div>

<p>By default Gleam runs on the Erlang virtual machine, so we’ll need to tell it to
compile to JavaScript instead.</p>

<p>Open up <code class="language-plaintext highlighter-rouge">assets/app/gleam.toml</code> and add <code class="language-plaintext highlighter-rouge">target = "javascript"</code>.</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">name</span> <span class="p">=</span> <span class="s">"app"</span>
<span class="py">version</span> <span class="p">=</span> <span class="s">"0.1.0"</span>
<span class="py">target</span> <span class="p">=</span> <span class="s">"javascript"</span> <span class="c"># &lt;- Add this line</span>

<span class="nn">[dependencies]</span>
<span class="py">gleam_stdlib</span> <span class="p">=</span> <span class="s">"~&gt; 0.25"</span>

<span class="nn">[dev-dependencies]</span>
<span class="py">gleeunit</span> <span class="p">=</span> <span class="s">"~&gt; 0.7"</span>
</code></pre></div></div>

<p>The entrypoint to the Gleam project is the <code class="language-plaintext highlighter-rouge">main</code> function in
<code class="language-plaintext highlighter-rouge">assets/app/src/app.gleam</code>. It prints a greeting, which in this case will go to
the browser’s developer console.</p>

<p>I’ve edited the greeting here to make it extra clear that the message is coming
from the Gleam frontend.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">io</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">io</span><span class="nf">.println</span><span class="p">(</span><span class="s">"Hello from the Gleam frontend!"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In a terminal window, run the Gleam build tool to compile Gleam code.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>assets/app
gleam build
<span class="nb">cd</span> ../..
</code></pre></div></div>

<p>The Gleam code has been compiled to JavaScript, but unless it is included into
the JavaScript bundle it won’t get run.</p>

<p>Open up the JavaScript file <code class="language-plaintext highlighter-rouge">assets/js/app.js</code> and at the bottom of the file
import the compiled Gleam <code class="language-plaintext highlighter-rouge">app</code> module, calling its <code class="language-plaintext highlighter-rouge">main</code> function.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">main</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../app/build/dev/javascript/app/app.mjs</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">main</span><span class="p">();</span>
</code></pre></div></div>

<p>Now run the Phoenix server with <code class="language-plaintext highlighter-rouge">mix phx.server</code> and open up
<code class="language-plaintext highlighter-rouge">http://localhost:4000</code> in the browser. In the developer console there is the
message “Hello from the Gleam frontend!”. Success! 💃</p>

<h2 id="automatic-recompilation">Automatic recompilation</h2>

<p>We’ve got Gleam code now running in the browser, but having to manually run the
compiler is annoying. What we really want is for the code to automatically be
recompiled when edits are saved. To do this we’re going to take advantage of
Phoenix’s development watchers feature.</p>

<p>By default Phoenix comes with one watcher that runs esbuild, a tool that bundles
front end assets together for use in the browser. We’re going to add a second
one that runs the Gleam compiler.</p>

<p>We’re going to need a way to detect when a Gleam file has changed, so add the
<code class="language-plaintext highlighter-rouge">file_system</code> package to your <code class="language-plaintext highlighter-rouge">mix.exs</code> file.</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
    <span class="p">[</span>
      <span class="c1"># ...other deps here...</span>
      <span class="p">{</span><span class="ss">:file_system</span><span class="p">,</span> <span class="s2">"~&gt; 0.2"</span><span class="p">,</span> <span class="ss">only:</span> <span class="ss">:dev</span><span class="p">}</span>
    <span class="p">]</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>Add a new watcher for Gleam to <code class="language-plaintext highlighter-rouge">config/dev.exs</code>.</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="ss">:my_app</span><span class="p">,</span> <span class="no">MyAppWeb</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
  <span class="c1"># ...other config here...</span>
  <span class="ss">watchers:</span> <span class="p">[</span>
    <span class="c1"># ...other watchers here...</span>
    <span class="ss">gleam:</span> <span class="p">{</span><span class="no">GleamBuilder</span><span class="p">,</span> <span class="ss">:start_link</span><span class="p">,</span> <span class="p">[]}</span> <span class="c1"># &lt;- Add this entry</span>
  <span class="p">]</span>
</code></pre></div></div>

<p>With this configuration when the application starts in dev mode Phoenix will
call the <code class="language-plaintext highlighter-rouge">start_link</code> function on the <code class="language-plaintext highlighter-rouge">GleamBuilder</code> module, and that function
will be responsible for building the Gleam code while the application is
running.</p>

<p>Open up the <code class="language-plaintext highlighter-rouge">lib/gleam_builder.ex</code> file and enter the code below to define the
new watcher.</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># This watcher uses dev deps so let's only define it in dev to</span>
<span class="c1"># avoid warnings during compilation in production.</span>
<span class="k">if</span> <span class="no">Mix</span><span class="o">.</span><span class="n">env</span><span class="p">()</span> <span class="o">==</span> <span class="ss">:dev</span> <span class="k">do</span>

  <span class="k">defmodule</span> <span class="no">GleamBuilder</span> <span class="k">do</span>
    <span class="nv">@gleam_dirs</span> <span class="p">[</span><span class="s2">"assets/app/src"</span><span class="p">,</span> <span class="s2">"assets/app/test"</span><span class="p">]</span>

    <span class="k">def</span> <span class="n">start_link</span><span class="p">(</span><span class="n">_args</span> <span class="p">\\</span> <span class="no">nil</span><span class="p">)</span> <span class="k">do</span>
      <span class="no">GenServer</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="bp">__MODULE__</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="n">init</span><span class="p">(</span><span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
      <span class="c1"># Watch for changes to Gleam files</span>
      <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">pid</span><span class="p">}</span> <span class="o">=</span> <span class="no">FileSystem</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="ss">dirs:</span> <span class="nv">@gleam_dirs</span><span class="p">)</span>
      <span class="no">FileSystem</span><span class="o">.</span><span class="n">subscribe</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>

      <span class="c1"># Run the compiler once for the initial code</span>
      <span class="n">run_gleam_compiler</span><span class="p">()</span>
      <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="no">nil</span><span class="p">}</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="n">handle_info</span><span class="p">({</span><span class="ss">:file_event</span><span class="p">,</span> <span class="n">_watcher</span><span class="p">,</span> <span class="n">_event</span><span class="p">},</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
      <span class="c1"># A Gleam file has changed, run the compiler</span>
      <span class="n">run_gleam_compiler</span><span class="p">()</span>
      <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">state</span><span class="p">}</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="n">run_gleam_compiler</span><span class="p">()</span> <span class="k">do</span>
      <span class="no">System</span><span class="o">.</span><span class="n">cmd</span><span class="p">(</span><span class="s2">"gleam"</span><span class="p">,</span> <span class="p">[</span><span class="s2">"build"</span><span class="p">],</span>
        <span class="ss">cd:</span> <span class="s2">"assets/app"</span><span class="p">,</span>
        <span class="ss">into:</span> <span class="no">IO</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span><span class="ss">:stdio</span><span class="p">,</span> <span class="ss">:line</span><span class="p">),</span>
        <span class="ss">stderr_to_stdout:</span> <span class="no">true</span>
      <span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Wow there’s a lot going on here!</p>

<p>The watcher is only used in <code class="language-plaintext highlighter-rouge">:dev</code> mode and uses development dependencies, so it
would emit warnings if it were compiled in <code class="language-plaintext highlighter-rouge">:prod</code> mode. To avoid this we wrap
the whole module definition in an <code class="language-plaintext highlighter-rouge">if</code> statement that checks the environment at
compile time.</p>

<p>The watcher is an Elixir <code class="language-plaintext highlighter-rouge">GenServer</code> which is started by the <code class="language-plaintext highlighter-rouge">start_link</code> function.</p>

<p>In the <code class="language-plaintext highlighter-rouge">init</code> function it starts a <code class="language-plaintext highlighter-rouge">FileSystem</code> process for the Gleam code
directories and then calls the <code class="language-plaintext highlighter-rouge">FileSystem.subscribe/1</code> function in order to get
notified with a message any time there are changes. It also runs the Gleam
compiler once in <code class="language-plaintext highlighter-rouge">init</code> to build the code initially.</p>

<p>The <code class="language-plaintext highlighter-rouge">handle_info</code> function is called for each message from the <code class="language-plaintext highlighter-rouge">FileSystem</code>
process. It runs the Gleam compiler again to rebuild the code with the
just-saved changes.</p>

<p>And that’s it! If you start the Phoenix server again you’ll see the latest
greeting get printed to the browser developer console each time you edit and
save the <code class="language-plaintext highlighter-rouge">assets/app/src/app.gleam</code> file. Or any other Gleam file for that
matter.</p>

<h2 id="whats-next">What’s next?</h2>

<p>Now that the project is set up you probably want to start writing some Gleam
code!</p>

<p>You could import and use some JavaScript DOM functions using Gleam’s
<a href="https://gleam.run/book/tour/external-functions.html">external function feature</a>,
or you could build a more sophisticated React based front end using
<a href="https://github.com/brettkolodny/react-gleam">react-gleam</a> or
<a href="https://github.com/hayleigh-dot-dev/gleam-lustre/">Lustre</a>.</p>

<p>Whatever you do, have fun! Be sure to share anything cool that you make on the
<a href="https://discord.gg/Fm8Pwmy">Gleam Discord server</a>.</p>

<p>P.S. If you want to see all the changes I made to the Phoenix app check out
<a href="https://github.com/lpil/lpil/pull/30/commits/e1db86af9d7701cd6f02ff038f4c9daeb480261a">this git commit</a>.</p>]]></content><author><name></name></author><category term="Gleam" /><category term="Elixir" /><category term="How to" /><summary type="html"><![CDATA[I was talking to Josh over at Alembic and he mentioned that he was interested in taking advantage of Gleam’s compile-to-JavaScript capability and using it to make a front end for an Elixir Phoenix app. What a great idea!]]></summary></entry><entry><title type="html">Deploying Gleam on Fly.io</title><link href="https://lpil.uk//blog/deploying-gleam-on-fly-io/" rel="alternate" type="text/html" title="Deploying Gleam on Fly.io" /><published>2022-06-19T00:00:00+00:00</published><updated>2022-06-19T00:00:00+00:00</updated><id>https://lpil.uk//blog/deploying-gleam-on-fly-io</id><content type="html" xml:base="https://lpil.uk//blog/deploying-gleam-on-fly-io/"><![CDATA[<p>The recently released Gleam v0.22 includes a new command to prepare a Gleam
application for deployment. With this in mind I thought it would be a good time
to show how deploy Gleam into production with my new favourite platform,
<a href="https://fly.io">Fly.io</a>.</p>

<p>Full disclosure, Fly.io are <a href="https://github.com/sponsors/lpil">sponsoring my work on Gleam</a>.
I hadn’t used their platform prior to their sponsoring, but now that I have I
believe that they have the best user experience of any deployment platform
today, beating even my old favourite of Heroku.</p>

<p>I’ve already got Gleam, Erlang, and the Fly.io command line installed, logged in
to Fly.io, and added a payment card to my Fly.io organisation. If you’re
following along at home you’ll likely want to do these too.</p>

<p>Right. Let’s get started.</p>

<h2 id="a-gleam-web-app">A Gleam web app</h2>

<p>I’m going to need an application to deploy, so I’m going to quickly make a super
basic one. First I create a new project and add the required dependencies.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gleam new pidgey
<span class="nb">cd </span>pidgey
gleam add mist gleam_http gleam_erlang
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">mist</code> is a web server, and <code class="language-plaintext highlighter-rouge">gleam_http</code> and <code class="language-plaintext highlighter-rouge">gleam_erlang</code> provide helper types
and functions for working with HTTP and the Erlang runtime respectively.</p>

<p>Next I’ll open up <code class="language-plaintext highlighter-rouge">src/pidgey.gleam</code> and write a tiny web application that
always returns the body “<a href="https://www.youtube.com/watch?v=uKfKtXYLG78">Hello, Joe!</a>”.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">mist</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">io</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">erlang</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">bit_builder</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span><span class="o">/</span><span class="n">response</span><span class="err">.</span><span class="p">{</span><span class="n">Response</span><span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// Start the web server</span>
  <span class="n">assert</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">mist</span><span class="nf">.run_service</span><span class="p">(</span><span class="mi">8080</span><span class="p">,</span> <span class="n">web_service</span><span class="p">)</span>

  <span class="c1">// Put the main thread to sleep while the server works</span>
  <span class="n">erlang</span><span class="nf">.sleep_forever</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">web_service</span><span class="p">(</span><span class="n">_request</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">body</span> <span class="o">=</span> <span class="n">bit_builder</span><span class="nf">.from_string</span><span class="p">(</span><span class="s">"Hello, Joe!"</span><span class="p">)</span>
  <span class="nf">Response</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="p">[],</span> <span class="n">body</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now I can run it with <code class="language-plaintext highlighter-rouge">gleam run</code>, and test it out in another terminal with curl.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl localhost:8080
<span class="c"># Hello, Joe!</span>
</code></pre></div></div>

<p>Neat :)</p>

<h2 id="preparing-to-deploy">Preparing to deploy</h2>

<p>Fly.io has built in support for some languages but Gleam isn’t one of them, so
I’ll have to use their docker support instead.</p>

<p>Within the Gleam project I create a file named <code class="language-plaintext highlighter-rouge">Dockerfile</code> with these contents.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> ghcr.io/gleam-lang/gleam:v0.22.0-erlang-alpine</span>

<span class="c"># Add project code</span>
<span class="k">COPY</span><span class="s"> . /build/</span>

<span class="c"># Compile the project</span>
<span class="k">RUN </span><span class="nb">cd</span> /build <span class="se">\
</span>  <span class="o">&amp;&amp;</span> gleam <span class="nb">export </span>erlang-shipment <span class="se">\
</span>  <span class="o">&amp;&amp;</span> <span class="nb">mv </span>build/erlang-shipment /app <span class="se">\
</span>  <span class="o">&amp;&amp;</span> <span class="nb">rm</span> <span class="nt">-r</span> /build

<span class="c"># Run the server</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/app/entrypoint.sh"]</span>
<span class="k">CMD</span><span class="s"> ["run"]</span>
</code></pre></div></div>

<p>If we were deploying a very large application we might want to add some caching,
and we should probably create a new Linux user for the application to run as,
but this is good enough for now. There’s lots of resources online for docker
best practices if you’d like to know more, and I’ll probably make a more
detailed example for Gleam in future.</p>

<h2 id="ship-it">Ship it!</h2>

<p>Now to host the web app on Fly.io by running <code class="language-plaintext highlighter-rouge">flyctl launch</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flyctl launch
<span class="c"># Creating app in /tmp/pidgey</span>
<span class="c"># Scanning source code</span>
<span class="c"># Detected a Dockerfile app</span>
<span class="c"># ? App Name (leave blank to use an auto-generated name): pidgey</span>
<span class="c"># ? Select organization: Louis Pilfold (personal)</span>
<span class="c"># ? Select region: lhr (London, United Kingdom)</span>
<span class="c"># Created app pidgey in organization personal</span>
<span class="c"># Wrote config file fly.toml</span>
<span class="c"># ? Would you like to setup a Postgresql database now? No</span>
<span class="c"># ? Would you like to deploy now? Yes</span>
<span class="c"># Deploying pidgey</span>
</code></pre></div></div>

<p>The command asks a few questions:</p>
<ul>
  <li>What should the app be named?</li>
  <li>What organisation should it belong to?</li>
  <li>Which region should it be deployed to?</li>
  <li>Should a PostgreSQL database also be deployed?</li>
  <li>Should the app be deployed now?</li>
</ul>

<p>After this it builds the application using the docker file, and within a minute
the application is live! To open it in a web browser I run <code class="language-plaintext highlighter-rouge">flyctl open</code>.</p>

<p>To deploy future versions of the application I can run <code class="language-plaintext highlighter-rouge">flyctl deploy</code> after
saving my changes.</p>

<p>And that’s it. I’m really impressed with <a href="https://fly.io">Fly.io</a> and I expect
I’ll be using them to host all my Gleam web applications going forward. Give
them a try, I think you’ll like their platform.</p>

<p>Happy hacking!</p>]]></content><author><name></name></author><category term="Gleam" /><category term="deployment" /><summary type="html"><![CDATA[The recently released Gleam v0.22 includes a new command to prepare a Gleam application for deployment. With this in mind I thought it would be a good time to show how deploy Gleam into production with my new favourite platform, Fly.io.]]></summary></entry><entry><title type="html">Test driving fly.io with Gleam</title><link href="https://lpil.uk//blog/test-driving-fly-io-with-gleam/" rel="alternate" type="text/html" title="Test driving fly.io with Gleam" /><published>2022-01-13T00:00:00+00:00</published><updated>2022-01-13T00:00:00+00:00</updated><id>https://lpil.uk//blog/test-driving-fly-io-with-gleam</id><content type="html" xml:base="https://lpil.uk//blog/test-driving-fly-io-with-gleam/"><![CDATA[<p>So I’ve been gearing up to write some guides on how to deploy <a href="https://gleam.run/">Gleam</a> web
applications, but I wasn’t sure what platform I should use.</p>

<p>Big clouds like AWS and GCP are powerful but are also intimidating, and they
provide many exciting new ways to blow your feet off and empty your bank account
if you make a mistake.</p>

<p>I’m rather fond of more straightforward virtual server providers such as
DigitalOcean and Linode, but setting up a deployment with just a Linux box and
an SSH connection is a bit involved. I suspect most people don’t enjoy wrangling
hacky little deployment scripts as much as I do.</p>

<p>This leaves us with the various different platform as a service offerings which
promise to automated the entire deployment process. In the past I’ve used
Heroku, Render, DigitalOcean Apps, and a few others that no longer exist, but
none of them really stand out to me as the obvious choice. I need to try some
more.</p>

<p>Rather serendipitously <a href="https://fly.io/">fly.io</a> (a nifty looking platform as a service)
has just started sponsoring Gleam, so that seems like a good place to start. In
this blog I’ll take the tiny Gleam <a href="https://github.com/example-echo-server">echo server</a> example project and see
how quickly I can get it running on fly.io. As I write this I’ve had a quick
scan over the website, but otherwise I’m completely new to the platform.</p>

<p>Note- While I picked fly.io first because they started sponsoring Gleam, they’re
not paying me to write this and I’m going to be honest with my experience.
Although if it’s really bad I’ll probably just quietly delete this post rather
than publishing it… But then you are reading this in the future after it has
been published, so I guess it went ok!</p>

<p>Alright, let’s go.</p>

<h2 id="getting-started">Getting started</h2>

<p>The homepage has a big “Get Started” button that takes me to a page showing me
how to install their CLI and register with it. I’m using a mac computer here,
but they’ve similar looking commands for Windows and Linux too.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>superfly/tap/flyctl
flyctl auth signup <span class="c"># This opens a browser tab...</span>
</code></pre></div></div>

<p>I opt for GitHub single sign on (rather than username/password), it
authenticates the CLI, and the browser tab takes me to a documentation page with
links a few different first-steps options. One is “introduction”, which offers
more background on the platform. One is “the speedrun”, for folks who are
familiar with docker etc and wish to get going quickly, and one is a more “hands
on” piece of step by step learning.</p>

<p>This choose-your-own adventure style onboarding seems rather clever, they don’t
have to try and write one tutorial that works for everybody. I might have to
keep this in mind when working on Gleam’s onboarding.</p>

<p>Right. I want to be a speedrunner. let’s see how that goes.</p>

<h2 id="click"><em>click</em></h2>

<p>Whoa, they were not joking when they said get going fast. This is a page with 2
lines of text, a list of 6 steps, and a rather lovely picture of a hot air
balloon. There’s not even a scroll-bar.</p>

<p>I’ve already done steps 1 and 2 (see above), and step 6 is “your app is now running”, so really it’s just 3 steps.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flyctl launch
flyctl info
flyctl open
</code></pre></div></div>

<p>Apparently if I’m familiar with docker this’ll be enough. I’m a little
skeptical. Let’s see how I can mess this up.</p>

<p>I’ll start by adding a Dockerfile</p>

<p>I run <code class="language-plaintext highlighter-rouge">flyctl launch</code> and up pops a little multiple-choice quiz. It’s quite
slick for a terminal program. It also prints this:</p>

<blockquote>
  <p>Could not find a Dockerfile, nor detect a runtime or framework from source
code. Continuing with a blank app.</p>
</blockquote>

<p>Gleam’s too new to have built-in support, so I better <code class="language-plaintext highlighter-rouge">ctrl-c</code> this quiz and add
a Dockerfile.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> ghcr.io/gleam-lang/gleam:v0.19.0-erlang-alpine</span>

<span class="c"># Create a group and user to run as</span>
<span class="k">RUN </span>addgroup <span class="nt">-S</span> echogroup <span class="o">&amp;&amp;</span> adduser <span class="nt">-S</span> echouser <span class="nt">-G</span> echogroup
<span class="k">USER</span><span class="s"> echouser</span>

<span class="c"># Add app code</span>
<span class="k">WORKDIR</span><span class="s"> /app/</span>
<span class="k">COPY</span><span class="s"> . ./</span>

<span class="c"># Compile the app and set the entrypoint</span>
<span class="k">RUN </span>gleam build
<span class="k">CMD</span><span class="s"> ["gleam", "run"]</span>
</code></pre></div></div>

<p>That’ll do. It’s only a tiny app so no need to worry about caching or other
fancier docker features.</p>

<p>On with the <code class="language-plaintext highlighter-rouge">flyctl launch</code> quiz.</p>

<ol>
  <li>Application name. “gleam-echo” will do.</li>
  <li>Region. I pick London.</li>
</ol>

<blockquote>
  <p><strong>ERROR</strong> We need your payment information to continue!</p>
</blockquote>

<p>That’s a reasonable requirement, but I feel that could have been mentioned
earlier, and not with a sudden bright red error message.</p>

<p>I click the link in the error message, put my card details in the Stripe form
that opens in a browser window, and try <code class="language-plaintext highlighter-rouge">flyctl launch</code> again.</p>

<ol>
  <li>Application name. “gleam-echo”.</li>
  <li>Region. London.</li>
  <li>Would you like to deploy now? You bet I do.</li>
</ol>

<p>After a few seconds waiting it connects to a server somewhere and starts
building the OCI image using the Dockerfile. I’m impressed! I was sure it was
going to build locally and crash as it’s an amd64 image and I’m using an ARM
computer.</p>

<p>It pushes the image to their image registry, deploys the app instance, and waits
for health checks to pass. It seems to be hanging forever…</p>

<p>I guess the command <code class="language-plaintext highlighter-rouge">flyctl logs</code> and it works, I get the app logs, and I can
see the application has started. That’s good, it’s not crashing, that makes me
think it’s a configuration issue.</p>

<p>I open the documentation site and spot a page in the sidebar called “Run a Go
App”. Go is easy to read, so hopefully I can spot anything I’m lacking there. Near the top of the page they have the source code for the tiny Go app.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"embed"</span>
    <span class="s">"html/template"</span>
    <span class="s">"log"</span>
    <span class="s">"net/http"</span>
    <span class="s">"os"</span>
<span class="p">)</span>

<span class="c">//go:embed templates/*</span>
<span class="k">var</span> <span class="n">resources</span> <span class="n">embed</span><span class="o">.</span><span class="n">FS</span>

<span class="k">var</span> <span class="n">t</span> <span class="o">=</span> <span class="n">template</span><span class="o">.</span><span class="n">Must</span><span class="p">(</span><span class="n">template</span><span class="o">.</span><span class="n">ParseFS</span><span class="p">(</span><span class="n">resources</span><span class="p">,</span> <span class="s">"templates/*"</span><span class="p">))</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">port</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"PORT"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">port</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
        <span class="n">port</span> <span class="o">=</span> <span class="s">"8080"</span>
    <span class="p">}</span>

    <span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="k">func</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">data</span> <span class="o">:=</span> <span class="k">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span>
            <span class="s">"Region"</span><span class="o">:</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"FLY_REGION"</span><span class="p">),</span>
        <span class="p">}</span>

        <span class="n">t</span><span class="o">.</span><span class="n">ExecuteTemplate</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"index.html.tmpl"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
    <span class="p">})</span>

    <span class="n">log</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"listening on"</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
    <span class="n">log</span><span class="o">.</span><span class="n">Fatal</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="s">":"</span><span class="o">+</span><span class="n">port</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Can you see what I see?</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">port</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"PORT"</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">port</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
      <span class="n">port</span> <span class="o">=</span> <span class="s">"8080"</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>My Gleam app doesn’t read the <code class="language-plaintext highlighter-rouge">PORT</code> environment variable. I better add that.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">port</span> <span class="o">=</span>
  <span class="nx">os</span><span class="p">.</span><span class="nx">get_env</span><span class="p">(</span><span class="dl">"</span><span class="s2">PORT</span><span class="dl">"</span><span class="p">)</span>
  <span class="o">|&gt;</span> <span class="nx">result</span><span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">int</span><span class="p">.</span><span class="nx">parse</span><span class="p">)</span>
  <span class="o">|&gt;</span> <span class="nx">result</span><span class="p">.</span><span class="nx">unwrap</span><span class="p">(</span><span class="mi">3000</span><span class="p">)</span>
</code></pre></div></div>

<p>Nope, no dice. It still hangs, and in the logs the app prints that it is using
the same port 3000 as before, so that variable must not have been set.</p>

<p>I open the <code class="language-plaintext highlighter-rouge">fly.toml</code> file which has been written into the project by the
<code class="language-plaintext highlighter-rouge">flyctl launch</code> command. It contains about 40 lines of configuration, including this:</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[[services]]</span>
  <span class="py">http_checks</span> <span class="p">=</span> <span class="p">[]</span>
  <span class="py">internal_port</span> <span class="p">=</span> <span class="mi">8080</span> <span class="c"># &lt;-- Aha!</span>
  <span class="py">processes</span> <span class="p">=</span> <span class="nn">["app"]</span>
  <span class="py">protocol</span> <span class="p">=</span> <span class="s">"tcp"</span>
  <span class="py">script_checks</span> <span class="p">=</span> <span class="p">[]</span>
</code></pre></div></div>

<p>This time it works! 52 seconds from start to deployment complete, including the
time it took to build and upload the the OCI image. That’s a lot faster than my
experiments with Heroku and DigitalOcean Apps.</p>

<p>I run steps 5 and 6 from the speedrun guide.</p>

<p><code class="language-plaintext highlighter-rouge">flyctl info</code> prints some basic information about the deployment. Release
number, hostname, ports exposed, that sort of thing. Potentially useful but
nothing overly exciting.</p>

<p>Running <code class="language-plaintext highlighter-rouge">flyctl open</code> opens a new browser tab with the app running. Pretty cool!
And it feels very snappy and has TLS too on the hostname <code class="language-plaintext highlighter-rouge">gleam-echo.fly.dev</code>.</p>

<p>Lastly let’s teardown the application. In the output of <code class="language-plaintext highlighter-rouge">flyctl --help</code> I spot a
<code class="language-plaintext highlighter-rouge">destroy</code> command, and running <code class="language-plaintext highlighter-rouge">flyctl destroy gleam-echo</code> deletes the
deployment (after a quick “are you sure?” check).</p>

<h2 id="first-impressions">First impressions</h2>

<p>I’m impressed! There’s clearly some bits that are not as slick as they could
be, but the level of polish otherwise gives me confidence that any rough patches
will be worked out over time.</p>

<p>Even with the port problem it didn’t take me long to get it live, and I’ve
certainly been more stressed trying to get an application running on Heroku
using docker.</p>

<p>If I were to suggest some improvements they would be:</p>

<ol>
  <li>Mention at the start of the speedrun guide that you need to have a dockerfile
or to be using one of the supported languages.</li>
  <li>Ask for the credit card information earlier in the process rather than when
you think the application is about to deploy, and don’t ask for it with big
red ERROR text.</li>
  <li>Ask what port the application is listening on during the <code class="language-plaintext highlighter-rouge">flyctl launch</code> quiz.</li>
</ol>

<p>I think I’m going to have to test out fly.io a lot more ✨</p>

<h2 id="postscript-hitting-a-bug">Postscript: hitting a bug</h2>

<p>For fun I tried the same thing again on a Linux box, but ran into a
show-stopping bug where <code class="language-plaintext highlighter-rouge">flyctl launch</code> couldn’t connect to the build server. I
think this is because the generated hostname of this Linux box I’m using has an
underscore in it. I’ve reported this to fly.io, we’ll see how quickly it gets
resolved.</p>]]></content><author><name></name></author><category term="Gleam" /><category term="deployment" /><summary type="html"><![CDATA[So I’ve been gearing up to write some guides on how to deploy Gleam web applications, but I wasn’t sure what platform I should use.]]></summary></entry><entry><title type="html">Gleam v0.11 released!</title><link href="https://lpil.uk//blog/gleam-v0.11-released/" rel="alternate" type="text/html" title="Gleam v0.11 released!" /><published>2020-08-28T00:00:00+00:00</published><updated>2020-08-28T00:00:00+00:00</updated><id>https://lpil.uk//blog/gleam-v0.11-released</id><content type="html" xml:base="https://lpil.uk//blog/gleam-v0.11-released/"><![CDATA[<p>It’s time for another Gleam release! This time as well as taking a look at
what’s new in the compiler, we’re going to take a look at what’s new in the
wider Gleam ecosystem too.</p>

<h2 id="record-update-syntax">Record update syntax</h2>

<p>Previously when we wanted to update some of the fields on the record we would
need to create a new instance and manually copy across the fields that have
not changed.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">level_up</span><span class="p">(</span><span class="n">pokemon</span><span class="p">:</span> <span class="n">Pokemon</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">new_level</span> <span class="o">=</span> <span class="n">pokemon</span><span class="py">.level</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="k">let</span> <span class="n">new_moves</span> <span class="o">=</span> <span class="nf">moves_for</span><span class="p">(</span><span class="n">pokemon</span><span class="py">.species</span><span class="p">,</span> <span class="n">new_level</span><span class="p">)</span>
  <span class="nf">Pokemon</span><span class="p">(</span>
    <span class="n">level</span><span class="p">:</span> <span class="n">new_level</span><span class="p">,</span>
    <span class="n">moves</span><span class="p">:</span> <span class="n">new_moves</span><span class="p">,</span>
    <span class="n">name</span><span class="p">:</span> <span class="n">pokemon</span><span class="py">.name</span><span class="p">,</span>
    <span class="n">species</span><span class="p">:</span> <span class="n">pokemon</span><span class="py">.species</span><span class="p">,</span>
    <span class="n">item</span><span class="p">:</span> <span class="n">pokemon</span><span class="py">.item</span><span class="p">,</span>
  <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is quite verbose! Worse, it’s error prone and annoying to type every
time.</p>

<p>To remedy this problem <a href="https://github.com/QuinnWilton">Quinn Wilton</a> has
added the record update syntax to Gleam, so now we only need to specify the
fields that change.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">level_up</span><span class="p">(</span><span class="n">pokemon</span><span class="p">:</span> <span class="n">Pokemon</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">new_level</span> <span class="o">=</span> <span class="n">pokemon</span><span class="py">.level</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="k">let</span> <span class="n">new_moves</span> <span class="o">=</span> <span class="nf">moves_for</span><span class="p">(</span><span class="n">pokemon</span><span class="py">.species</span><span class="p">,</span> <span class="n">new_level</span><span class="p">)</span>
  <span class="nf">Pokemon</span><span class="p">(</span><span class="o">..</span><span class="n">pokemon</span><span class="p">,</span> <span class="n">level</span><span class="p">:</span> <span class="n">new_level</span><span class="p">,</span> <span class="n">moves</span><span class="p">:</span> <span class="n">new_moves</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>All data in Gleam is immutable, so this syntax does not alter the values of
the existing record, instead it creates a new record with the original values
and the updated values.</p>

<h2 id="numbers">Numbers</h2>

<p><a href="https://github.com/tomwhatmore">Tom Whatmore</a> has added some new features to
Gleam’s numbers.</p>

<p>There’s now syntaxes for binary, octal, and hexadecimal int literals.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 4 ways of writing the int 15</span>
<span class="k">let</span> <span class="n">hexadecimal</span> <span class="o">=</span> <span class="mi">0xF</span>
<span class="k">let</span> <span class="n">decimal</span> <span class="o">=</span> <span class="mi">15</span>
<span class="k">let</span> <span class="n">octal</span> <span class="o">=</span> <span class="mi">0o17</span>
<span class="k">let</span> <span class="n">binary</span> <span class="o">=</span> <span class="mi">0b00001111</span>
</code></pre></div></div>

<p>These might be handy in many situations such as when implementing
<a href="https://github.com/QuinnWilton/gleam-chip8">a virtual machine</a> or <a href="https://github.com/lpil/tetromino">a game of tetris</a> in Gleam.</p>

<p>Underscores can now be added to numbers, making larger numbers easier to read.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">billion</span> <span class="o">=</span> <span class="mi">1_000_000_000</span>
<span class="k">let</span> <span class="n">trillion_and_change</span> <span class="o">=</span> <span class="mf">1_000_000_000_000.57</span>
</code></pre></div></div>

<h2 id="type-holes">Type holes</h2>

<p>Type holes can be used to give partial annotations to values.</p>

<p>Here we’re saying that <code class="language-plaintext highlighter-rouge">x</code> is a List, but we’re not saying what type the list
contains, leaving the compiler to infer it.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nf">List</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="nf">run</span><span class="p">()</span>
</code></pre></div></div>

<p>This may be useful when adding annotations for documentation or to use a more
restrictive type than would be inferred by the compiler, as you can leave out
any parts of the annotation that are not important to you there.</p>

<h2 id="compiler-improvements">Compiler improvements</h2>

<p>The majority of the compiler work in this release has been improvements to
existing features.</p>

<p>The formatter style has been improved in several ways, and the performance of
the formatter has been improved. One popular change is to how it formats
assignments that don’t fit on a single line.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Before</span>
<span class="n">assert</span> <span class="nf">Ok</span><span class="p">(</span>
  <span class="nf">tuple</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">session</span><span class="p">),</span>
<span class="p">)</span> <span class="o">=</span> <span class="n">application_registry</span><span class="nf">.get_session</span><span class="p">(</span><span class="n">request</span><span class="py">.user_id</span><span class="p">)</span>

<span class="c1">// Now</span>
<span class="n">assert</span> <span class="nf">Ok</span><span class="p">(</span><span class="nf">tuple</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">session</span><span class="p">))</span> <span class="o">=</span>
  <span class="n">application_registry</span><span class="nf">.get_session</span><span class="p">(</span><span class="n">request</span><span class="py">.user_id</span><span class="p">)</span>
</code></pre></div></div>

<p>Much better!</p>

<p>Additionally several error messages have been improved to include more
information on how to fix the problem, and a whole bunch of bug have been
squashed. Please see the
<a href="https://github.com/gleam-lang/gleam/blob/master/CHANGELOG.md">changelog</a> for
more detail.</p>

<h2 id="http">HTTP</h2>

<p>Outside of the compiler the big new thing in Gleam is the first release of
<a href="https://github.com/gleam-lang/http">Gleam HTTP</a>. Inspired by existing libraries such as Ruby’s Rack,
Elixir’s Raxx, and Haskell’s WAI, Gleam HTTP provides a single well-typed
interface for creating web applications and clients while keeping the
underlying HTTP client and server code modular.</p>

<p>In Gleam a HTTP service is a regular function that takes a HTTP request and
returns a HTTP response, where request and response are types defined by the
HTTP library.</p>

<h4 id="services">Services</h4>

<p>Here’s a simplistic service that echoes back any body sent to it.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span><span class="err">.</span><span class="p">{</span><span class="n">Request</span><span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">echo_service</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="nf">Request</span><span class="p">(</span><span class="n">_</span><span class="p">))</span> <span class="p">{</span>
  <span class="n">http</span><span class="nf">.response</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">http</span><span class="nf">.set_resp_body</span><span class="p">(</span><span class="n">request</span><span class="py">.body</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As a normal Gleam function it is easy to test this service, no special test
helpers or test server are required.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">should</span>
<span class="n">import</span> <span class="n">my_app</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">echo_test</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">response</span> <span class="o">=</span>
    <span class="n">http</span><span class="nf">.default_req</span><span class="p">()</span>
    <span class="p">|</span><span class="o">&gt;</span> <span class="n">http</span><span class="nf">.set_req_body</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
    <span class="p">|</span><span class="o">&gt;</span> <span class="n">my_app</span><span class="py">.service</span>

  <span class="n">response</span><span class="py">.status</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">should</span><span class="nf">.equal</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>

  <span class="n">response</span><span class="py">.body</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">should</span><span class="nf">.equal</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Gleam’s HTTP library also provides middleware, which can be used to add
additional functionality to a HTTP service.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">bit_builder</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span><span class="o">/</span><span class="n">middleware</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">service</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">my_app</span><span class="py">.echo_service</span>
  <span class="c1">// Add a header</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">middleware</span><span class="nf">.prepend_resp_header</span><span class="p">(</span><span class="s">"made-with"</span><span class="p">,</span> <span class="s">"Gleam"</span><span class="p">)</span>
  <span class="c1">// Convert the response body type</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">middleware</span><span class="nf">.map_resp_body</span><span class="p">(</span><span class="n">bit_builder</span><span class="py">.from_bit_string</span><span class="p">)</span>
  <span class="c1">// Support PATCH, PUT, and DELETE from browsers</span>
  <span class="p">|</span><span class="o">&gt;</span> <span class="n">middleware</span><span class="py">.method_override</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="running-a-service">Running a service</h4>

<p>Once we have defined a service we want to run the service with a web server so
that it can handle HTTP requests from the outside world. To do this we use a
HTTP server adapter, such as one for the <a href="https://github.com/elli-lib/elli">Elli Erlang web server</a>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">my_app</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span><span class="o">/</span><span class="n">elli</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">elli</span><span class="nf">.start</span><span class="p">(</span><span class="n">my_app</span><span class="py">.service</span><span class="p">,</span> <span class="n">on_port</span><span class="p">:</span> <span class="mi">4000</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Or one for the <a href="https://github.com/ninenines/cowboy">Cowboy Erlang web server</a>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">my_app</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span><span class="o">/</span><span class="n">cowboy</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">cowboy</span><span class="nf">.start</span><span class="p">(</span><span class="n">my_app</span><span class="py">.service</span><span class="p">,</span> <span class="n">on_port</span><span class="p">:</span> <span class="mi">4000</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Or even using the adapter for the <a href="https://github.com/elixir-plug/plug">Elixir’s Plug interface</a>, so that a
Gleam HTTP service can be mounted within an Elixir Phoenix web application.</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">MyAppWeb</span><span class="o">.</span><span class="no">UserController</span> <span class="k">do</span>
  <span class="kn">use</span> <span class="no">MyAppWeb</span><span class="p">,</span> <span class="ss">:controller</span>

  <span class="k">def</span> <span class="n">show</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">conn</span>
    <span class="o">|&gt;</span> <span class="no">GleamPlug</span><span class="o">.</span><span class="n">call_service</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="o">&amp;</span><span class="ss">:my_app</span><span class="o">.</span><span class="n">service</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>A list of all available server adapters can be found in the <a href="https://github.com/gleam-lang/http/#server-adapters">Gleam HTTP
project’s README</a>.</p>

<p>If you would like to see more examples of Gleam HTTP being used to create a
web application see the <a href="https://github.com/gleam-lang/example-echo-server">echo server</a> on GitHub. It
includes all the above as well as other concepts such as routing and logging.</p>

<h4 id="http-clients">HTTP clients</h4>

<p>HTTP services are only half the story! We also can use the HTTP library to
make out own requests.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">http</span>
<span class="n">import</span> <span class="n">gleam</span><span class="o">/</span><span class="n">httpc</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">get_my_ip</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">try</span> <span class="n">response</span> <span class="o">=</span>
    <span class="n">http</span><span class="nf">.default_req</span><span class="p">()</span>
    <span class="p">|</span><span class="o">&gt;</span> <span class="n">http</span><span class="nf">.set_method</span><span class="p">(</span><span class="n">http</span><span class="py">.Get</span><span class="p">)</span>
    <span class="p">|</span><span class="o">&gt;</span> <span class="n">http</span><span class="nf">.set_host</span><span class="p">(</span><span class="s">"api.ipify.org"</span><span class="p">)</span>
    <span class="p">|</span><span class="o">&gt;</span> <span class="n">httpc</span><span class="py">.send</span>
  <span class="nf">Ok</span><span class="p">(</span><span class="n">response</span><span class="py">.body</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here we’ve build a HTTP request and sent it using the <code class="language-plaintext highlighter-rouge">httpc</code> client adapter,
returning the response body if the request is successful.</p>

<p>A list of all available client adapters can be found in the <a href="https://github.com/gleam-lang/http/#client-adapters">Gleam HTTP
project’s README</a>.</p>

<h2 id="try-it-out">Try it out</h2>

<p>If you want to try out the new version of Gleam head over to the <a href="https://gleam.run/getting-started/installing-gleam.html">installation
page</a>. I’d love to hear how you find it and get your feedback so
Gleam can continue to improve.</p>

<p>Want to view some existing Gleam projects? Head on over to the
<a href="https://github.com/gleam-lang/awesome-gleam">awesome-gleam</a> list. Looking for something to build in
Gleam? Check out <a href="https://github.com/gleam-lang/suggestions/issues">the suggestions tracker</a>.</p>

<h2 id="supporting-gleam">Supporting Gleam</h2>

<p>If you would like to help make strongly typed programming on the Erlang
virtual machine a production-ready reality please consider <strong><a href="https://github.com/sponsors/lpil">sponsoring
Gleam</a></strong> via the GitHub Sponsors program.</p>

<p>This release would not have been possible without the support of all the
people who have <a href="https://github.com/sponsors/lpil">sponsored</a> and contributed
to it, so a huge thank you to them.</p>

<p>Lastly, a huge thank you to the contributors to and sponsors of Gleam since
last release!</p>

<ul>
  <li><a href="https://github.com/adamnbowen">Adam Bowen</a></li>
  <li><a href="https://github.com/amokan">Adam Mokan</a></li>
  <li><a href="https://github.com/thehabbos007">Ahmad Sattar</a></li>
  <li><a href="https://github.com/scripttease">Al Dee</a></li>
  <li><a href="https://github.com/bees">Arian Daneshvar</a></li>
  <li><a href="https://github.com/arnodirlam">Arno Dirlam</a></li>
  <li><a href="https://github.com/benmyles">Ben Myles</a></li>
  <li><a href="https://github.com/bdusauso">Bruno Dusausoy</a></li>
  <li><a href="https://github.com/nono">Bruno Michel</a></li>
  <li><a href="https://github.com/starbelly">Bryan Paxton</a></li>
  <li><a href="https://github.com/worldofchris">Chris Young</a></li>
  <li><a href="https://github.com/tlvenn">Christian Meunier</a></li>
  <li><a href="https://github.com/xtian">Christian Wesselhoeft</a></li>
  <li><a href="https://github.com/cschembor3">Connor Schembor</a></li>
  <li><a href="https://github.com/davydog187">Dave Lucia</a></li>
  <li><a href="https://github.com/rawkode">David McKay</a></li>
  <li><a href="https://github.com/delucks">delucks</a></li>
  <li><a href="https://github.com/ericmj">Eric Meadows-Jönsson</a></li>
  <li><a href="https://github.com/eterps">Erik Terpstra</a></li>
  <li><a href="https://github.com/Gazler">Gary Rennie</a></li>
  <li><a href="https://github.com/obmarg">Graeme Coupar</a></li>
  <li><a href="https://github.com/ggpasqualino">Guilherme Pasqualino</a></li>
  <li><a href="https://github.com/hsnyildiz">Hasan YILDIZ</a></li>
  <li><a href="https://github.com/hendi">Hendrik Richter</a></li>
  <li><a href="https://github.com/hhandoko">Herdy Handoko</a></li>
  <li><a href="https://github.com/igagen">Ingmar Gagen</a></li>
  <li><a href="https://github.com/ivarvong">Ivar Vong</a></li>
  <li><a href="https://github.com/jamesmacaulay">James MacAulay</a></li>
  <li><a href="https://github.com/jechol">Jechol Lee</a></li>
  <li><a href="https://github.com/Jwsonic">John Palgut</a></li>
  <li><a href="https://github.com/josevalim">José Valim</a></li>
  <li><a href="https://github.com/cleverbunny">Clever Bunny LTD</a></li>
  <li><a href="https://github.com/kodeFant">Lars Lillo Ulvestad</a></li>
  <li><a href="https://github.com/lawik">Lars Wikman</a></li>
  <li><a href="https://github.com/leandrocp">Leandro Cesquini Pereira</a></li>
  <li><a href="https://github.com/mvellandi">Mario Vellandi</a></li>
  <li><a href="https://github.com/mario-mazo">mario</a></li>
  <li><a href="https://github.com/markusfeyh">Markus</a></li>
  <li><a href="https://github.com/MattCheely">Matthew Cheely</a></li>
  <li><a href="https://github.com/michaeljones">Michael Jones</a></li>
  <li><a href="https://github.com/mroach">Mike Roach</a></li>
  <li><a href="https://github.com/slashmili">Milad</a></li>
  <li><a href="https://github.com/ontofractal">ontofractal</a></li>
  <li><a href="https://github.com/sorentwo">Parker Selbert</a></li>
  <li><a href="https://github.com/CrowdHailer">Peter Saxton</a></li>
  <li><a href="https://github.com/QuinnWilton">Quinn Wilton</a></li>
  <li><a href="https://github.com/happysalada">Raphael Megzari</a></li>
  <li><a href="https://github.com/romatthe">Robin Mattheussen</a></li>
  <li><a href="https://github.com/samaaron">Sam Aaron</a></li>
  <li><a href="https://github.com/mrgleam">Santi Lertsumran</a></li>
  <li><a href="https://github.com/gootik">Sasan Hezarkhani</a></li>
  <li><a href="https://github.com/sascha-wolf">Sascha Wolf</a></li>
  <li><a href="https://github.com/sasa1977">Saša Jurić</a></li>
  <li><a href="https://github.com/seanjensengrey">Sean Jensen-Grey</a></li>
  <li><a href="https://github.com/sporto">Sebastian Porto</a></li>
  <li><a href="https://github.com/shanesveller">Shane Sveller</a></li>
  <li><a href="https://github.com/shritesh">Shritesh Bhattarai</a></li>
  <li><a href="https://github.com/simonewebdesign">Simone Vittori</a></li>
  <li><a href="https://github.com/timbuchwaldt">Tim Buchwaldt</a></li>
  <li><a href="https://github.com/tomwhatmore">Tom Whatmore</a></li>
  <li><a href="https://github.com/tsloughter">Tristan Sloughter</a></li>
  <li><a href="https://github.com/twilco">Tyler Wilcock</a></li>
  <li><a href="https://github.com/wojtekmach">Wojtek Mach</a></li>
</ul>

<p>Thanks for reading! Have fun! 💜</p>]]></content><author><name></name></author><category term="Languages" /><category term="Gleam" /><summary type="html"><![CDATA[It’s time for another Gleam release! This time as well as taking a look at what’s new in the compiler, we’re going to take a look at what’s new in the wider Gleam ecosystem too.]]></summary></entry><entry><title type="html">Gleam v0.10 released!</title><link href="https://lpil.uk//blog/gleam-v0.10-released/" rel="alternate" type="text/html" title="Gleam v0.10 released!" /><published>2020-07-01T00:00:00+00:00</published><updated>2020-07-01T00:00:00+00:00</updated><id>https://lpil.uk//blog/gleam-v0.10-released</id><content type="html" xml:base="https://lpil.uk//blog/gleam-v0.10-released/"><![CDATA[<p>1 month and 10,910 lines of code later it is time for another Gleam
release! Let’s see what’s new this time.</p>

<h2 id="module-constants">Module constants</h2>

<p>Sometimes we want to use a certain fixed value in multiple places in our
project. Until now we’ve had two options. The first option was to copy and
paste the value into multiple places in our code.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_before</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="n">year</span> <span class="o">&lt;</span> <span class="mi">2101</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_during</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="mi">2101</span> <span class="o">&lt;=</span> <span class="n">year</span> <span class="o">&amp;&amp;</span> <span class="n">year</span> <span class="o">&lt;=</span> <span class="mi">2111</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Duplication of values like this is error prone, especially if we want to
update the values later, as we’ll need to find every place it’s used in order
to change it.</p>

<p>Another option is to wrap the value in a function.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">start_year</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">Int</span> <span class="p">{</span>
  <span class="mi">2101</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">end_year</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">Int</span> <span class="p">{</span>
  <span class="mi">2111</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_before</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="n">year</span> <span class="o">&lt;</span> <span class="nf">start_year</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_during</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="nf">start_year</span><span class="p">()</span> <span class="o">&lt;=</span> <span class="n">year</span> <span class="o">&amp;&amp;</span> <span class="n">year</span> <span class="o">&lt;=</span> <span class="nf">end_year</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is better than copying the value in our code, but isn’t yet ideal. A
function can perform any amount of computation (with side effects!) when it is
called, so we have to read the definition of the function to be sure what it
does.</p>

<p>To make matters worse Gleam’s case clause guards don’t support calling of
functions within them, so this code will be rejected by the compiler:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="nf">describe</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
  <span class="n">case</span> <span class="n">year</span> <span class="p">{</span>
    <span class="n">year</span> <span class="k">if</span> <span class="n">year</span> <span class="o">&lt;</span> <span class="nf">start_year</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="s">"Before"</span>
    <span class="n">year</span> <span class="k">if</span> <span class="n">year</span> <span class="o">&gt;</span> <span class="nf">end_year</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="s">"After"</span>
    <span class="n">_</span> <span class="k">-&gt;</span> <span class="s">"During"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To solve all these problems Gleam now has module constants. They are always
inlined by the compiler, and can be used in case clause guards.</p>

<p>Using them the above code can be rewritten like so:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">const</span> <span class="n">start_year</span><span class="p">:</span> <span class="n">Int</span> <span class="o">=</span> <span class="mi">2101</span>
<span class="k">pub</span> <span class="k">const</span> <span class="n">end_year</span><span class="p">:</span> <span class="n">Int</span> <span class="o">=</span> <span class="mi">2111</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_before</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="n">year</span> <span class="o">&lt;</span> <span class="n">start_year</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">is_during</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
  <span class="n">start_year</span> <span class="o">&lt;=</span> <span class="n">year</span> <span class="o">&amp;&amp;</span> <span class="n">year</span> <span class="o">&lt;=</span> <span class="n">end_year</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="nf">describe</span><span class="p">(</span><span class="n">year</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
  <span class="n">case</span> <span class="n">year</span> <span class="p">{</span>
    <span class="n">year</span> <span class="k">if</span> <span class="n">year</span> <span class="o">&lt;</span> <span class="n">start_year</span> <span class="k">-&gt;</span> <span class="s">"Before"</span>
    <span class="n">year</span> <span class="k">if</span> <span class="n">year</span> <span class="o">&gt;</span> <span class="n">end_year</span> <span class="k">-&gt;</span> <span class="s">"After"</span>
    <span class="n">_</span> <span class="k">-&gt;</span> <span class="s">"During"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Much better! Module constants are going to provide the basis of some exciting
new features in future, so watch this space. 🚀</p>

<p>Thanks to <a href="https://github.com/thehabbos007">Ahmad Sattar</a> for taking the
lead in the implementation of this feature.</p>

<h2 id="bit-string-syntax">Bit string syntax</h2>

<p>One of great things about Erlang is how easy it is to work with raw bits and
bytes using the bit string literal syntax. Starting with this release Gleam
supports this too!</p>

<p>Explaining all the things one can do with the bit string syntax would take
longer than I have now, but in short they give a declarative way of
constructing and parsing raw data of any format. What’s more the Erlang VM is
highly optimised for this, so bit syntax is highly efficient too!</p>

<p>For example, if I wanted to create a 32 bit unsigned little endian integer
with the value of 100 I could do so using bit syntax like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">my_integer</span> <span class="o">=</span> <span class="o">&lt;&lt;</span><span class="mi">100</span><span class="p">:</span><span class="n">unsigned</span><span class="o">-</span><span class="n">little</span><span class="o">-</span><span class="nb">int</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span><span class="o">&gt;&gt;</span>
</code></pre></div></div>

<p>Or if I wanted to extract the title, artist, album, year, and comment from an
MP3 music file’s ID3 tags I could pattern match on it like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">get_metadata</span><span class="p">(</span><span class="n">id3</span><span class="p">:</span> <span class="n">BitString</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nf">Result</span><span class="p">(</span><span class="n">Metadata</span><span class="p">,</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">case</span> <span class="n">id3</span> <span class="p">{</span>
    <span class="o">&lt;&lt;</span>
      <span class="s">"TAG"</span><span class="p">:</span><span class="n">utf8</span><span class="p">,</span>
      <span class="n">title</span><span class="p">:</span><span class="n">binary</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span>
      <span class="n">artist</span><span class="p">:</span><span class="n">binary</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span>
      <span class="n">album</span><span class="p">:</span><span class="n">binary</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span>
      <span class="n">year</span><span class="p">:</span><span class="n">binary</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span>
      <span class="n">comment</span><span class="p">:</span><span class="n">binary</span><span class="o">-</span><span class="nf">size</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span>
      <span class="n">_rest</span><span class="p">:</span><span class="n">binary</span><span class="p">,</span>
    <span class="o">&gt;&gt;</span> <span class="k">-&gt;</span> <span class="nf">Ok</span><span class="p">(</span><span class="nf">Metadata</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">artist</span><span class="p">,</span> <span class="n">album</span><span class="p">,</span> <span class="n">year</span><span class="p">,</span> <span class="n">comment</span><span class="p">))</span>

    <span class="n">_</span> <span class="k">-&gt;</span> <span class="nf">Error</span><span class="p">(</span><span class="s">"Not a valid ID3 tag"</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Thanks to <a href="https://benjamintan.io/blog/2014/06/10/elixir-bit-syntax-and-id3/">Benjamin Tan’s blog post</a>
for this example.</p>

<p>For another example of bit syntax in action check out this <a href="https://github.com/lpil/ecoji/"><em>very serious</em>
base64 encoding alternative</a> which converts arbitrary data into emojis!</p>

<p>As part of this new feature we have introduced <code class="language-plaintext highlighter-rouge">BitString</code> and <code class="language-plaintext highlighter-rouge">UtfCodepoint</code>
types into the prelude, and split the standard library <code class="language-plaintext highlighter-rouge">Iodata</code> type into
<a href="https://hexdocs.pm/gleam_stdlib/gleam/string_builder/"><code class="language-plaintext highlighter-rouge">StringBuilder</code></a> and <a href="https://hexdocs.pm/gleam_stdlib/gleam/bit_builder/"><code class="language-plaintext highlighter-rouge">BitBuilder</code></a> types, which
are for efficiently constructing strings and bit strings respectively.</p>

<p>Thanks to <a href="https://github.com/tomwhatmore">Tom Whatmore</a> for taking the
lead in the implementation of this feature.</p>

<h2 id="the-rest">The rest</h2>

<p>There are many more new additions, improvements, and bug fixes to the compiler and
the standard library, including but not limited to support for regex expressions
(implemented by <a href="https://github.com/eterps/">Erik Terpstra</a>) and environment
variables (implemented by <a href="https://github.com/crowdhailer/">Peter Saxton</a>).</p>

<p>For information on the rest in this release please check out the <a href="https://github.com/gleam-lang/gleam/blob/main/CHANGELOG.md#v0100---2020-07-01">Gleam
changelog</a> and the <a href="https://github.com/gleam-lang/stdlib/blob/main/CHANGELOG.md#v0101---2020-07-01">standard library
changelog</a>.</p>

<h2 id="supporting-gleam">Supporting Gleam</h2>

<p>If you would like to help make strongly typed programming on the Erlang
virtual machine a production-ready reality please consider <strong><a href="https://github.com/sponsors/lpil">sponsoring
Gleam</a></strong> via the GitHub Sponsors program.</p>

<p>This release would not have been possible without the support of all the
people who have <a href="https://github.com/sponsors/lpil">sponsored</a> and contributed
to it, so a huge thank you to them.</p>

<ul>
  <li><a href="https://github.com/adelarsq">Adelar da Silva Queiróz</a></li>
  <li><a href="https://github.com/thehabbos007">Ahmad Sattar</a></li>
  <li><a href="https://github.com/scripttease">Al Dee</a></li>
  <li><a href="https://github.com/bees">Arian Daneshvar</a></li>
  <li><a href="https://github.com/arnodirlam">Arno Dirlam</a></li>
  <li><a href="https://github.com/benmyles">Ben Myles</a></li>
  <li><a href="https://github.com/bdusauso">Bruno Dusausoy</a></li>
  <li><a href="https://github.com/nono">Bruno Michel</a></li>
  <li><a href="https://github.com/starbelly">Bryan Paxton</a></li>
  <li><a href="https://github.com/worldofchris">Chris Young</a></li>
  <li><a href="https://github.com/tlvenn">Christian Meunier</a></li>
  <li><a href="https://github.com/xtian">Christian Wesselhoeft</a></li>
  <li><a href="https://github.com/cleverbunny">Clever Bunny LTD</a></li>
  <li><a href="https://github.com/davydog187">Dave Lucia</a></li>
  <li><a href="https://github.com/rawkode">David McKay</a></li>
  <li><a href="https://github.com/dym-sh">Dym Sohin</a></li>
  <li><a href="https://github.com/eterps">Erik Terpstra</a></li>
  <li><a href="https://github.com/namoscagnm">Gustavo Martins</a></li>
  <li><a href="https://github.com/hsnyildiz">Hasan YILDIZ</a></li>
  <li><a href="https://github.com/hendi">Hendrik Richter</a></li>
  <li><a href="https://github.com/hindenbug">hindenbug</a></li>
  <li><a href="https://github.com/Kleidukos">Hécate</a></li>
  <li><a href="https://github.com/jamesmacaulay">James MacAulay</a></li>
  <li><a href="https://github.com/badboy">Jan-Erik Rediger</a></li>
  <li><a href="https://github.com/Jwsonic">John Palgut</a></li>
  <li><a href="https://github.com/josevalim">José Valim</a></li>
  <li><a href="https://github.com/oneness">Kasim Tuman</a></li>
  <li><a href="https://github.com/lawik">Lars Wikman</a></li>
  <li><a href="https://github.com/leandrocp">Leandro Cesquini Pereira</a></li>
  <li><a href="https://github.com/mvellandi">Mario Vellandi</a></li>
  <li><a href="https://github.com/mario-mazo">mario</a></li>
  <li><a href="https://github.com/michaeljones">Michael Jones</a></li>
  <li><a href="https://github.com/mroach">Mike Roach</a></li>
  <li><a href="https://github.com/ontofractal">ontofractal</a></li>
  <li><a href="https://github.com/sorentwo">Parker Selbert</a></li>
  <li><a href="https://github.com/CrowdHailer">Peter Saxton</a></li>
  <li><a href="https://github.com/QuinnWilton">Quinn Wilton</a></li>
  <li><a href="https://github.com/romatthe">Robin Mattheussen</a></li>
  <li><a href="https://github.com/gootik">Sasan Hezarkhani</a></li>
  <li><a href="https://github.com/sascha-wolf">Sascha Wolf</a></li>
  <li><a href="https://github.com/scileo">Sasha</a></li>
  <li><a href="https://github.com/sasa1977">Saša Jurić</a></li>
  <li><a href="https://github.com/seanjensengrey">Sean Jensen-Grey</a></li>
  <li><a href="https://github.com/sporto">Sebastian Porto</a></li>
  <li><a href="https://github.com/MainShayne233">Shayne Tremblay</a></li>
  <li><a href="https://github.com/shritesh">Shritesh Bhattarai</a></li>
  <li><a href="https://github.com/simonewebdesign">Simone Vittori</a></li>
  <li><a href="https://github.com/tomwhatmore">Tom Whatmore</a></li>
  <li><a href="https://github.com/tsloughter">Tristan Sloughter</a></li>
  <li><a href="https://github.com/twilco">Tyler Wilcock</a></li>
  <li><a href="https://github.com/wojtekmach">Wojtek Mach</a></li>
</ul>

<p>Thanks for reading! Have fun! 💜</p>]]></content><author><name></name></author><category term="Languages" /><category term="Gleam" /><summary type="html"><![CDATA[Version v0.10 of the Gleam programming language has been released! Let's take a look at what's new.]]></summary></entry></feed>