<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<id>https://www.pinguinorodriguez.cl//</id>
	<title>JJA</title>
	<subtitle>I write code for fun, and some times get paid to do it</subtitle>
	<updated>2026-03-15T06:31:16+00:00</updated>
	<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/" />
	<link rel="alternate" type="application/rss+xml" href="https://www.pinguinorodriguez.cl/feed.xml" />
	<link rel="self" type="application/atom+xml" href="https://www.pinguinorodriguez.cl/atom.xml" />
	<author>
		<name>José Joaquín Atria</name>
		<email>jjatria@gmail.com</email>
	</author>
	<generator version="4.3.3">Jekyll</generator>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/autoinstrument-your-code-with-opentelemetry/</id>
		<title>Auto-instrument your code with OTel</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2025-12-16T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/autoinstrument-your-code-with-opentelemetry/" />
		<summary>A post from the Perl Advent Calendar showing what options are available to auto-instrument your code, why you might be interested, and what caveats come with it.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/autoinstrument-your-code-with-opentelemetry/">
			&lt;aside class=&quot;remark&quot;&gt;
  &lt;p&gt;This blog post was originally posted in the &lt;a href=&quot;https://perladvent.org/2025/2025-12-16.html&quot;&gt;Perl Advent Calendar 2025&lt;/a&gt;.
It is reposted here for posterity, but please use that URL if you need
a canonical one. Also: check out the rest of the posts while you’re at it!&lt;/p&gt;
&lt;/aside&gt;

&lt;figure class=&quot;graphic&quot;&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/autoinstrument-your-code-with-opentelemetry/hot-chocolate.jpg&quot; alt=&quot;
A close-up photo of a cup of hot chocolate
&quot; /&gt;&lt;/p&gt;
  &lt;figcaption&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.flickr.com/photos/fatty/2226622699&quot;&gt;“Hot Chocolate”&lt;/a&gt; by
&lt;a href=&quot;https://www.flickr.com/photos/fatty&quot;&gt;David Thompson&lt;/a&gt;,
&lt;a href=&quot;https://creativecommons.org/licenses/by-nc-sa/2.0/deed.en&quot;&gt;CC BY-NC-SA 2.0&lt;/a&gt;&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;A whole year had gone by since the elves at Santa’s workshop had started
using &lt;a href=&quot;https://metacpan.org/module/OpenTelemetry&quot;&gt;OpenTelemetry&lt;/a&gt; to collect and export telemetry from their multiple
services, and things had been going well. But in a meeting room deep under
the icy cover of the North Pole, trouble was brewing.&lt;/p&gt;

&lt;p&gt;“Well what do you suggest, then?”, asked Duende Juniorsson, the junior elf
in the team.&lt;/p&gt;

&lt;p&gt;“I don’t have a solution, I just know where the problem is”, answered Gnomo
Knullpointer perhaps a little more exasperated than he should be. “I know
that OpenTelemetry has been a game changer, and that we want to have more
of it. But instrumenting code has a cost, and I’m just wondering how much
longer we can keep paying it”.&lt;/p&gt;

&lt;p&gt;The elves had been relying on instrumentation libraries to generate telemetry
without having to make changes to their own codebase. This &lt;a href=&quot;https://opentelemetry.io/docs/concepts/instrumentation/zero-code&quot;&gt;“zero-code
instrumentation”&lt;/a&gt; had been the key selling point when they started using
OpenTelemetry. But it meant that they were limited either by the libraries
that were available, or by the resources they could dedicate to writing their
own.&lt;/p&gt;

&lt;p&gt;“If we want to instrument something, and there is no instrumentation
library for it, &lt;em&gt;and&lt;/em&gt; we don’t have the resources to write our own, then the
only other option is to instrument it manually”, said Duende.&lt;/p&gt;

&lt;p&gt;“Sure, but that is only marginally less work. And anything you touch will have
to be tested”, said Tess ‘t Moor, the QA elf. “So we might be saving time for
you developer elves, but only because I’ll be the one testing things”.&lt;/p&gt;

&lt;p&gt;There was a pause in the conversation, as each elf stopped to gather their
thoughts. Santa, who was sitting at one end of the table looking nervous, did
not like this. He did not like his elves getting upset and forgetting what
Christmas was all about, but more than anything, he did not like being in a
meeting where nobody was talking. It made him feel like maybe they were
waiting for &lt;em&gt;him&lt;/em&gt; to say something, and under pressure the only thing he could
ever think of saying was “Ho ho ho”. He was pretty sure this was not the right
time to say it.&lt;/p&gt;

&lt;p&gt;Ada Slashdóttir, the team’s senior elf, cleared her throat and absentmindedly
started tapping her cup of hot chocolate with a candy cane. “What if we get
Perl to write the instrumentation code for us?”.&lt;/p&gt;

&lt;h3 id=&quot;auto-auto-instrumentation&quot;&gt;Auto-auto-instrumentation&lt;/h3&gt;

&lt;p&gt;“Most OpenTelemetry instrumentation libraries look very similar”, she continued.
“They declare methods in a package that need to be instrumented, and then
monkey-patch them to generate the necessary telemetry.”&lt;/p&gt;

&lt;p&gt;“But not all telemetry is the same”, replied Gnomo. “OpenTelemetry specifies
which attributes need to be set in what kinds of spans: a span representing
an HTTP transaction needs to specify the URL it is for, one for a database
operation needs to specify what database it is for, etc. We cannot have
a one-size-fits-all approach”.&lt;/p&gt;

&lt;p&gt;“We can probably get 90% of the way with a generic approach”, continued Ada.
“And worry about the last 10% when we get there”.&lt;/p&gt;

&lt;p&gt;“Isn’t this making the problem worse?”, asked Duende. “A minute ago we didn’t
have the resources to write an instrumentation library, and now we are talking
about writing a library to write instrumentation libraries?”.&lt;/p&gt;

&lt;p&gt;“Ah, but we don’t need to write it ourselves”, said Ada. “We can use the new
&lt;a href=&quot;https://metacpan.org/pod/OpenTelemetry::Instrumentation::namespace&quot;&gt;OpenTelemetry::Instrumentation::namespace&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;The elves loaded up the documentation and dove in.&lt;/p&gt;

&lt;p&gt;Ada brought up her terminal. “Say you have some code that calls some package
to do something”:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UUID4::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Tiny&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“That will print out a UUID”, said Duende.&lt;/p&gt;

&lt;p&gt;“Exactly”, replied Ada. “And if we add OpenTelemetry?”.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Dump spans to STDERR in prettified JSON format&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_PERL_EXPORTER_CONSOLE_FORMAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;json,pretty=1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OpenTelemetry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SDK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UUID4::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Tiny&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“Nothing will change”, said Gnomo. “We loaded the API and initialised the SDK,
but since we are not generating any telemetry, nothing is different”.&lt;/p&gt;

&lt;p&gt;“Now let’s try loading the namespace instrumentation”, said Ada.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Dump spans to STDERR in prettified JSON format&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_PERL_EXPORTER_CONSOLE_FORMAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;json,pretty=1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OpenTelemetry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SDK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Instrumentation&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UUID4::Tiny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UUID4::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Tiny&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output of the script had changed! The UUID was still being printed at
the bottom of the screen, but above it were several lines with JSON encoded
OpenTelemetry spans. Slightly edited for brevity, the output looked a little
bit like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
   &quot;end_timestamp&quot; : 1765582438.54618,
   &quot;instrumentation_scope&quot; : {
      &quot;name&quot; : &quot;UUID4::Tiny&quot;,
      &quot;version&quot; : &quot;0.003&quot;
   },
   &quot;name&quot; : &quot;UUID4::Tiny::create_uuid&quot;,
   &quot;parent_span_id&quot; : &quot;dbed67eb5146795b&quot;,
   &quot;span_id&quot; : &quot;c2f03b363333f610&quot;,
   &quot;start_timestamp&quot; : 1765582438.54615,
   &quot;trace_id&quot; : &quot;386e98e4fa576fc5280e8da1f59d3031&quot;,
   ...
}
{
   &quot;end_timestamp&quot; : 1765582438.54669,
   &quot;instrumentation_scope&quot; : {
      &quot;name&quot; : &quot;UUID4::Tiny&quot;,
      &quot;version&quot; : &quot;0.003&quot;
   },
   &quot;name&quot; : &quot;UUID4::Tiny::is_uuid_string&quot;,
   &quot;parent_span_id&quot; : &quot;e266cf2b01725042&quot;,
   &quot;span_id&quot; : &quot;6165bab8a0b6f448&quot;,
   &quot;start_timestamp&quot; : 1765582438.54666,
   &quot;trace_id&quot; : &quot;386e98e4fa576fc5280e8da1f59d3031&quot;,
   ...
}
{
   &quot;end_timestamp&quot; : 1765582438.54678,
   &quot;instrumentation_scope&quot; : {
      &quot;name&quot; : &quot;UUID4::Tiny&quot;,
      &quot;version&quot; : &quot;0.003&quot;
   },
   &quot;name&quot; : &quot;UUID4::Tiny::uuid_to_string&quot;,
   &quot;parent_span_id&quot; : &quot;dbed67eb5146795b&quot;,
   &quot;span_id&quot; : &quot;e266cf2b01725042&quot;,
   &quot;start_timestamp&quot; : 1765582438.54654,
   &quot;trace_id&quot; : &quot;386e98e4fa576fc5280e8da1f59d3031&quot;,
   ...
}
{
   &quot;end_timestamp&quot; : 1765582438.54686,
   &quot;instrumentation_scope&quot; : {
      &quot;name&quot; : &quot;UUID4::Tiny&quot;,
      &quot;version&quot; : &quot;0.003&quot;
   },
   &quot;name&quot; : &quot;UUID4::Tiny::create_uuid_string&quot;,
   &quot;parent_span_id&quot; : &quot;0000000000000000&quot;,
   &quot;span_id&quot; : &quot;dbed67eb5146795b&quot;,
   &quot;start_timestamp&quot; : 1765582438.54598,
   &quot;trace_id&quot; : &quot;386e98e4fa576fc5280e8da1f59d3031&quot;,
   ...
}
4c9debb9-6370-4d7c-94f2-a5f1799475ce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“Hey, this is much more like it”, said Duende, who had got pretty familiar
with OpenTelemetry spans. “You can see that they all share a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trace_id&lt;/code&gt;,
and can track what span created which other span by looking at the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span_id&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parent_span_id&lt;/code&gt;”.&lt;/p&gt;

&lt;p&gt;“Yeah, and look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;s”, said Gnomo. “They are the fully-qualified
subroutine name of the code that executed. So you can tell that when we called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_uuid_string&lt;/code&gt; it called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uuid_to_string&lt;/code&gt;, which itself called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_uuid_string&lt;/code&gt;”.&lt;/p&gt;

&lt;p&gt;“And all of that without using a pre-made instrumentation library for
&lt;a href=&quot;https://metacpan.org/pod/UUID4::Tiny&quot;&gt;UUID4::Tiny&lt;/a&gt;, or manually instrumenting anything”, said Ada.&lt;/p&gt;

&lt;p&gt;“How does this even work?”, asked Duende after looking at the code a little
more carefully. “We load the instrumentation before loading the code to be
instrumented. How does it know what to instrument before we import it?”.&lt;/p&gt;

&lt;p&gt;“Good question!”, said Ada excitedly. “Let’s dive deeper.”&lt;/p&gt;

&lt;h3 id=&quot;looking-under-the-hood&quot;&gt;Looking under the hood&lt;/h3&gt;

&lt;p&gt;“When you load the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namespace&lt;/code&gt; instrumentation with a rule like the one we
gave it, it will look among the modules that have been loaded to see if any
match the one we want to instrument”, said Ada. “In this case, since we haven’t
loaded &lt;a href=&quot;https://metacpan.org/pod/UUID4::Tiny&quot;&gt;UUID4::Tiny&lt;/a&gt;, it won’t find it”.&lt;/p&gt;

&lt;p&gt;“So how does it know when to instrument it?”, asked Gnomo.&lt;/p&gt;

&lt;p&gt;“It uses a &lt;a href=&quot;https://perldoc.perl.org/functions/require&quot;&gt;require hook&lt;/a&gt; that will execute when a module of interest is loaded.
At that point, it installs the necessary instrumentation”, explained Ada.&lt;/p&gt;

&lt;p&gt;“Is that safe? Won’t that make everything slower?”, asked Duende.&lt;/p&gt;

&lt;p&gt;“To some extent, yes”, replied Ada. “Which is why this is not really made as a
substitute for writing instrumentation libraries. It is meant as a way to
facilitate the instrumentation of existing codebases, or as a stopgap measure
to use when exploring what to instrument. But it’s hard to predict the impact
in the abstract: we’ll always just have to benchmark things to see what the real
impact is, and decide whether it makes sense to pay the price”.&lt;/p&gt;

&lt;p&gt;“That seems reasonable, but why does it need the hook at all?”, asked Gnomo.
“Wouldn’t it be simpler to just make sure to load it after the import?”.&lt;/p&gt;

&lt;p&gt;“Ah, but there we hit another interesting caveat with this instrumentation”,
replied Ada. “Let’s try it: what happens if we do that?”. Ada modified the code
to look like this and re-ran it.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Dump spans to STDERR in prettified JSON format&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;OTEL_PERL_EXPORTER_CONSOLE_FORMAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;json,pretty=1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Import the function first, then instrument&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This probably won&apos;t do what you want!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UUID4::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Tiny&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OpenTelemetry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SDK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Instrumentation&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UUID4::Tiny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;create_uuid_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“It still works”, said Gnomo. “I can see the OpenTelemetry traces”.&lt;/p&gt;

&lt;p&gt;“But there’s one span missing!”, said Duende. “We lost the one for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_uuid_string&lt;/code&gt;”.&lt;/p&gt;

&lt;p&gt;“Precisely”, said Ada. “A case like this, where we load a package and import
a function from that namespace into ours, is very common. But if we load the
instrumentation &lt;em&gt;after&lt;/em&gt; we’ve imported it, when we execute the imported symbol
from our own namespace we’ll be running the uninstrumented code. To solve this
we have to instrument the code before importing it, but then we &lt;em&gt;have&lt;/em&gt; to use
the require hook”.&lt;/p&gt;

&lt;p&gt;“This suddenly feels very Perlish”, said Duende. “In that it is very powerful
but also a little risky”.&lt;/p&gt;

&lt;p&gt;“It’s all about taking managed risks”, replied Ada. “This instrumentation works
best when used in a targeted way, aimed at having the smallest impact possible
while still being useful. That’s why it tries to give you plenty of options to
control what gets instrumented and when: you can match packages with literal
strings or with regular expressions, and while we haven’t been doing it here,
you can also do the same for individual functions within any package of interest”.&lt;/p&gt;

&lt;p&gt;“Is that why the options are in an array reference?”, asked Duende.&lt;/p&gt;

&lt;p&gt;“Yes”, answered Ada. “The instrumentation will process the rules in order. You
can pass them in an array reference to control that order, or in a hash
reference if you don’t care about the order. But the array reference is more
predictable”.&lt;/p&gt;

&lt;h3 id=&quot;here-a-use-there-a-use-everywhere-a-yule-use&quot;&gt;Here a use, there a use, everywhere a yule use&lt;/h3&gt;

&lt;p&gt;“I can see how this would be useful”, chimed in Tess. “But I could see this
list of ‘rules’ getting unwieldy, if we need to have them all in the same
place and they include package and subroutine matchers, etc”.&lt;/p&gt;

&lt;p&gt;“Yes, that can become a problem”, agreed Ada. “So you don’t actually have to
have everything in the same place. You can load the instrumentation multiple
times with different rules, and each invocation will be independent. This is
particularly true of &lt;a href=&quot;https://metacpan.org/pod/OpenTelemetry::Instrumentation::namespace#Options&quot;&gt;&lt;em&gt;options&lt;/em&gt;&lt;/a&gt;, which can be passed together with the rules
to modify how that particular set of rules are interpreted, or to load the
rules from an external source.”&lt;/p&gt;

&lt;p&gt;“That’s useful”, said Gnomo. “But even then, if the code we are instrumenting
is our own code, it might be awkward to have the instrumentation live far away
from the code itself. In those cases, manual instrumentation might still be
better”.&lt;/p&gt;

&lt;p&gt;“Yeah, I agree. A small scope is always better”, replied Ada. “Luckily, there’s
a related instrumentation that you can use in those cases:
&lt;a href=&quot;https://metacpan.org/pod/OpenTelemetry::Instrumentation::caller&quot;&gt;OpenTelemetry::Instrumentation::caller&lt;/a&gt;. Let me show you how using that one
looks like”.&lt;/p&gt;

&lt;h3 id=&quot;the-instrumentation-is-coming-from-inside-the-package&quot;&gt;The instrumentation is coming from inside the package&lt;/h3&gt;

&lt;p&gt;“Say we have some utility package where we have some functions”, said Ada.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Santa::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Workshop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;allocate_gifts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_naughty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Sensitive data. Do not reveal!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump_naughty_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“If we want to auto-instrument this code”, said Ada, “but we don’t want (or
can’t) touch it, we can load the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;caller&lt;/code&gt; instrumentation. We just need to make
sure we load it after the functions have been defined”.&lt;/p&gt;

&lt;p&gt;“Does it also take the same options and rules as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namespace&lt;/code&gt; instrumentation?”,
asked Duende.&lt;/p&gt;

&lt;p&gt;“Yes”, said Ada, “but since we are already ‘inside’ a package, so to speak, we
skip the package-level matchers and go straight to the subroutine-level ones.”&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!perl&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Santa::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Workshop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;allocate_gifts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_naughty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Sensitive data. Do not reveal!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump_naughty_list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OpenTelemetry::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Instrumentation&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;dump_naughty_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Ignore sensitive function&lt;/span&gt;
    &lt;span class=&quot;sx&quot;&gt;qr/.*/&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Instrument the rest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h3&gt;

&lt;p&gt;The elves were happy to have at least a path forward. They understood that these
instrumentations were experimental, and that the best way to help was to use them
and see where they felt awkward. And that’s exactly what they aimed to do.&lt;/p&gt;

&lt;p&gt;It was another successful day at Santa’s workshop, making hard things possible,
and Santa was happy to see that his elves had once again managed to figure things
out by themselves. He was proud of his elves. Proud to see them grow and learn and
try new things. And proud as well to see that, even when deep in the weeds of some
technical problem, they knew that what mattered was what they were working towards.
The rest was just the tools for the job.&lt;/p&gt;

&lt;p&gt;Oh-oh. Santa had been lovingly staring at the elves for too long, and now they
were staring back, as if waiting for him to say something. But this time, he knew
exactly what to say.&lt;/p&gt;

&lt;p&gt;“Ho ho ho!”, laughed Santa, and everybody smiled.&lt;/p&gt;
		</content>
		<category term="perl" />
		<category term="advent" />
		<category term="tutorial" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/lpw2025/</id>
		<title>London Perl &amp; Raku Workshop 2025</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2025-12-02T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/lpw2025/" />
		<summary>My thoughts on this version of the London Perl &amp; Raku Workshop</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/lpw2025/">
			&lt;p&gt;This year’s &lt;a href=&quot;https://act.yapc.eu/lpw2025&quot;&gt;London Perl &amp;amp; Raku Workshop&lt;/a&gt; took place on 29 November,
a couple of days before I write these lines. I first attended this conference
in 2018 when I gave a talk about starting my life as a programmer, and I’ve
attended every iteration since then.&lt;/p&gt;

&lt;p&gt;But this year was special, because it was the first time I was in the organising
committee. And this was not just my first time organising this conference in
particular, but the first time I ever helped organise an event of this type at
all.&lt;/p&gt;

&lt;p&gt;I knew that organising a conference would not be easy, and indeed there are a
lot of things that I would now have done differently. There’s a lot to digest,
and I know my feelings will change as time passes and the dust settles. But I
wanted to leave here a collection of my fresh thoughts about the experience and
its impact, even if only for future reference.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The experience was a success&lt;/strong&gt;. It was hard, and there were times when I
would not have put my money on the event even taking place, but I’m glad that
we stuck with it and pushed forward regardless. I think the venue worked well,
and I think the post-event social was good, with plenty of attendees and a
comfortable room for ourselves.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The community that participates in this event is incredible&lt;/strong&gt;. We announced
the event late in the year, with less than a month’s head start, and were
floored by the number of talk submissions and the attendance. All in all, we
had 22 talks, 7 of which were lightning talks, from 18 different speakers.
There were 57 registered attendees all in all.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;While we &lt;em&gt;can&lt;/em&gt; have a successful conference despite the organising team’s
failings&lt;/strong&gt; (because the community is amazing)&lt;strong&gt;, we should not abuse this&lt;/strong&gt;.
I know there were people who care deeply about this community in general and
this event in particular who could not attend or did not have time to prepare
the talks they would have liked. I cannot apologise enough for this, and I
at least will try to avoid this in the future. We owe the community that much. 🙇&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Even in his absence, &lt;a href=&quot;https://metacpan.org/author/MSTROUT&quot;&gt;Matt Trout’s&lt;/a&gt; presence was clearly felt&lt;/strong&gt;.
He was explicitly present in &lt;a href=&quot;https://act.yapc.eu/lpw2025/talk/7994&quot;&gt;Stevan Little’s “YAARP” talk&lt;/a&gt; and in
several conversations during the social, but his contributions had a much
broader silent impact. We build on the shoulders of giants. Matt, you are
already missed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Remote talks are hard&lt;/strong&gt;. Not only for the obvious technical reasons, but also
because they are harder to manage as an organiser. They are good in principle,
because they can accommodate more of people’s varying circumstances. But even
when everything goes to plan it is difficult to make them feel like they fit
with the rest. I do not think this is a technology problem, and I’m not sure it
can be solved.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Swag is not that important&lt;/strong&gt;. Or at least not important enough for anyone
to mention to us that they wished they had received a hoodie or a scarf or
what have you. That said, I do feel that having some tangible reminder that
the event took place would have been nice.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;I missed having &lt;em&gt;some&lt;/em&gt; sort of catering&lt;/strong&gt;. It was awkward to have a “coffee
break” which meant “go outside and find yourselves some coffee”. And I don’t know
what to say: I like biscuits. I want biscuits.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, I am happy that I participated in this year’s event, and I look forward
to helping bring the next iteration of this event to fruition next year. More than
anything, I am happy to be able to help build this community.&lt;/p&gt;

&lt;p&gt;Thank you to &lt;a href=&quot;https://andrewjamesmehta.com&quot;&gt;Andrew Mehta&lt;/a&gt;, &lt;a href=&quot;https://leejo.github.io&quot;&gt;Lee Johnson&lt;/a&gt;,
&lt;a href=&quot;https://metacpan.org/author/SIMBABQUE&quot;&gt;Julien Fiegehenn&lt;/a&gt;, &lt;a href=&quot;https://metacpan.org/author/DAVIES&quot;&gt;John Davies&lt;/a&gt; and very specially to all of
those who were there. You made it all worth it.&lt;/p&gt;
		</content>
		<category term="perl" />
		<category term="raku" />
		<category term="conference" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/step-into-the-cookie-jar/</id>
		<title>Step into the cookie jar</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2024-05-23T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/step-into-the-cookie-jar/" />
		<summary>Adding cookie support to HTTP::Tiny and the distributions released along the way. A talk given at The Raku Conference in the Cloud 2022.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/step-into-the-cookie-jar/">
			&lt;aside class=&quot;remark&quot;&gt;
  &lt;p&gt;This post is based on the transcript of a talk given at The Raku Conference
in the Cloud on 7 September, 2022. This version has been tidied-up and
expanded in places, but you can watch &lt;a href=&quot;https://youtu.be/fgsAZCS_xoo&quot;&gt;a video of the original talk on
YouTube&lt;/a&gt; and follow along with &lt;a href=&quot;https://jjatria.gitlab.io/step-into-the-cookie-jar&quot;&gt;the talk slides&lt;/a&gt; if you prefer.&lt;/p&gt;
&lt;/aside&gt;

&lt;div class=&quot;center&quot;&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/fgsAZCS_xoo?si=XTpxigCvE99l1DAc&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;I’ve written my fair number of Raku distributions, but I think the one that
seems to be the most successful, in that it appears to have the most use, is
&lt;a href=&quot;https://raku.land/zef:jjatria/HTTP::Tiny&quot;&gt;HTTP::Tiny&lt;/a&gt;, a port of
&lt;a href=&quot;https://metacpan.org/pod/HTTP::Tiny&quot;&gt;the Perl library of the same name&lt;/a&gt;. This
was released a couple of years ago already, so you might be familiar with it.
You can read &lt;a href=&quot;/blog/introducing-http-tiny/&quot;&gt;the post where it was first announced&lt;/a&gt;,
but as a summary, here are some of the things that it can do:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;it’s a fully HTTP/1.1 compliant library&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;it has no dependencies, although some of its functionality (eg. HTTPS
support) depends on optional libraries&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;it supports both HTTP and HTTPS proxies&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;it supports streaming requests and responses, form uploads, ranged requests&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had a lot of fun working on it, and I learned loads while doing so as well.
But perhaps more importantly, it filled a niche that I think was missing, and
judging on its usage, I’m not the only one who thinks this.&lt;/p&gt;

&lt;p&gt;Overall, I’m pretty happy with how that one turned out&lt;/p&gt;

&lt;p&gt;However, for all the things it does support, there are some things that it
can’t do which I really want. And some of those, specifically timeouts and
cookie support, have been on the to-do list from the very beginning…&lt;/p&gt;

&lt;p&gt;Timeouts are missing because of some limitations with the handles that it uses
under the hood, so this will be a hard problem to crack. But from &lt;a href=&quot;https://raku.land/zef:jjatria/HTTP::Tiny&quot;&gt;HTTP::Tiny&lt;/a&gt;
v0.2.0, cookie support is no longer a limitation, and I wanted to talk a little
bit about how that came to be.&lt;/p&gt;

&lt;h2 id=&quot;a-cookie-primer&quot;&gt;A cookie primer&lt;/h2&gt;

&lt;p&gt;I’ve been thinking about HTTP cookies quite a bit while working on this, but I
know that not everybody has had the pleasure of reading the RFC. So just in
case, let’s do a very quick cookie primer.&lt;/p&gt;

&lt;p&gt;The core idea of HTTP cookies is to give the server the ability to store
state in the client. It’s a simple idea, but as you can imagine, it has all
sorts of ramifications and security implications, some of which we’ll discuss
below.&lt;/p&gt;

&lt;p&gt;They’ve been used for a long time. They started being used experimentally in
1994, and since then there’s been several RFCs that have specified what you
may call different “versions” of cookies: &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2109&quot;&gt;RFC 2109&lt;/a&gt; published in 1997,
&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2965&quot;&gt;RFC 2965&lt;/a&gt; published in 2000, and &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6265&quot;&gt;RFC 6265&lt;/a&gt; published in 2011, which is the
most current one (at the time of writing). The next version is currently
&lt;a href=&quot;https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html&quot;&gt;being drafted&lt;/a&gt; and has seen several revisions, so it’s very
likely that it will end up being published Real Soon Now.&lt;/p&gt;

&lt;p&gt;But since RFC 6265 is the one that is currently active, that’s the one that
I’m targeting and the one we’ll be focusing on.&lt;/p&gt;

&lt;p&gt;Their shape is very simple: it’s just a key/value pair with a set of
additional attributes. They’re sent by the server in a special header called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set-Cookie&lt;/code&gt; with the key and the value, followed by the cookie’s attributes.&lt;/p&gt;

&lt;p&gt;Every RFC so far has specified a list of recognised attributes with specific
meanings, but this list is constantly changing, with new attributes being
added and old ones being removed. But users have always used attributes to
extend the specification and to experiment with additional features. Sometimes
these become well-received and spread, becoming de-facto standards (like the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SameSite&lt;/code&gt; attribute, which has yet to be specified in any RFC).&lt;/p&gt;

&lt;p&gt;All of the attributes that are currently recognised by RFC 6265 are there to
govern what the scope of the cookie should be. Some examples of existing
attributes are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secure&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpOnly&lt;/code&gt;, which restrict the protocols of the cookie&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;, which restrict the URLs the cookie is applicable to&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Expires&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Max-Age&lt;/code&gt;, which restrict the lifespan of the cookie&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these attributes, the client determines what cookie is applicable
for a request, and then sends it back to the server in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookie&lt;/code&gt; header
(although the cookie that the client sends back to the server does not contain
any attributes, only the value).&lt;/p&gt;

&lt;h3 id=&quot;domains-and-paths&quot;&gt;Domains and paths&lt;/h3&gt;

&lt;p&gt;Back to the cookie attributes, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; attributes are
interesting because there’s some risks involved.&lt;/p&gt;

&lt;p&gt;Let’s take the following four cookies as an example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;A=1; Domain=    domain.com; Path=/
B=1; Domain=    domain.com; Path=/path
C=1; Domain=sub.domain.com; Path=/
D=1; Domain=sub.domain.com; Path=/path
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each of these cookies has a separate combination of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;, and
they will serve to illustrate how these attributes interact. The table below
illustrates which cookies the client can set (in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookie&lt;/code&gt; header) for any
given request, and which can be set by the server (in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set-Cookie&lt;/code&gt; header)
for the same requests.&lt;/p&gt;

&lt;figure&gt;
  &lt;table class=&quot;booktab&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Domain&lt;/th&gt;
        &lt;th&gt;Path&lt;/th&gt;
        &lt;th colspan=&quot;4&quot;&gt;Cookie&lt;/th&gt;
        &lt;th colspan=&quot;4&quot;&gt;Set-Cookie&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;domain.com&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;domain.com&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;&lt;code&gt;/path&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;sub.domain.com&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;C&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;C&lt;/td&gt;
        &lt;td&gt;D&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;sub.domain.com&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;&lt;code&gt;/path&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;C&lt;/td&gt;
        &lt;td&gt;D&lt;/td&gt;
        &lt;td&gt;A&lt;/td&gt;
        &lt;td&gt;B&lt;/td&gt;
        &lt;td&gt;C&lt;/td&gt;
        &lt;td&gt;D&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
  &lt;figcaption&gt;Cookies that the client and server can set for different requests&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;So, for example, with the cookies above, a client making a request to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain.com/&lt;/code&gt; can only send cookie A along with the request. All other cookies
either apply to a subdomain of the domain the request is for, or to a path that
is under the one that is being requested.&lt;/p&gt;

&lt;p&gt;On the other hand, a request to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub.domain.com/path&lt;/code&gt; can be sent by a client
with cookies A, B, C, and D, because they all apply to a domain that is either
the one being requested or to a domain that is above it, and to either the path
that is being requested or one above it.&lt;/p&gt;

&lt;p&gt;Note, however, that the rules that specify which cookies the client can send
with a given request are not the same as those that specify which cookies the
server can set for the same request.&lt;/p&gt;

&lt;p&gt;Taking the same requests as in the previous examples, in response to a
request to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain.com/&lt;/code&gt; the server can set cookies A and B, even though B
applies to a path that sits below the one that was requested. And the same goes
for a response to a request from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub.domain.com/path&lt;/code&gt;: in that case, the server
can set all four of the example cookies, including cookies that apply to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain.com&lt;/code&gt;, which sits above the one that was requested.&lt;/p&gt;

&lt;h3 id=&quot;supercookies&quot;&gt;Supercookies&lt;/h3&gt;

&lt;p&gt;And here’s where we bump into the first real security consideration around
cookies.&lt;/p&gt;

&lt;p&gt;If we go back to the table above, we can see that a cookie that applies to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain.com&lt;/code&gt; applies also to any domain &lt;em&gt;under&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain.com&lt;/code&gt;. But this means
that there’s technically nothing that stops you from setting a cookie with a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; set to plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com&lt;/code&gt;. And if you did, that cookie would technically
apply to any domain that ended with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because of their ability to apply so widely, any cookie with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; set to
a top-level domain is called a “supercookie”, and they present a risk that
needs to be accounted for.&lt;/p&gt;

&lt;p&gt;Before we get too far down the rabbit-hole, let’s put a pin on this and get
back to our main topic. We’ll come back to supercookies later.&lt;/p&gt;

&lt;h2 id=&quot;finding-a-cookie-jar&quot;&gt;Finding a cookie jar&lt;/h2&gt;

&lt;p&gt;Throughout the discussion of cookie applicability above I was careful to use
the word “can”: “the client &lt;em&gt;can&lt;/em&gt; send such-and-such cookie”. Why is that?
This is because cookies are entirely optional (indeed, this is why we can have
HTTP user agents that are compliant with HTTP/1.1 but do not support cookies,
like HTTP::Tiny).&lt;/p&gt;

&lt;p&gt;And even if the client supports cookies, there are good reasons why the client
can choose not to send them, or the server not to set them: for the client,
this requires cookies to be stored somewhere, which uses memory, and this may
be limited. And for the server, setting cookies requires sending data on the
request’s headers, which themselves have limits, and will have some overhead,
etc.&lt;/p&gt;

&lt;p&gt;The goal of this project was to create a library that would take on that
responsibility on the client side. It would retrieve cookies from a response,
determine whether they were being legitimately set by the server, find storage
space for them, and determine also which of the cookies that have been stored
are applicable to any new requests. This is traditionally called a “cookie
jar”.&lt;/p&gt;

&lt;p&gt;Furthermore, since the idea was for this was for it to be usable with
HTTP::Tiny, I wanted this cookie jar to follow the same principles as that
library: with minimal dependencies, and compliant with the specification, in
this case RFC 6265.&lt;/p&gt;

&lt;h3 id=&quot;prior-art&quot;&gt;Prior art&lt;/h3&gt;

&lt;p&gt;When I looked in the Raku ecosystem I could find two cookie jars:
HTTP::Cookies, which seems to be a port of a Perl library, and
Cro::HTTP::Client::CookieJar. But both of these had issues that made them
unattractive from my perspective.&lt;/p&gt;

&lt;h4 id=&quot;poor-interoperability&quot;&gt;Poor interoperability&lt;/h4&gt;

&lt;p&gt;The first issue was that both of these are actually distributed with an HTTP
client. This might not sound like a big problem, but remember the goal here is
to have a cookie jar that can be used with HTTP::Tiny, and that is a
distribution which is supposed to have no dependencies. If in order to use
cookies with it you need to install &lt;em&gt;another&lt;/em&gt; user agent, then the exercise
becomes a little pointless.&lt;/p&gt;

&lt;p&gt;A related issue is that, because they are distributed with these user agents,
they rely on those frameworks to work. For example, the HTTP::Cookies module
has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add-cookie-header&lt;/code&gt; method that will generate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookie&lt;/code&gt; header to send
with a request. But in order to use it, you need to give it an HTTP::Request
object, and the method will inject the header into the request directly,
there is no way to access that header without using that object.&lt;/p&gt;

&lt;p&gt;The Cro::HTTP::Client::CookieJar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add-to-request&lt;/code&gt; method has a similar
requirement, but this time using a &lt;em&gt;different&lt;/em&gt; class to represent a request:
this one needs a Cro::HTTP::Request object, as well as a Cro::Uri object.&lt;/p&gt;

&lt;p&gt;What this means is that in order to use these libraries, you &lt;em&gt;have&lt;/em&gt; to buy in
to the rest of their frameworks, which makes interoperability more difficult
(and makes them unsuitable for this particular project).&lt;/p&gt;

&lt;h4 id=&quot;non-compliant-behaviours&quot;&gt;Non-compliant behaviours&lt;/h4&gt;

&lt;p&gt;But even if I had decided that this was not a problem, or I had worked around
it with some wrapper code or something of that sort, none of these were as
compliant with RFC 6265 as I would have liked.&lt;/p&gt;

&lt;p&gt;Some of these were relatively minor. For example, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6265#section-5.4&quot;&gt;the spec specifies an
order&lt;/a&gt; in which the cookies should be serialised in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookie&lt;/code&gt;
header, but Cro::HTTP::Client::CookieJar &lt;a href=&quot;https://github.com/croservices/cro-http/issues/177&quot;&gt;does not follow this order&lt;/a&gt;. This is “relatively minor” because even though this goes
against the spec, this point is only a “should”, and the spec also states that
servers “should not” rely on the order of the cookies.&lt;/p&gt;

&lt;p&gt;But some of these can have more serious consequences. For example, HTTP::Cookies
&lt;a href=&quot;https://github.com/sergot/http-useragent/blob/531f6fb8d8ef56c190086e2d2da93a9ce2b7627d/lib/HTTP/Cookies.rakumod#L57&quot;&gt;does not do path restriction for cookies&lt;/a&gt;, and in this
case &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4&quot;&gt;this is a “must”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And although not in the spec, perhaps their most serious limitation in my eyes
was that neither of them had a way to protect the user against the
supercookies mentioned above (we’ll get back to this point later).&lt;/p&gt;

&lt;h3 id=&quot;introducing-cookiejar&quot;&gt;Introducing Cookie::Jar&lt;/h3&gt;

&lt;p&gt;Since none of the existing tools were suitable for my goals, this project
resulted in the release of a distribution called &lt;a href=&quot;https://raku.land/zef:jjatria/Cookie::Jar&quot;&gt;Cookie::Jar&lt;/a&gt;. In the spirit
of HTTP::Tiny, Cookie::Jar is a minimalist HTTP cookie jar which takes
inspiration from existing Perl libraries&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and is compliant with RFC 6265.
It does have one dependency on &lt;a href=&quot;https://raku.land/zef:raku-community-modules/IDNA::Punycode&quot;&gt;IDNA::Punycode&lt;/a&gt; to support international
domains, but it has no other functional dependencies, by which I mean that
that is the only distribution that it needs in order to function.&lt;/p&gt;

&lt;p&gt;Other than that, the main difference between this cookie jar and the ones
discussed above is that its interface relies only on Raku built-in types, to
make it usable in the largest possible set of contexts.&lt;/p&gt;

&lt;p&gt;But in order to protect against supercookies there is still one more problem
we need to solve.&lt;/p&gt;

&lt;h2 id=&quot;the-public-suffix-list&quot;&gt;The Public Suffix List&lt;/h2&gt;

&lt;p&gt;We said supercookies are cookies that have their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain&lt;/code&gt; attribute set to a
top-level domain. But this begs the questions: what is a top-level domain?&lt;/p&gt;

&lt;p&gt;Some of them are pretty easy to spot: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.com&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.org&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net&lt;/code&gt;. Those we all
know.&lt;/p&gt;

&lt;p&gt;In recent years, however, the list has grown significantly with the release
of newer top-level domains: we now have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pizza&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.香港&lt;/code&gt;. And new ones get
added all the time.&lt;/p&gt;

&lt;p&gt;More seriously, you cannot simply assume that the last component of the domain
will be top-level domain, since there are combined top-level domains, like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.co.uk&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ne.jp&lt;/code&gt; or even cases like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pvt.k12.ma.us&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I do not want to get into the discussion about how close to the top a domain
needs to be to be a “top-level domain”. Not only because it’s difficult, but
mainly because, luckily for us, that question has already been answered by
a thing called the &lt;a href=&quot;http://publicsuffix.org/&quot;&gt;Public Suffix List&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Public Suffix List is a project initially started by Mozilla, and
currently maintained by a community of volunteers who is constantly keeping an
eye on what new top-level domains appear. It’s available online, and it’s used
very widely by a large number of different projects.&lt;/p&gt;

&lt;p&gt;The list includes both domains that have been registered by &lt;a href=&quot;https://www.icann.org&quot;&gt;ICANN&lt;/a&gt;, as well
as “private” domains that have been submitted by certain domain owners who
want to treat their domains as a top-level domain. This includes cases like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.blogspot.com&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github.io&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.googleapis.com&lt;/code&gt; (at time of writing).&lt;/p&gt;

&lt;p&gt;This list has become &lt;em&gt;the&lt;/em&gt; way to protect against supercookies, so if I wanted
to be able to identify supercookies, I needed a way to get access to the list.&lt;/p&gt;

&lt;p&gt;Like with the cookie jar, I tried looking for prior art in the Raku ecosystem,
but surprisingly there was nothing. This explains why none of the other cookie
jars offer this feature.&lt;/p&gt;

&lt;h3 id=&quot;using-the-list&quot;&gt;Using the list&lt;/h3&gt;

&lt;p&gt;The list has three types of entries. There’s literal top-level domains, like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kumamoto.jp&lt;/code&gt;; patterns that match all domains under a given literal, like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.yokohama.jp&lt;/code&gt;; and exceptions that restrict those patterns, like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!city.yokohama.jp&lt;/code&gt;. Since the list has been designed to be machine-readable,
parsing it and using it is not actually the hardest part of this problem.&lt;/p&gt;

&lt;p&gt;The real challenge comes from the fact that this list is &lt;em&gt;constantly&lt;/em&gt; being
updated, sometimes several times per week. Which raised the question about how
a library that uses this list can be kept up to date.&lt;/p&gt;

&lt;p&gt;One possible solution would be to curate the changes, so that every month or
so I could look at the list and decide whether the most recent changes were
worthy of a release. However, I did not want to commit to that, and I also did
not want to take on the responsibility of making that decision.&lt;/p&gt;

&lt;p&gt;Another possibility would be to shift that responsibility to the user, and
either require or allow them to provide a list that the library can then use.
This is indeed the approach taken by some of the existing Perl libraries I
looked at as a reference. However, this meant that the list could not be
pre-compiled into the module, and had to be parsed every time the module was
loaded. This was something I was trying to avoid.&lt;/p&gt;

&lt;p&gt;The third possibility was an idea I got from &lt;a href=&quot;https://docs.rs/psl/latest/psl&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psl&lt;/code&gt; Rust library&lt;/a&gt;,
which &lt;a href=&quot;https://github.com/addr-rs/psl/blob/main/.github/workflows/update.yaml&quot;&gt;every day checks for changes&lt;/a&gt; in the upstream list, and
mints a new release if anything has changed. The first time I saw this I
thought it was madness, but this is actually the method I ended up going with.&lt;/p&gt;

&lt;h3 id=&quot;introducing-publicsuffix&quot;&gt;Introducing PublicSuffix&lt;/h3&gt;

&lt;p&gt;This resulted in a new distribution called &lt;a href=&quot;https://raku.land/zef:jjatria/PublicSuffix&quot;&gt;PublicSuffix&lt;/a&gt; which was released
very soon after &lt;a href=&quot;https://raku.land/zef:jjatria/Cookie::Jar&quot;&gt;Cookie::Jar&lt;/a&gt;. This is a very small distribution that parses
the list, stores it on compilation, and gets updated automatically whenever
a new version of the list is made available.&lt;/p&gt;

&lt;p&gt;It is not clear to me whether this is the best solution to this problem, but
it did mean that I could offer a good balance between allowing users to
control what version of the list they are on, while still supporting fast
execution times by compiling the list into the module&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. That said, I am open
to the idea of adding support for users providing their own lists if this
proves to be a wanted feature.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h2&gt;

&lt;p&gt;We’ve now delved several levels deep, so it’s a good idea to try to go back
to the surface and see how all these pieces fit together.&lt;/p&gt;

&lt;p&gt;The PublicSuffix module is now available on the Raku ecosystem, and can be
used on its own for whatever you think might benefit from it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;use PublicSuffix;

say public-suffix &apos;福.個人.香港&apos;;
# OUTPUT: 個人.香港

say public-suffix &apos;test.pvt.k12.ma.us&apos;;
# OUTPUT: pvt.k12.ma.us

say registrable-domain &apos;raku.land&apos;;
# OUTPUT: raku.land
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By importing it, it will export two functions. The main function is
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public-suffix&lt;/code&gt;, which takes a string representing a valid host and returns a
string with the top-level domain of that host, or the type object if the host
is an IP address. It will also export the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registrable-domain&lt;/code&gt; function, which
also takes a valid host as a string and returns the “registrable domain” of
the host, or &lt;a href=&quot;https://url.spec.whatwg.org/#host-miscellaneous&quot;&gt;“the host’s public suffix and the domain label preceding it, if
any”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Both of them support hosts in either ASCII “punycode” or in unicode, and they
will attempt to return a value in the same format that it was provided in.&lt;/p&gt;

&lt;p&gt;In order to use it with Cookie::Jar, all you have to do is have the module
installed. As long as is it is usable, Cookie::Jar will load it and use it
to reject any supercookies that may have been received from a response.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;use Cookie::Jar;

my $c = Cookie::Jar.new;
$c.add: &apos;https://foo.com&apos;, &apos;foo=123; Domain=foo.com&apos;;
$c.add: &apos;https://foo.com&apos;, &apos;bar=234&apos;;

# Rejected if PublicSuffix is available
$c.add: &apos;https://foo.com&apos;, &apos;super=1; Domain=com&apos;;

say &quot;{ .name } -&amp;gt; { .value }&quot;
    for $c.get: &apos;GET&apos;, &apos;https://foo.com&apos;;
# OUTPUT:
# foo -&amp;gt; 123
# bar -&amp;gt; 234

# The &apos;bar&apos; cookie does not apply to this subdomain
say $c.header: &apos;GET&apos;, &apos;https://www.foo.com&apos;;
# OUTPUT:
# foo=123
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As for the cookie jar itself, as you can see in the code snippet above, you
can add cookies with the URL that issues the response, and the value from the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set-Cookie&lt;/code&gt; header. The jar will know how to parse that value, it will
determine which cookies are valid and can be set by this response, and will
know where to store them for future use.&lt;/p&gt;

&lt;p&gt;To allow for inspection or for any extra processing you may want to do on the
cookies, you can retrieve them from the jar. In this case, however, you have
access to read-only versions of the cookies, to ensure that the cookies in the
jar remain the ones that were received from the server.&lt;/p&gt;

&lt;p&gt;Alternatively, you can give it the request method, and the URL the request is
for, and the jar will return a the value for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookie&lt;/code&gt; header that can be
sent along with that request.&lt;/p&gt;

&lt;p&gt;And coming all the way to the top, starting from HTTP::Tiny version 0.2.0 the
constructor accepts a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookie-jar&lt;/code&gt; parameter which can be set to an object
to use to process cookies. Importantly, the client does not internally check
that the cookie jar object is an instance of Cookie::Jar: as long as the class
of the cookie jar provides an interface that is compatible, HTTP::Tiny is
happy to use it&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;use HTTP::Tiny;
use Cookie::Jar;

my $cookie-jar = Cookie::Jar.new;
$cookie-jar.load: &apos;cookie.jar&apos; if &apos;cookie.jar&apos;.IO.e;

my $client = HTTP::Tiny.new: :$cookie-jar;

... # Client will use the cookie jar

$cookie-jar.save: &apos;cookie.jar&apos;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting thoughts&lt;/h2&gt;

&lt;p&gt;Before wrapping up, I thought it would be good to go over a couple of things
that this project brought to mind.&lt;/p&gt;

&lt;p&gt;The first is the now almost commonplace idea that Raku is “a young language
with a long youth”. A couple of times now, when starting to use the language
in earnest for a serious task, I bump into holes in surprising places. For a
specific example, when setting out to implement cookie support, I did not
expect to find no tool to check the public suffix list and to have to
implement my own. It caught me by surprise to have to go that low-level.&lt;/p&gt;

&lt;p&gt;The good thing, as I think has also been said by others before me, is that
Raku is very versatile, so the tools you need to fill in those holes are often
available. I find that experiences like this one have a value not only in
their specific outcomes, but also in the holes that get covered along the way.&lt;/p&gt;

&lt;p&gt;A related but different aspect is that even when the tools are available, it
is not always easy to find them, or to evaluate which of them are fit for
purpose. The process of finding existing cookie jars, and then realising why
they were not useful for my task, took a considerable amount of effort. And
there may be other cookie jars that I couldn’t find, which only makes this
more serious.&lt;/p&gt;

&lt;p&gt;I think the Raku ecosystem is good. I think there’s plenty of good in there,
but to a large extent &lt;em&gt;because&lt;/em&gt; of that “long youth”, there’s a lot of stuff
that we need to curate, and building the tools that allow us to find the real
gems in that ecosystem is going to be hard work, but very valuable.&lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Specially &lt;a href=&quot;https://metacpan.org/pod/HTTP::CookieJar&quot;&gt;HTTP::CookieJar&lt;/a&gt;,
  &lt;a href=&quot;https://metacpan.org/pod/HTTP::Cookies&quot;&gt;HTTP::Cookies&lt;/a&gt;, and
  &lt;a href=&quot;https://metacpan.org/pod/Mojo::UserAgent::CookieJar&quot;&gt;Mojo::UserAgent::CookieJar&lt;/a&gt;.
  My gratitude to the authors and maintainers of those distributions. 🙇 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;Note that this is just my expectation, and has not been verified by
  a benchmark. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="raku" />
		<category term="conference" />
		<category term="talk" />
		<category term="cookie-jar" />
		<category term="http-tiny" />
		<category term="public-suffix" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/santa-kotlin-is-coming-to-perl/</id>
		<title>Santa Kotlin is coming to Perl</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2023-12-24T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/santa-kotlin-is-coming-to-perl/" />
		<summary>A post from the Perl Advent Calendar with notes about what it takes to bind to Kotlin code from Perl using FFI::Platypus, and why this matters on a personal level.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/santa-kotlin-is-coming-to-perl/">
			&lt;aside class=&quot;remark&quot;&gt;
  &lt;p&gt;This blog post was originally posted in the &lt;a href=&quot;https://perladvent.org/2023/2023-12-24.html&quot;&gt;Perl Advent Calendar 2023&lt;/a&gt;.
It is reposted here for posterity, but please use that URL if you need
a canonical one. Also: check out the rest of the posts while you’re at it!&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;This post starts with a long introduction to explain why I’m writing, but if
you want to skip all of that you can jump directly to the meat and potatoes.&lt;/p&gt;

&lt;h2 id=&quot;stay-awhile-and-listen&quot;&gt;Stay awhile and listen&lt;/h2&gt;

&lt;p&gt;I grew up as a Nintendo kid. Some of the first video games I played were on an
NES, and the first console I ever owned was an SNES. I have very fond memories
of those times and of the games I got to know and play.&lt;/p&gt;

&lt;p&gt;Although at the time I don’t think I considered applying that label to myself,
it was very much a part of how I understood myself and my place among my
peers. I was a Nintendo kid, and they… well, they were something else. Sega
kids, maybe.&lt;/p&gt;

&lt;p&gt;This misguided sense of identity paired well with a misguided sense of loyalty,
which made it so I found it difficult to enjoy both at the same time. If I was
a “Nintendo” kid, what would it mean if I enjoyed Sega… things? What would
it mean to own one?&lt;/p&gt;

&lt;p&gt;This was not only related to video games, either. I remember very easily
falling into this trap with all sorts of similar “contrasts”. I was a Beatles
kid, so I couldn’t like The Rolling Stones. I was a Star Wars kid, so I
couldn’t enjoy Star Trek. The list was tragically endless.&lt;/p&gt;

&lt;p&gt;This was not so hard when my team was inarguably better than the other. But I
remember how hard it got to be a “Nintendo kid” when the PlayStation came
around. It was gritty, it was powerful, it was exciting… and it felt
inaccessible.&lt;/p&gt;

&lt;h2 id=&quot;am-i-still-reading-the-perl-advent-calendar&quot;&gt;Am I still reading the Perl advent calendar?&lt;/h2&gt;

&lt;p&gt;Ah, yes. Perl.&lt;/p&gt;

&lt;p&gt;I’ve always loved Perl. It was not my very first programming language (you and
me, BASIC, for life), but it was the first one where I felt like I could write
real programs. The first that I felt was worth mastering, and the one I’m most
comfortable with, even today.&lt;/p&gt;

&lt;p&gt;So, surprise surprise, I was a Perl kid.&lt;/p&gt;

&lt;p&gt;And there have been times when being a Perl kid has not been easy.&lt;/p&gt;

&lt;p&gt;I am fortunately past the time when I look at the world in terms of clubs that
you belong to because of the things you like. I will have you know I can
listen to both Radiohead and Coldplay without breaking a sweat (I take no
responsibility for deciding what contrasted with what).&lt;/p&gt;

&lt;p&gt;But to this day, there are aspects of this worldview that remain in me.&lt;/p&gt;

&lt;h2 id=&quot;perls-playstation&quot;&gt;Perl’s PlayStation&lt;/h2&gt;

&lt;p&gt;I imagine this largely depends on my particular interests, but for the Perl
kid in me, it was hard to see how easy the other kids had it when they wanted
to integrate with other languages.&lt;/p&gt;

&lt;p&gt;To me, this was the PlayStation to Perl’s Nintendo.&lt;/p&gt;

&lt;p&gt;I remember several attempts trying to teach my teenager-self how to write XS,
so I could bind to this or that library. I remember feeling frustrated and
defeated. I remember wondering if this meant that Perl was holding me back…&lt;/p&gt;

&lt;p&gt;The answer is “no”. If I was being held back, it was me who was doing so by
again thinking in terms of clubs.&lt;/p&gt;

&lt;p&gt;But even if I had continued to see the world through that lens, the Perl we
have at our disposal today is miles from the Perl I learned as a kid. There
are still, I am sure, plenty of areas where I think Perl has to catch up. But
we are at a moment where Perl is positively blooming with new features and
tools, that make catching up possible, if not outright easy.&lt;/p&gt;

&lt;p&gt;In the last two versions alone (at the time of writing, 5.36 and 5.38) we have
&lt;a href=&quot;https://perldoc.pl/perl5360delta#iterating-over-multiple-values-at-a-time-(experimental)&quot;&gt;n-at-a-time iteration&lt;/a&gt;, &lt;a href=&quot;https://perldoc.pl/perl5360delta#try/catch-can-now-have-a-finally-block-(experimental)&quot;&gt;a native try with finally support&lt;/a&gt; (finally!), &lt;a href=&quot;https://perldoc.pl/perl5360delta#defer-blocks-(experimental)&quot;&gt;the
new defer blocks&lt;/a&gt;, &lt;a href=&quot;https://perldoc.pl/perl5360delta#Stable-boolean-tracking&quot;&gt;native booleans&lt;/a&gt;, &lt;a href=&quot;https://perldoc.pl/perl5360delta#builtin-functions-(experimental)&quot;&gt;the new builtin namespace&lt;/a&gt;, and a
powerful &lt;a href=&quot;https://perldoc.pl/perl5380delta#New-class-Feature&quot;&gt;new syntax for defining classes&lt;/a&gt;. Not to mention other recent native
features (like sub signatures and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isa&lt;/code&gt; operator), or the things made
possible via CPAN: &lt;a href=&quot;https://metacpan.org/module/Future::AsyncAwait&quot;&gt;async/await support&lt;/a&gt;, the renewed efforts into &lt;a href=&quot;https://metacpan.org/module/PDL&quot;&gt;PDL&lt;/a&gt;, and
what I might consider the jewel of modern Perl: &lt;a href=&quot;https://metacpan.org/module/FFI::Platypus&quot;&gt;FFI::Platypus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Time will tell, but I feel like this is what it must feel like to live during
a renaissance.&lt;/p&gt;

&lt;h2 id=&quot;any-chance-of-having-actual-code-in-this-post&quot;&gt;Any chance of having actual code in this post?&lt;/h2&gt;

&lt;p&gt;Yes, I’m getting to that. Now that I’ve finished with the introduction we can
get to the meat and potatoes of this post. I hope I didn’t lose too many of
you along the way.&lt;/p&gt;

&lt;h2 id=&quot;binding-to-kotlin-from-perl&quot;&gt;Binding to Kotlin from Perl&lt;/h2&gt;

&lt;p&gt;What motivated this post in the first place was a task at work where I was
asked to look into the feasibility of integrating with a third-party that
provided SDKs for several languages… but not Perl.&lt;/p&gt;

&lt;p&gt;Lucky for me, they had made the code of those SDKs publicly available, so I
could examine it. And while looking through them I realised that most of the
heavy lifting was done by binding to a shared C library. My teenager-self
would have had a traumatic flashback sequence at this point, but this is
modern Perl. We have &lt;a href=&quot;https://metacpan.org/module/FFI::Platypus&quot;&gt;FFI::Platypus&lt;/a&gt;. “This will be easy”, I thought.&lt;/p&gt;

&lt;p&gt;The challenge came when I realised that the library was originally written in
Kotlin via what they know as “Kotlin/Native”, which &lt;a href=&quot;https://kotlinlang.org/docs/native-dynamic-libraries.html#generated-headers-file&quot;&gt;generates header files&lt;/a&gt;
with some ad-hoc hoops for us to jump through. As an attempt at simplifying
things, I’ve put together &lt;a href=&quot;https://github.com/jjatria/santa-kotlin&quot;&gt;a repository&lt;/a&gt; with a sort of sample distribution
that you can play around with as an illustration. The code examples below will
be taken from it.&lt;/p&gt;

&lt;p&gt;In any case, the native Kotlin extension will take code that looks like &lt;a href=&quot;https://github.com/jjatria/santa-kotlin/blob/91714e6a2928c54f738253537f1ee362bbc41b88/share/src/nativeMain/kotlin/example.kt&quot;&gt;this&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;example&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reverseString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and eventually wrap it in a C struct which will look like the one below:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;cm&quot;&gt;/* Service functions. */&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... Snipped 28 fields with fields pointing to service functions&lt;/span&gt;

  &lt;span class=&quot;cm&quot;&gt;/* User functions. */&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reverseString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kotlin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;libexample_ExportedSymbols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;libexample_ExportedSymbols&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;libexample_symbols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which, to summarise, is exposing a global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libexample_symbols&lt;/code&gt; function which
returns a pointer to a struct where the last field (named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kotlin&lt;/code&gt;) holds a
pointer to a struct with a field (named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;) which holds a pointer to a
struct with a field (named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example&lt;/code&gt;) which holds a pointer to the function
that you wrote.&lt;/p&gt;

&lt;p&gt;That’s a mouthful.&lt;/p&gt;

&lt;p&gt;When I first saw this, and saw that doing it in eg. Ruby (the SDK I was
looking at for guidance) was not only possible, but relatively simple-looking,
I got pangs of that PlayStation feeling.&lt;/p&gt;

&lt;p&gt;But as it turns out, &lt;a href=&quot;https://metacpan.org/module/FFI::Platypus&quot;&gt;FFI::Platypus&lt;/a&gt; already gives us all the tools to deal
with something like this.&lt;/p&gt;

&lt;p&gt;The first thing will be to define the nested structs, and for that we will
need &lt;a href=&quot;https://metacpan.org/module/FFI::C&quot;&gt;FFI::C&lt;/a&gt; (remember that you can look at &lt;a href=&quot;https://github.com/jjatria/santa-kotlin/blob/main/lib/Santa/Kotlin.pm&quot;&gt;the whole file&lt;/a&gt; these snippets
are taken from &lt;a href=&quot;https://github.com/jjatria/santa-kotlin&quot;&gt;in the sample repository&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Santa::Kotlin::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;FFI::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;reverseString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;opaque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Santa::Kotlin::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;FFI::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Santa::Kotlin::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Kotlin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;FFI::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kotlin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;package&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Santa::Kotlin::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Symbols&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nn&quot;&gt;FFI::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Symbols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ... 28 skipped fields which we must have here too ...&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;kotlin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Kotlin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These packages are only for internal use, so that’s why they have a newline
after the package keyword: it makes it so that if this code is ever put on
CPAN, these packages will not be indexed.&lt;/p&gt;

&lt;p&gt;When defining a struct with &lt;a href=&quot;https://metacpan.org/module/FFI::C&quot;&gt;FFI::C&lt;/a&gt;, the first parameter is a name that can
be referred to later, which is why these are defined from the inside (the
ones most deeply nested) going out. It means I can refer to the types of the
inner fields when defining the outer structs, like in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; field of type
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Root&lt;/code&gt; in the struct for the Santa::Kotlin::Kotlin package: since it is of
type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Root&lt;/code&gt;, its value will automatically be cast into a Santa::Kotlin::Root
object.&lt;/p&gt;

&lt;p&gt;We still need to get our hands on an instance of this outermost struct, and
for that we have to bind to that global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libexample_symbols&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Register $ffi with FFI::C, so new types become available&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;FFI::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$symbols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ffi&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;libexample_symbols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Symbols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since we’ve told &lt;a href=&quot;https://metacpan.org/module/FFI::C&quot;&gt;FFI::C&lt;/a&gt; that it should register any types it creates with
this instance of &lt;a href=&quot;https://metacpan.org/module/FFI::Platypus&quot;&gt;FFI::Platypus&lt;/a&gt;, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbols&lt;/code&gt; type (which
corresponds to the Santa::Kotlin::Symbols package defined above) as the
return value of this function.&lt;/p&gt;

&lt;p&gt;Note also that we are not &lt;a href=&quot;https://metacpan.org/module/FFI::Platypus#attach&quot;&gt;attaching&lt;/a&gt; this function, because we are not going
to expose it to our users. We only want to be able to call it once so we can
get a reference to the struct it returns, which we store in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$symbols&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once we’ve done all this preparation, we are ready to attach any functions in
our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example&lt;/code&gt; Kotlin package to our Santa::Kotlin Perl package, and we do this
by using the memory addresses of the functions we are interested in:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ffi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#          we look up the address    and give it a Perl name&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#                                \               \&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$symbols&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kotlin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;reverseString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;reverse_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, we are ready to call this function as we would any other from
our perl code:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Santa::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Kotlin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Santa::Kotlin::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;reverse_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lrep ot gnimoc si niltok atnas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&apos;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# OUTPUT: santa kotlin is coming to perl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These are good times to be a Perl kid, so happy holidays to all the good ones
out there.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;
		</content>
		<category term="perl" />
		<category term="advent" />
		<category term="tutorial" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/bundling-specific-modules/</id>
		<title>Bundling specific modules</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2021-04-28T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/bundling-specific-modules/" />
		<summary>A previous post detailed how to bundle all the dependencies of a Perl project. This post expands that with some details on how to bundle specific vendored modules.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/bundling-specific-modules/">
			&lt;p&gt;The &lt;a href=&quot;/blog/bundling-vendored-modules&quot;&gt;last time I wrote about bundling vendored modules&lt;/a&gt; in Perl, I
was a little annoyed by the fact that the tools I was using did not allow
me to bundle &lt;em&gt;a specific&lt;/em&gt; dependency. They made it very easy to bundle
&lt;em&gt;all&lt;/em&gt; of them. But every time I’ve needed to do this in the past, I’ve
always only needed to bundle &lt;em&gt;some&lt;/em&gt; dependencies (the ones that I’m
vendoring for whatever reason), being happy to fetch all the rest from
upstream public sources.&lt;/p&gt;

&lt;p&gt;I said as much that time:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you only want to bundle some dependencies with your project you’ll
have to manually remove all the ones you don’t care about (from both
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vendor/cache&lt;/code&gt; and your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;02packages.details.txt.gz&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As it turns out, doing this is much simpler than I thought.&lt;/p&gt;

&lt;h2 id=&quot;honorable-mentions&quot;&gt;Honorable mentions&lt;/h2&gt;

&lt;p&gt;Considering the TIMTOWTDI Perl ethos, it should come as no surprise that
there are a bunch of tools already available on CPAN to do more or less what
I was looking for. So before going through what I actually ended up using,
let’s go through some of the alternatives I considered… and why I didn’t
use them.&lt;/p&gt;

&lt;h3 id=&quot;carton&quot;&gt;&lt;a href=&quot;https://metacpan.org/pod/Carton&quot;&gt;Carton&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;This was the tool I initially used in my previous post. It is great for what
it does, but does not allow you to bundle specific distributions. As far as
I’m concerned, it’s the best tool for this sort of thing… unless you’re
trying to do what I wanted.&lt;/p&gt;

&lt;h3 id=&quot;pinto&quot;&gt;&lt;a href=&quot;https://metacpan.org/pod/Pinto&quot;&gt;Pinto&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;I’ve heard about Pinto in a couple of places, and I really wanted to like
it. However, it’s not only &lt;em&gt;massive&lt;/em&gt;, it also seems to unfortunately be
rather unmaintained.&lt;/p&gt;

&lt;p&gt;I tried installing Pinto on a fresh Perl install and it meant installing a
whooping 175 distributions. And looking through the list of dependencies,
that hardly comes as a surprise: the list includes heavy hitters like Moose,
LWP::UserAgent, DBIx::Class, Plack, and Starman.&lt;/p&gt;

&lt;p&gt;It might be that it’s the right tool for &lt;em&gt;some&lt;/em&gt; task, but not for this.&lt;/p&gt;

&lt;h3 id=&quot;cpanmini&quot;&gt;&lt;a href=&quot;https://metacpan.org/pod/CPAN::Mini&quot;&gt;CPAN::Mini&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://metacpan.org/pod/CPAN::Mini&quot;&gt;CPAN::Mini&lt;/a&gt; is a fairly minimal distribution that provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minicpan&lt;/code&gt;
command line utility to create a local clone of CPAN. Pair this with
something like &lt;a href=&quot;https://metacpan.org/pod/CPAN::Mini::Inject&quot;&gt;CPAN::Mini::Inject&lt;/a&gt; and its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcpani&lt;/code&gt; and you can now create
ether a CPAN mirror or a superset of CPAN.&lt;/p&gt;

&lt;p&gt;If I wanted to create a DarkPAN that served all the code in CPAN &lt;em&gt;as well
as some extra distributions&lt;/em&gt;, these two are probably the tools I’d go with.&lt;/p&gt;

&lt;p&gt;Still, this was a little more than what I had bargained for, since I only
wanted those extra non-CPAN modules, skipping what could already be obtained
upstream.&lt;/p&gt;

&lt;h2 id=&quot;and-the-winner-is&quot;&gt;And the winner is…&lt;/h2&gt;

&lt;h3 id=&quot;cpanrepository&quot;&gt;&lt;a href=&quot;https://metacpan.org/pod/CPAN::Repository&quot;&gt;CPAN::Repository&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;In the end, &lt;a href=&quot;https://metacpan.org/pod/CPAN::Repository&quot;&gt;CPAN::Repository&lt;/a&gt; did everything I wanted, and with a fairly
small number of dependencies.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The absolute minimal way to do this is with
something like the following:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;perl &lt;span class=&quot;nt&quot;&gt;-MCPAN&lt;/span&gt;::Repository &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;
    CPAN::Repository-&amp;gt;new( dir =&amp;gt; &quot;vendor/cache&quot; )
        -&amp;gt;add_author_distribution(@ARGV)
&apos;&lt;/span&gt; SOMEAUTHOR Some-Distro-1.337.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will generate the following file structure:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vendor/
└── cache
    ├── authors
    │  ├── 01mailrc.txt
    │  ├── 01mailrc.txt.gz
    │  └── id
    │      └── S
    │          └── SO
    │              └── SOMEAUTHOR
    │                  └── Some-Distro-1.337.tar.gz
    └── modules
        ├── 02packages.details.txt
        ├── 02packages.details.txt.gz
        └── 02STAMP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which can then be used following the same instructions as in &lt;a href=&quot;/blog/bundling-vendored-modules&quot;&gt;my initial
post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-use-case&quot;&gt;The use case&lt;/h2&gt;

&lt;p&gt;What motivated this was work on a codebase that depends on some CPAN
distributions that are currently undermaintained.&lt;/p&gt;

&lt;p&gt;When possible, the first choice is of course to move away from those
dependencies to other alternatives. Maybe even alternatives maintained by
yourself or the organisation you work for.&lt;/p&gt;

&lt;p&gt;But in some cases, moving away from a dependency can be too much work, or
you might be unwilling or unable to commit to maintaining a new distribution
(and having &lt;em&gt;two&lt;/em&gt; unmaintained distributions instead of one helps no one).&lt;/p&gt;

&lt;p&gt;In this case, the solution I was investigating was whether we could create
a fork of the distributions in question, and go through the regular release
cycle… except without a publicly indexed release.&lt;/p&gt;

&lt;p&gt;This has all the good things we know that come with a proper release: version
numbers that can be tracked and maintained using your regular cpanfile;
auditable change logs; etc. But all of that without the commitment to third
parties.&lt;/p&gt;

&lt;p&gt;To this end, I ended up writing &lt;a href=&quot;https://gitlab.com/-/snippets/2110304&quot;&gt;a small script&lt;/a&gt; that would wrap
around those CPAN::Repository calls and make things a little more extensible
and robust.&lt;/p&gt;

&lt;p&gt;The script takes a mandatory list of possible URIs (which can either point
to external repositories on providers like Github or GitLab, or directly to
distribution tarballs) and some options to define how that distribution
should be indexed. It adds some checks to encourage the use of proper
releases, but these are designed to be easily skippable in case you feel
like your foot could use a bullet-hole.&lt;/p&gt;

&lt;p&gt;And that’s pretty much it! All I need now is to convince the rest of my team
that this is a reasonable way to go. But even if we end up not using this,
I’m sure this will come in handy.&lt;/p&gt;

&lt;p&gt;All in all, I’m glad I got a chance to take another stab at this problem,
and that the solution was as simple as it was.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Installing on a fresh Perl like with Pinto, it installed 56
  distributions in total. This is not &lt;em&gt;tiny&lt;/em&gt; but it’s also not the end of
  the world. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="perl" />
		<category term="tutorial" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/porting-from-go/</id>
		<title>Porting from Go</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2020-11-22T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/porting-from-go/" />
		<summary>Some notes on porting code from Go to Raku, and what I found along the way</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/porting-from-go/">
			&lt;p&gt;In the past year or so, every time I’ve wanted to use hot-reloading&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; for
some service I’m writing, I’ve reached for a tool called &lt;a href=&quot;https://github.com/cespare/reflex&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reflex&lt;/code&gt;&lt;/a&gt;. This
tool is not particularly original, but it is a reasonably good implementation,
and it ships as a standalone-binary, which makes it very easy to move about.&lt;/p&gt;

&lt;p&gt;It is also written in a language I have some familiarity with, and one that
I’m trying to learn more about.&lt;/p&gt;

&lt;p&gt;So of course I decided to implement my own.&lt;/p&gt;

&lt;p&gt;The motivation was basically the same as &lt;a href=&quot;/blog/introducing-http-tiny&quot;&gt;the one that lead to
HTTP::Tiny&lt;/a&gt;: a desire to understand how the code worked, and a
search for real-world problems to attempt to solve using Raku. I might write
more in a later post about the specifics of &lt;a href=&quot;https://modules.raku.org/dist/App::Lorea:cpan:JJATRIA&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lorea&lt;/code&gt;&lt;/a&gt;, but in this post I
want to focus on one aspect in particular, which I think showcases some of
the strengths of Raku… and some of its weaknesses.&lt;/p&gt;

&lt;h2 id=&quot;some-go-code-to-get-going&quot;&gt;Some Go code to get going&lt;/h2&gt;

&lt;p&gt;One of the nice features of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reflex&lt;/code&gt; is that it batches file-system changes
to limit how often the command it needs to re-run gets executed. This is how
that feature is explained in &lt;a href=&quot;https://github.com/cespare/reflex#batching&quot;&gt;the documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Part of what reflex does is apply some heuristics to batch together
file changes. There are many reasons that files change on disk, and
these changes frequently come in large bursts. For instance, when you
save a file in your editor, it probably makes a tempfile and then
copies it over the target, leading to several different changes.
Reflex hides this from you by batching some changes together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple enough.&lt;/p&gt;

&lt;p&gt;Now let’s look at &lt;a href=&quot;https://github.com/cespare/reflex/blob/456b3718abbf1922cfbd498521c27851250f5496/reflex.go#L149-L186&quot;&gt;the implementation&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reflex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;silenceInterval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Millisecond&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewTimer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;silenceInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;silenceInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yikes.&lt;/p&gt;

&lt;p&gt;That block comes with a comment on top with these notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;batch&lt;/code&gt; receives file notification events and batches them up. It’s a bit
tricky, but here’s what it accomplishes:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;When we initially get a message, wait a bit and batch messages before
trying to send anything. This is because the file events come in bursts.&lt;/li&gt;
    &lt;li&gt;Once it’s time to send, don’t do it until the out channel is unblocked.
In the meantime, keep batching. When we’ve sent off all the batched
messages, go back to the beginning.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I came across this piece of code, I had to re-read it several times, and
cross-reference that to the comment, and read through a couple of pages of
documentation before I could say I understood it. And now, to write this post,
I had to spend a good couple of minutes more staring at it before I could
remember how it worked.&lt;/p&gt;

&lt;p&gt;In short, this is waiting on file-system changes to come in from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel&lt;/code&gt;,
and it adds them to a backlog as they come in. It continues to do this until
there is a break between the incoming changes of at least 300 milliseconds,
and it then processes them.&lt;/p&gt;

&lt;p&gt;The key here is the interaction between the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.Timer&lt;/code&gt; to keep track of
that time window, and the incoming and outgoing channels, which in Go are
blocking. The nested loops are there to make it so that, if an event comes
in while some other part of the system is running, it will also get
processed.&lt;/p&gt;

&lt;h2 id=&quot;porting-it-to-raku&quot;&gt;Porting it to Raku&lt;/h2&gt;

&lt;p&gt;As it turns out, porting code from Go to Raku is normally quite painless.
Like Go, Raku has superb support for asynchronous programming, and helper
classes like channels are built-in.&lt;/p&gt;

&lt;p&gt;However, when it came to porting this bit of code I ran into a bit of a
complication: Raku has no equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.Timer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For this particular case, the Go timer was being used to schedule an
event that would trigger in the future, unless it was re-scheduled to
happen further in the future via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timer.Reset&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;Now, while Raku comes with classes to represent &lt;a href=&quot;https://docs.raku.org/type/Instant&quot;&gt;moments in time&lt;/a&gt;, as
well as &lt;a href=&quot;https://docs.raku.org/type/Promise&quot;&gt;events in the future&lt;/a&gt;, these are not particularly well suited
for this case. For one, even if you can schedule an event in the future
(eg. with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Promise.new: $delay&lt;/code&gt;) this promise &lt;em&gt;will take place&lt;/em&gt;. Even if
you are not paying attention to it, any code you schedule will run.&lt;/p&gt;

&lt;p&gt;While it does look like there is some intention of implementing cancellations
for Promises in the future, we’re not there yet. So we’ve got to make do with
what we have.&lt;/p&gt;

&lt;p&gt;Luckily, Raku is a language that is built on the notion that you should have
enough rope to shoot yourself in the foot, and that it does. Enter
&lt;a href=&quot;https://modules.raku.org/dist/Timer::Breakable:cpan:SCIMON&quot;&gt;Timer::Breakable&lt;/a&gt;, which implements something alike to breakable promises,
and my very own &lt;a href=&quot;https://modules.raku.org/dist/Timer::Stopwatch:cpan:JJATRIA&quot;&gt;Timer::Stopwatch&lt;/a&gt;, which uses them to implement basically
the same idea as the Go timer.&lt;/p&gt;

&lt;h2 id=&quot;the-resulting-code&quot;&gt;The resulting code&lt;/h2&gt;

&lt;p&gt;With that set aside, let’s look at how that Go code compares to &lt;a href=&quot;https://gitlab.com/jjatria/lorea/-/blob/a01e6ea0293fae79108bc912ac09e37757a12931/lib/App/Lorea/Command.rakumod#L95-109&quot;&gt;the equivalent
code in Raku&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;method run ( --&amp;gt; Promise ) {
    use Timer::Stopwatch;
    my Timer::Stopwatch $timer .= new;

    start react {
        whenever $!supply {
            $timer.reset: 0.3;
            $!queue.add: .path;
        }
        whenever $timer { start self!process-queue }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The beauty here is that, unlike in Go, I’m using a &lt;a href=&quot;https://docs.raku.org/type/Supply&quot;&gt;Supply&lt;/a&gt; which offers the
same asynchronous guarantees as a channel with the benefit that reading and
writing from it does not block, so the nested loops to make sure that we catch
events that happen while we are waiting are gone.&lt;/p&gt;

&lt;h2 id=&quot;trade-offs-trade-offs&quot;&gt;Trade-offs, trade-offs&lt;/h2&gt;

&lt;p&gt;It’s possible that there are some subtle differences between the two versions
of this code, but they are functionally identical, and I was happy to get rid
of the added complexity to get code that I can understand at a glance.&lt;/p&gt;

&lt;p&gt;It did highlight in my eyes one of the limitations in Raku, however: that the
standard library, while huge in some respects, still lacks tools to do some of
the things that you might argue are pretty basic.&lt;/p&gt;

&lt;p&gt;Luckily, this kind of thing can be remedied by libraries and modules that
extend the language, like the ones shown here. And as more of these real-world
cases are explored, one can hope that cases like these, where we lack the
tools to get the job done, become a rarity.&lt;/p&gt;

&lt;p&gt;I’ll be happy to continue to contribute to that end.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;“Hot reloading” is a feature made popular particularly by web
  frameworks wherein any change in the source code of the service will
  make the development version of the server reload.&lt;/p&gt;

      &lt;p&gt;More generally, however, this can be applied to anything that will
  trigger some command when a file on disk has changed. And this is
  precisely what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reflex&lt;/code&gt; does. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="raku" />
		<category term="lorea" />
		<category term="go" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/typesetting-tobi/</id>
		<title>Typesetting ToBI</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2020-11-15T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/typesetting-tobi/" />
		<summary>Some personal notes on my choices when typesetting ToBI tone markers.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/typesetting-tobi/">
			&lt;p&gt;A lifetime ago,&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; back when I was still convinced I was on track to become
a speech researcher, I spent a considerable amount of time dealing with ToBI
tone markers.&lt;/p&gt;

&lt;p&gt;In case you need a refresher, or come from an entirely different walk of life,
ToBI stands for “Tones and Break Indices”, and is a standard for speech
researchers to mark intonation, or &lt;em&gt;prosody&lt;/em&gt;. This sort of annotation is
pretty essential for research, because it allows researchers to record these
data, share them with their colleagues, and process it as a whole.&lt;/p&gt;

&lt;p&gt;This last point is important, and is one of the reasons why the standard has
been this successful: the transcription uses only ASCII characters that were
and continue to be trivial to input, and easy to read by machines. This is in
fact something that was identified as a strength in &lt;a href=&quot;#silverman1992&quot;&gt;the original publication
that described it&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[We believe ToBI will become a standard for prosodic transcription because,
among other reasons,] it defines ASCII formats for machine-readable
representations of the transcriptions which are in principle independent of
the pitch extraction and signal available to the researcher.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In brief, ToBI tone markers are largely made of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;H&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; characters,
some of which might be marked as primary stresses with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; character, and
sometimes combined with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; to make up complex tones.&lt;/p&gt;

&lt;figure class=&quot;graphic&quot;&gt;
 &lt;img src=&quot;/assets/images/typesetting-tobi/basic-tobi.png&quot; alt=&quot;A sample of the basic tone markers in ToBI: a H* and a L* tones,
 with red lines marking the baseline, the median, and the ascender height&quot; /&gt;
 &lt;figcaption&gt;Some basic ToBI tones&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;The problem with these tones, as I imagine some of you might have immediately
noticed, is that they are butt-ugly, and have absolutely zero chance of
looking good in a paragraph of text.&lt;/p&gt;

&lt;figure class=&quot;graphic&quot;&gt;
 &lt;img src=&quot;/assets/images/typesetting-tobi/tobi-in-context.png&quot; alt=&quot;A couple of lines from an academic paper where compound tones appear
 as part of sentences. The text uses the standard typesetting of ToBI tones,
 which does not blend into the surrounding text&quot; /&gt;
 &lt;figcaption&gt;The typical typesetting of ToBI tones in context&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And here is where the rubber meets the road: we want tone markers that use
only ASCII characters so they are machine readable to be able to process our
data. But once they’ve been processed, and we want to report the results, we
are going to be putting those tones markers in an entirely different context:
one where humans, and not machines, are the primary readers, and where they
will be surrounded by more of those pesky human-readable sentences.&lt;/p&gt;

&lt;p&gt;I’d posit that using the standard machine-readable notation in prose is
ultimately wrong.&lt;/p&gt;

&lt;p&gt;Before anyone gets the wrong idea: none of this, including the example above,
is an indictment on the quality of the academic work that typesets these
markers this way. I’m pretty sure that most people in academia does this
because that’s how they’ve always seen it in  print, and because they have
better things to do with their time than obsess over the spacing of the
characters in their publications.&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Then again, when I got around to writing &lt;a href=&quot;https://gitlab.com/jjatria/thesis&quot;&gt;my dissertation&lt;/a&gt;, I was
procrastinating pretty heavily, and a lot of it was pretty much &lt;em&gt;exactly&lt;/em&gt;
obsessing over kerning and trying to make sure anything I wrote would
simultaneously make Bringhurst and Tufte proud.&lt;/p&gt;

&lt;p&gt;Which is to say: there was no way I was going to just print that in my thesis.&lt;/p&gt;

&lt;h2 id=&quot;my-solution&quot;&gt;My solution&lt;/h2&gt;

&lt;p&gt;So I came up with a different way to do it that seemed better in my eyes:&lt;/p&gt;

&lt;figure class=&quot;graphic&quot;&gt;
 &lt;img itemprop=&quot;image&quot; src=&quot;/assets/images/typesetting-tobi/single-tone.png&quot; alt=&quot;A comparison between two ways of typesetting of ToBI tone markers
 for H* and L*. A column on the left shows the standard typesetting using
 uppercase characters followed by a * character, while a column on the
 right uses small caps for the letters with the * character placed above&quot; /&gt;
 &lt;figcaption&gt;Typesetting single tones&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The fundamental idea was twofold. First, use small-caps instead of the
upper-case for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;H&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; to make the text blend into the surrounding
text. Second, recognise that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; character was marking &lt;em&gt;a property of the
tone&lt;/em&gt;, which meant that anything that made this link easier to see should be in
scope.&lt;/p&gt;

&lt;p&gt;The result, as seen above, makes the markers have less vertical movement
(meaning that they can be read from left to right with the eye largely
following stable horizontal line), and allows each of them to occupy the
same amount of horizontal space, which promotes the idea that the tones
are the fundamental unit at play.&lt;/p&gt;

&lt;p&gt;This was easily accomplished in LaTeX.&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; First we define some basic
components:&lt;/p&gt;

&lt;div class=&quot;language-latex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\high&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\textsc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;h&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\low&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\textsc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;l&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\ToBIminus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;-&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\ToBIplus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;+&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which can then be combined to generate the tones shown above:&lt;/p&gt;

&lt;div class=&quot;language-latex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\Lstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\low\kern&lt;/span&gt;-0.465em&lt;span class=&quot;k&quot;&gt;\raisebox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;0.15em&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;*&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\kern&lt;/span&gt;0.09em&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\Hstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\high\kern&lt;/span&gt;-0.485em&lt;span class=&quot;k&quot;&gt;\raisebox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;0.15em&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;*&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\kern&lt;/span&gt;0.11em&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;what-about-compound-tones&quot;&gt;What about compound tones&lt;/h3&gt;

&lt;figure class=&quot;graphic&quot;&gt;
 &lt;img src=&quot;/assets/images/typesetting-tobi/compound-tone.png&quot; alt=&quot;A comparison between two ways of typesetting of ToBI markers for
 L+H* and L*+H tones. A column on the left shows the standard typesetting using
 uppercase characters followed by a * character, while a column on the right uses
 small caps for the letters with the * character placed above&quot; /&gt;
 &lt;figcaption&gt;Typesetting compound tones&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For compound tones, it’s just a matter of defining the rest of the relevant
combinations:&lt;/p&gt;

&lt;div class=&quot;language-latex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\LHstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\mbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\low\ToBIplus\Hstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\LstarH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\mbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\Lstar\ToBIplus\high&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\HLstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\mbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\high\ToBIplus\Lstar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;\newcommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\HstarL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\mbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;\Hstar\ToBIplus\low&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I am pretty sure that the values used by me to do this are subject to a lot
of improvement. And I’m sure also that, as much as I’m interested in
typography, others are probably better than me at evaluating where I might
have gone too far or not far enough.&lt;/p&gt;

&lt;p&gt;Still, I’m very happy with the result as it looks in the document I was
producing, and if I can share this and expose other people to the &lt;em&gt;possibility&lt;/em&gt;
of these tones not making your document look like someone coughed a bunch of
letters on your page, I’ll consider my job here done. And I’d say that the
results speak for themselves:&lt;/p&gt;

&lt;figure class=&quot;graphic&quot;&gt;
 &lt;img src=&quot;/assets/images/typesetting-tobi/tones-in-context.png&quot; alt=&quot;A couple of lines from an academic paper where compound tones appear
 as part of sentences. The text uses the custom typesetting for ToBI tones,
 which makes them blend into the surrounding text&quot; /&gt;
 &lt;figcaption&gt;My alternative ToBI tones in context&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The best part of this? Because of the characters I used, the end results are
still completely searchable using a standard PDF reader, which really
highlights the fact that the storage of machine-readable information, and its
&lt;em&gt;display&lt;/em&gt; are different things, and we should not sacrifice the latter for
the former.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li id=&quot;silverman1992&quot; class=&quot;reference&quot;&gt;Silverman, K. et al. (1992) &lt;a href=&quot;/docs/Silverman+.1992.pdf&quot;&gt;ToBI: A Standard for Labeling English
  Prosody&lt;/a&gt; In: &lt;em&gt;The Second International Conference on Spoken Language
  Processing&lt;/em&gt;, ICSLP 1992, Banff, Alberta, Canada, October 13-16, 1992&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Or at least what &lt;em&gt;feels&lt;/em&gt; like a lifetime ago… &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;After all, that strongly worded response to Reviewer #2’s
  passive-aggressive remarks is not going to write itself. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;In hindsight, my getting into LaTeX in order to write my dissertation,
  and the amount of time I spent &lt;em&gt;not actually&lt;/em&gt; writing my dissertation,
  seems like a pretty clear symptom that I was dissatisfied with a purely
  academic career. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="typesetting" />
		<category term="latex" />
		<category term="phonology" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/introducing-http-tiny/</id>
		<title>Introducing HTTP::Tiny</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2020-11-08T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/introducing-http-tiny/" />
		<summary>HTTP::Tiny has been release upon an unsuspecting world! Here&apos;s a little about why I wrote it, and why I think it might come in handy in the future.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/introducing-http-tiny/">
			&lt;p&gt;For the past couple of weeks I’ve been working on a minimal HTTP client
library for Raku, and it has now been released. Say hello to &lt;a href=&quot;https://modules.raku.org/dist/HTTP::Tiny:cpan:JJATRIA&quot;&gt;HTTP::Tiny&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;This might feel like a bit of an unnecessary thing to do, since Raku
&lt;a href=&quot;https://modules.raku.org/dist/HTTP::UserAgent:github:github:sergot&quot;&gt;already&lt;/a&gt;
&lt;a href=&quot;https://modules.raku.org/dist/Cro::HTTP:cpan:JNTHN&quot;&gt;has&lt;/a&gt;
&lt;a href=&quot;https://modules.raku.org/dist/HTTP::Tinyish:cpan:SKAJI&quot;&gt;several&lt;/a&gt;
&lt;a href=&quot;https://modules.raku.org/dist/LibCurl:cpan:CTILMES&quot;&gt;usable&lt;/a&gt;
&lt;a href=&quot;https://modules.raku.org/dist/LWP::Simple:github:Cosimo%20Streppone&quot;&gt;HTTP&lt;/a&gt;
&lt;a href=&quot;https://modules.raku.org/dist/Net::HTTP:github:ugexe&quot;&gt;clients&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;Initially, the project started as an excuse for me to learn about using Raku
in the real world. After the project was chosen, a secondary goal was to learn
about the HTTP protocol.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;That second part was not as courageous as you might think because, in case you
are not coming from the Perl world, &lt;a href=&quot;https://metacpan.org/pod/HTTP::Tiny&quot;&gt;there is already a Perl library named
HTTP::Tiny&lt;/a&gt; which I was going to use as a model. So rather than
implementing the HTTP spec from scratch, the idea was to port an existing and
well tested library.&lt;/p&gt;

&lt;p&gt;The more I worked on it, however, the more I realised that this might actually
be useful to have in the Raku world as well, and these are reflected in the
abstract of the original Perl library, which I’ve inherited for the Raku
one as well:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A small, simple, correct HTTP/1.1 client&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;small&quot;&gt;Small&lt;/h2&gt;

&lt;p&gt;The “small” in the abstract is related to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::Tiny&lt;/code&gt; in the name, which comes
from &lt;a href=&quot;https://beta.nntp.perl.org/group/perl.datetime/2007/01/msg6584.html&quot;&gt;an idea first proposed by Adam Kennedy for CPAN modules&lt;/a&gt;: to write
“tiny” versions of useful modules implemented in as little code as possible,
with “no non-core dependencies”.&lt;/p&gt;

&lt;p&gt;This reference to a “core” makes more sense in Perl because that language has
a clearly defined distribution of “core” modules that can be considered “part
of the language”. It is not common, and indeed difficult, to have a working
version of Perl that does not have the core modules installed.&lt;/p&gt;

&lt;p&gt;And although Raku also has &lt;a href=&quot;https://rakudo.org/star&quot;&gt;Rakudo Star&lt;/a&gt;, which works as a sort of &lt;em&gt;de-facto&lt;/em&gt;
core distribution, this is by no means a “core” set, but more a recommended
set of modules to accomplish common tasks.&lt;/p&gt;

&lt;p&gt;And this is important, because particularly when trying to bring Raku into
environments where Raku is not already established, any external dependency
is a barrier.&lt;/p&gt;

&lt;p&gt;My challenge when writing the Raku version of HTTP::Tiny was to use only
language built-ins. To write a minimal HTTP client that was usable, and had
no dependencies.&lt;/p&gt;

&lt;p&gt;As for “as little code as possible”, HTTP::Tiny has just over 1000 lines of
code. Deciding if that’s too much is left as an exercise for the beholder.&lt;/p&gt;

&lt;h2 id=&quot;simple&quot;&gt;Simple&lt;/h2&gt;

&lt;p&gt;Simplicity in software can be understood in a number of different ways, and
some of them are related to the point above: the more code you use, the more
complexity it hides. In that sense, HTTP::Tiny is as simple as I’ve been able
to make it.&lt;/p&gt;

&lt;p&gt;But a different way to understand “simple” is in the simplicity of use, which
relates to the interface that is available for the user.&lt;/p&gt;

&lt;p&gt;At its core, HTTP::Tiny has one method: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request&lt;/code&gt;. Every other method
available (with the exception of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;can-ssl&lt;/code&gt; utility one) calls this one
method to perform the core of its work. The signature is shown below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;method request (
    Str $method,
    Str $url,
       :%headers,
       :$content,
       :&amp;amp;data-callback,
       :&amp;amp;trailer-callback,
) returns Hash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only required parameters are the method and the URL, with the remaining
ones existing to support the multiple different kinds of requests available.&lt;/p&gt;

&lt;p&gt;Every effort was made to keep this interface small and predictable, but
ultimately, whether this interface is simple or not will also be up to the
users of this library. To see what this means, &lt;a href=&quot;https://modules.raku.org/dist/HTTP::Tiny:cpan:JJATRIA#request&quot;&gt;the documentation&lt;/a&gt;
is probably the best place to check, and &lt;a href=&quot;https://gitlab.com/jjatria/http-tiny/-/blob/master/examples/cookbook.md&quot;&gt;a cookbook-style document with
examples of common requests&lt;/a&gt; is also available to see what common
requests look like.&lt;/p&gt;

&lt;h2 id=&quot;correct&quot;&gt;Correct&lt;/h2&gt;

&lt;p&gt;More importantly, HTTP::Tiny aims to be a &lt;em&gt;correct&lt;/em&gt; implementation of the
HTTP/1.1 transport, and to my surprise, this might be the area where this
library might make the most significant contribution.&lt;/p&gt;

&lt;p&gt;HTTP::Tiny of course supports the most common use cases, including form and
file uploads. But it also supports streaming requests, multipart ranged
responses, and both HTTP and HTTPS proxied connections. It also handles
unexpected 1XX responses, request redirection, and trailers in chunked
responses.&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Needless to say, it might &lt;em&gt;strive&lt;/em&gt; for correctness, but it is far from
perfect. A &lt;a href=&quot;https://modules.raku.org/dist/HTTP::Tiny:cpan:JJATRIA#limitations&quot;&gt;full list of known limitations&lt;/a&gt; is available in the
documentation, and that list will undoubtedly grow to include the limitations
that will only become apparent with use.&lt;/p&gt;

&lt;p&gt;But for that it needs users, so please consider giving HTTP::Tiny a shot.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Yes, the P stands for “protocol” already. Thanks. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;Some of these things might seem obvious, but I found that support for
  all of them is unexpectedly hard to come by in the existing libraries.
  The ones that were easily resolved
  &lt;a href=&quot;https://github.com/sergot/http-useragent/pull/237&quot;&gt;became&lt;/a&gt;
  &lt;a href=&quot;https://github.com/sergot/http-useragent/pull/235&quot;&gt;upstream&lt;/a&gt;
  &lt;a href=&quot;https://github.com/sergot/io-socket-ssl/pull/21&quot;&gt;contributions&lt;/a&gt;
  (because we’re not competitors but colleagues), but not all of them
  have been resolved. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="raku" />
		<category term="http-tiny" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/self-unwrapping-routine/</id>
		<title>Dissecting a Raku snippet</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2020-10-26T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/self-unwrapping-routine/" />
		<summary>Dissecting a Raku snippet that wraps a subroutine in a self-unwrapping block of code.</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/self-unwrapping-routine/">
			&lt;p&gt;A couple of days ago, while prototyping the solution to a problem I had,
I wrote a bit of code that brought immense joy to me. It was the kind of
code that makes you giggle each time you see it run just because of how
clever and elegant the solution seems, and because of the excitement of
proving that, against your expectations, &lt;em&gt;it actually works&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Wanting to share the excitement, &lt;a href=&quot;https://twitter.com/jjatria/status/1319384812814204930&quot;&gt;I shared it online&lt;/a&gt;. The snippet
looked like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sub foo ( $a, $b ) { $a ~ $b }

my $wh;

$wh = &amp;amp;foo.wrap: {
    LEAVE &amp;amp;foo.unwrap: $wh;
    callwith( $^b, $^a );
}

say foo( 1, 2 ) for 1 .. 3;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is &lt;em&gt;very&lt;/em&gt; dense.&lt;/p&gt;

&lt;p&gt;And a bit cryptic.&lt;/p&gt;

&lt;p&gt;And hella fun.&lt;/p&gt;

&lt;p&gt;So let’s break it down. To do this, we’ll start from the end, and work our
way back to the beginning.&lt;/p&gt;

&lt;h3 id=&quot;the-results&quot;&gt;The results&lt;/h3&gt;

&lt;p&gt;When the code above gets executed, it prints out the following lines:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;21
12
12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the result of the final line:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;say foo( 1, 2 ) for 1 .. 3;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;which prints the result of calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo( 1, 2 )&lt;/code&gt; three times. This line will
be familiar to Perl programmers because Raku supports “&lt;a href=&quot;https://docs.raku.org/language/5to6-perlsyn#Statement_modifiers&quot;&gt;statement modifiers&lt;/a&gt;”,
which it inherited &lt;a href=&quot;https://perldoc.perl.org/perlsyn#Statement-Modifiers&quot;&gt;from Perl&lt;/a&gt;: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; at the end of the statement modifies
the statement that comes before, and executes it once for each element in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 .. 3&lt;/code&gt; range (which is &lt;em&gt;actually&lt;/em&gt; &lt;a href=&quot;https://docs.raku.org/type/Range&quot;&gt;a Range object&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;But if we are calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo( 1, 2 )&lt;/code&gt; three times, then why does it output &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21&lt;/code&gt;
the first time we call it, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt; the rest of the times?&lt;/p&gt;

&lt;h3 id=&quot;wrapping-code&quot;&gt;Wrapping code&lt;/h3&gt;

&lt;p&gt;That’s because of the previous block:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;my $wh;

$wh = &amp;amp;foo.wrap: {
    LEAVE &amp;amp;foo.unwrap: $wh;
    callwith( $^b, $^a );
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This bit of code is where the core of the magic happens. ✨&lt;/p&gt;

&lt;p&gt;The first bit that matters is the call to &lt;a href=&quot;https://docs.raku.org/type/Routine#method_wrap&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrap&lt;/code&gt;&lt;/a&gt; This is called on the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; subroutine object (which is why we reference it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; &lt;a href=&quot;https://docs.raku.org/language/glossary#index-entry-Sigil&quot;&gt;sigil&lt;/a&gt;,
otherwise we might &lt;em&gt;call&lt;/em&gt; the function rather than get a reference to it).&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrap&lt;/code&gt; method allows us to attach a block of code to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, so that
calling that function will execute the block of code instead.&lt;/p&gt;

&lt;h3 id=&quot;re-dispatch&quot;&gt;Re-dispatch&lt;/h3&gt;

&lt;p&gt;Inside the block we are using to wrap &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; we use &lt;a href=&quot;https://docs.raku.org/language/functions#sub_callwith&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callwith&lt;/code&gt;&lt;/a&gt; to be able
to call the wrapped subroutine. We could use a number of different
alternatives depending on whether we wanted to get a return value and whether
we wanted to modify the arguments the underlying function gets called with.&lt;/p&gt;

&lt;p&gt;In this case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callwith&lt;/code&gt; allows us to modify the arguments, but it never
returns. And we modify the arguments by calling it like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callwith( $^b, $^a )&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But where did these variables come from?&lt;/p&gt;

&lt;h3 id=&quot;self-declared-positionals&quot;&gt;Self-declared positionals&lt;/h3&gt;

&lt;p&gt;They are &lt;a href=&quot;https://docs.raku.org/language/variables#The_^_twigil&quot;&gt;self-declared positional arguments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When a block does not define an explicit list of arguments, any variables
used within its scope that use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt; twigil (= any variable with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt; after
the sigil) declares an implicit positional argument. Each positional the block
receives will get assigned to these variables in alphabetical order.&lt;/p&gt;

&lt;p&gt;In the example above, then, the call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callwith( $^b, $^a )&lt;/code&gt; will re-dispatch
to the function we are wrapping, with the first and second arguments switched.
Since the original subroutine concatenates both arguments (with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~&lt;/code&gt;
operator), this explains the first line of output: we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo( 1, 2 )&lt;/code&gt; and
this re-dispatches to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo( 2, 1 )&lt;/code&gt; which results in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;Progress!&lt;/p&gt;

&lt;p&gt;Now for the truly magic bit.&lt;/p&gt;

&lt;h3 id=&quot;self-unwrapping-wrappers&quot;&gt;Self-unwrapping wrappers&lt;/h3&gt;

&lt;p&gt;Before &lt;em&gt;actually&lt;/em&gt; re-dispatching to the original code, we have another line:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;LEAVE &amp;amp;foo.unwrap: $wh;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This line uses a &lt;a href=&quot;https://docs.raku.org/language/phasers#LEAVE&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LEAVE&lt;/code&gt; phaser&lt;/a&gt; to register a statement to be called when
execution &lt;em&gt;leaves&lt;/em&gt; the current block. So after switching the arguments and
re-dispatching to the original subroutine, execution leaves the block and this
statement finally gets executed.&lt;/p&gt;

&lt;p&gt;When it does, it calls the &lt;a href=&quot;https://docs.raku.org/type/Routine#method_unwrap&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unwrap&lt;/code&gt;&lt;/a&gt; method on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; to remove the wrapping
code (it does this by passing in the wrapping handle that was stored in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$wh&lt;/code&gt; variable).&lt;/p&gt;

&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;/h3&gt;

&lt;p&gt;And that’s it! The snippet defines a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; subroutine that takes two arguments
and concatenates them. It then wraps it into a block of code that swaps the
arguments before calling the original subroutine, and then removes itself as
if it was never there.&lt;/p&gt;

&lt;p&gt;This explains the output: the first time we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo( 1, 2 )&lt;/code&gt; the arguments
get swapped and we print &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21&lt;/code&gt;, but on subsequent calls there is no wrapping
code anymore, so we get lines like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What a mouthful! But all of that in seven lines of code. Raku can really pack
a punch. ✊&lt;/p&gt;

&lt;h3 id=&quot;thats-nice-but-whats-the-real-use&quot;&gt;That’s nice, but what’s the real use?&lt;/h3&gt;

&lt;p&gt;Incidentally, this was something that I was prototyping to solve a real
problem: I had a callback that would get called repeatedly, and I wanted to
hook into the first time that callback was executed so I could run a
preliminary check. But once the check had passed, there was no point in
doing it every time. &lt;a href=&quot;https://gitlab.com/jjatria/http-tiny/-/commit/03db51456f9a85bbdb1b3638b9c75cf0ad291abe#8ffc9e23bce11e380f350e72d95e4b50ebc5ef2e_68_72&quot;&gt;Unwrap to the rescue&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Incidentally, this makes use of another useful Raku feature: in Raku
  &lt;em&gt;everything is an object&lt;/em&gt;. This is what allows us to, for example,
  seamlessly call methods on subroutines. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="raku" />
		<category term="tutorial" />
	</entry>
	<entry>
		<id>https://www.pinguinorodriguez.cl/blog/raku-argument-parsing/</id>
		<title>Sharing command line parameters in Raku</title>
		<author>
			<name>José Joaquín Atria</name>
			<email>jjatria@gmail.com</email>
		</author>
		<updated>2020-09-28T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.pinguinorodriguez.cl/blog/raku-argument-parsing/" />
		<summary>Some things I learned about argument parsing when writing a command line tool in Raku</summary>
		<content type="html" xml:base="https://www.pinguinorodriguez.cl/blog/raku-argument-parsing/">
			&lt;p&gt;Raku is full of neat little tricks and gems that can make the programmer’s
life simpler (or at least a whole more entertaining). And as a language that
rewards mastery, it’s not a surprise that these can combine into sophisticated
solutions to common problems.&lt;/p&gt;

&lt;p&gt;This post will illustrate this point with one such example: sharing parameters
in a command line application. But first, we’ll need to cover some of the neat
little building blocks that allow for this.&lt;/p&gt;

&lt;h2 id=&quot;command-line-interfaces&quot;&gt;Command line interfaces&lt;/h2&gt;

&lt;p&gt;Raku has &lt;a href=&quot;https://docs.raku.org/language/create-cli&quot;&gt;built-in support for writing command line interfaces&lt;/a&gt;. If a
file has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAIN&lt;/code&gt; subroutine, it will called automatically when the file
is directly executed. And more interestingly will use that subroutine’s
signature to automatically parse the command line arguments.&lt;/p&gt;

&lt;p&gt;That means that an executable file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cli&lt;/code&gt; that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env raku

sub MAIN ( Str $command, Str $path, Bool :$debug ) {
    note &quot;Working on $path&quot; if $debug;

    given $command {
        when &apos;grep&apos; {
            .say for $path.IO.lines.grep: /raku/;
        }
        when &apos;count&apos; {
            say &quot;$_ has { .IO.lines.elems } lines&quot; given $path;
        }
        default {
            say &quot;Unknown command: $command&quot;;
            say $*USAGE;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;will result in the following output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./cli grep ./cli
#!/usr/bin/env raku
            .say for $path.IO.lines.grep: /raku/;
$ ./cli count ./cli
/home/user/cli has 17 lines
$ ./cli
Usage:
  /home/user/cli [--debug] &amp;lt;command&amp;gt; &amp;lt;path&amp;gt;
$ ./cli reverse ./cli
Unknown command: reverse
Usage:
  /home/user/cli [--debug] &amp;lt;command&amp;gt; &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;multiple-dispatch&quot;&gt;Multiple dispatch&lt;/h2&gt;

&lt;p&gt;This is made possible by the signature in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAIN&lt;/code&gt; subroutine, which
specifies what parameters that subroutine can take (and of course, which ones
it cannot, like that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--fake-option&lt;/code&gt; one I used).&lt;/p&gt;

&lt;p&gt;But the compiler not only can determine whether a function call is valid or
not based on its signature, it can also decide &lt;em&gt;which function to call based
the arguments used&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can use this to expand our application:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env raku

multi sub MAIN ( &apos;grep&apos;, Str $path, Bool :$debug ) {
    note &quot;Working on $path&quot; if $debug;
    .say for $path.IO.lines.grep: /raku/;
}

multi sub MAIN ( &apos;count&apos;, Str $path, Bool :$debug ) {
    note &quot;Working on $path&quot; if $debug;
    say &quot;$_ has { .IO.lines.elems } lines&quot; given $path;
}

multi sub MAIN (
    Str $command, Str $path, Bool :$debug,
) is hidden-from-USAGE {
    note &quot;Working on $path&quot; if $debug;
    say &quot;Unknown command: $command&quot;;
    say $*USAGE;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which will keep the behaviour for all the above calls, but will now give us
a different help message:&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./cli --help
Usage:
  /home/user/cli [--debug] grep &amp;lt;path&amp;gt;
  /home/user/cli [--debug] count &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This new version keeps different behaviours separate, which makes
implementing a new command a lot simpler. But it also has some unwanted
issues, like forcing us to repeat the shared parameters on each entry,
which can get pretty unwieldy with more complex applications.&lt;/p&gt;

&lt;p&gt;Luckily, we have another ace up our sleeves.&lt;/p&gt;

&lt;h2 id=&quot;subroutine-prototypes&quot;&gt;Subroutine prototypes&lt;/h2&gt;

&lt;p&gt;When declaring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multi&lt;/code&gt; subroutines we can declare common behaviours by
declaring a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proto&lt;/code&gt;. This can be useful when we want to make sure that
we don’t accidentally make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$path&lt;/code&gt; into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt;, for example. But
we can also use it for our application:&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env raku

proto sub MAIN ( $, Str $path, Bool :$debug, | ) {
    note &quot;Working on $path&quot; if $debug;
    {*}
}

multi sub MAIN ( &apos;grep&apos;, Str $path, *% ) {
    .say for $path.IO.lines.grep: /raku/;
}

multi sub MAIN ( &apos;count&apos;, Str $path, *% ) {
    say &quot;$_ has { .IO.lines.elems } lines&quot; given $path;
}

multi sub MAIN ( Str $command, Str $path, *% ) is hidden-from-USAGE {
    say &quot;Unknown command: $command&quot;;
    say $*USAGE;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proto&lt;/code&gt; defines a common portion of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAIN&lt;/code&gt; in which we can place any
shared behaviours (including for example any initialisation we may want
to do with our now global parameters). Unfortunately, it does mean that
our automatically generated usage message gets slightly less nice:&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./cli --help
Usage:
  /home/user/cli grep &amp;lt;path&amp;gt;
  /home/user/cli count &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;timtowtdi&quot;&gt;TIMTOWTDI&lt;/h2&gt;

&lt;p&gt;As always, there is more than one way to do it,&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; and it’s up to you
as the developer to decide which one is the most appropriate for your needs.&lt;/p&gt;

&lt;p&gt;I’m just glad that Raku has all these tools at my disposal, and gives me
enough &lt;del&gt;rope&lt;/del&gt; room to grow.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Note how we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is-hidden-from-USAGE&lt;/code&gt; trait to tell Raku to
  ignore a particular subroutine when generating the usage message. This
  is another of those nice little tricks Raku is littered with. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|&lt;/code&gt; at the end of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proto&lt;/code&gt; declaration declares a &lt;a href=&quot;https://docs.raku.org/type/Signature#Capture_parameters&quot;&gt;capture
  parameter&lt;/a&gt;, and in this case has the effect of saying that the
  specific implementations of this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proto&lt;/code&gt; may have other parameters
  (like sub-command-specific options, for example).&lt;/p&gt;

      &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*%&lt;/code&gt; I used in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAIN&lt;/code&gt; subs is a &lt;a href=&quot;https://docs.raku.org/type/Signature#Slurpy_(A.K.A._variadic)_parameters&quot;&gt;slurpy parameter&lt;/a&gt; which
  &lt;em&gt;slurps&lt;/em&gt; in any non-specified parameters to lets that signature match
  even if I don’t bother to specify a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:$debug&lt;/code&gt; named parameter, for
  example. I could have used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|&lt;/code&gt; instead, but that has some effects
  on the usage message that I wanted to avoid. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;Fortunately, there are ways in which &lt;a href=&quot;https://docs.raku.org/language/create-cli#sub_USAGE&quot;&gt;we can solve this&lt;/a&gt; by
  either expanding the built-in message available in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$*USAGE&lt;/code&gt; or
  replacing it entirely by defining our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USAGE&lt;/code&gt; subroutine. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;You could also us &lt;a href=&quot;https://docs.raku.org/syntax/unit&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unit&lt;/code&gt;&lt;/a&gt; to define file-wide &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAIN&lt;/code&gt;s for each
  separete command, and leave the top level file to dispatch to
  the right file! &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
		</content>
		<category term="raku" />
		<category term="tutorial" />
	</entry>
</feed>

