Featured

The 2025 Raku Advent Posts

(in chronological order, with comment references)

Featured

All the blog posts of 2021

Featured

It’s that time of the year

When we start all over again with advent calendars, publishing one article a day until Christmas. This is going to be the first full year with Raku being called Raku, and the second year we have moved to this new site. However, it’s going to be the 12th year (after this first article) in a row with a Perl 6 or Raku calendar, previously published in the Perl 6 Advent Calendar blog. And also the 5th year since the Christmas release, which was announced in the advent calendar of that year.

Anyway. Here we go again! We have lined a up a full (or eventually full by the time the Advent Calendar is finished) set of articles on many different topics, but all of them about our beloved Raku.

So, enjoy, stay healthy, and have -Ofun reading this nice list of articles this year will be bringing.

Day 25 – Raku 2025 Review

How time flies. Yet another year has flown by.

Let’s first start with the technical stuff, as nerds do!

Rakudo

Rakudo saw about 1650 commits (MoarVM, NQP, Rakudo, doc) this year, which is about 20% less than 2024. All of these repositories now have “main” as their default branch (rather than “master”).

About 58% of the Rakudo commits were in the development of RakuAST (up from 33% in 2024), of which more than 95% were done by Stefan Seifert. This work, that was supported by a TPRF grant, resulted in being able to build the part of Rakudo that’s written in Raku using RakuAST (often referred to as the “bootstrap“).

However there are still quite a few issues that need to be fixed before RakuAST-based Rakudo can be made the default. And thus be ready for the next language level release. Still, an amazing amount of work done by Stefan Seifert, so kudos!

Under the hood, behind the scenes

Geoffrey Broadwell has done a major update of the internal MoarVM Unicode tools. Based on that work Shimmerfairy developed an update of the MoarVM Unicode support from 15.0 to 17.0. This includes support for some new emojis such as FINGERPRINT 🫆, SPLATTER 🫟, HARP 🪉. This means that all of the changes of Unicode 16 and Unicode 17 were implemented to a very high degree. Quite a feat!

Patrick Böker was really busy this year: new script runners not only made execution of CLI-scripts a bit faster, it also made it possible to run CLI-scripts on Windows without any issues.

Again, a lot of work was done on making the Continuous Integration testing produce fewer (and recently hardly any) false positives anymore. Which makes life for core developers a lot easier!

Very important for packagers: Rakudo now again has a reproducible build process, thanks for Timo Paulssen and others.

Also the REPL (Read, Evaluate, Print Loop) has been improved: grammar changes are now persistent, and it’s also possible to enter multi-line comments. Some of these changes were backported from the REPL module, as described in this blog post.

The tests for experimental Raku features (such as :pack, :cached, :macros) have been moved from the Raku test-suite (roast) to the Rakudo repository, as they are technically not part of the definition of the Raku Programming Language.

Sadly, the JVM backend has hardly seen any updates the past year. It was therefore decided to not mention the JVM backend in releases anymore, nor to make sure that there would not be any ecosystem breakage on the JVM backend before a release.

If you want the JVM to remain a viable backend, you are very much invited to get involved!

New features in 6.d

The most notable new features in the default language level:

Varargs support in NativeCall

After many years of people asking for this feature, Patrick Böker actually wrote the infrastructure in MoarVM and Rakudo to support calling C-functions that support a variable number of arguments using the va_arg standard.

To give an example, it’s now possible to create a foo subroutine that will call the printf C-function (from the standard library) that takes a format string as the first argument, and a variable number of arguments after that:

use NativeCall;
sub foo(str, **@ --> int32) is native is symbol('printf') {*}
foo "The answer: %d\n", 42;  # The answer: 42

The printf() C-function is a good example of using varargs.

Support for pseudo-terminals (PTY)

Writing terminal applications has become much simpler with the support for pseudo-terminals that Patrick Böker has written. This Advent post explains the how and why of this development, and the new Anolis terminal emulator module.

Support is still a bit raw around the edges: pretty sure the coming year will see a lot of smoothing over, so that it can e.g. be used to create a full featured terminal-based Raku debugger!

Hash.new(a => 42, b => 666)

A common beginner mistake was trying to create a new Hash (or Map) object by using .new and named arguments. This would silently create an empty Hash (or Map) because by default any unexpected named arguments are ignored in calls to .new. This was changed so that if Hash.new (or Map.new) is called with named arguments only, they will be interpreted as key/value pairs to be put into the Hash (or Map):

dd Hash.new(a => 42, b => 666);
# {:a(42), :b(666)}
dd Map.new(a => 42, b => 666);
# Map.new((:a(42),:b(666)))

exit-ok

The Test module now also provides an exit-ok tester subroutine, making it a lot easier to test the exit behaviour of a piece of code. It takes a Callable, and an integer value. It expects the code to execute an exit statement, and will then compare the (implicitly) given exit value with the given integer value.

use Test;
exit-ok { exit 1 }, 1;
# ok 1 - Was the exit code 1?
exit-ok { exit }, 1;
# not ok 2 - Was the exit code 1?
exit-ok { 42 }, 0;
# not ok 3 - Code did not exit, no exit value to check

Language changes (6.e.PREVIEW)

The most notable additions to the future language level of the Raku Programming Language:

Hash::Ordered

An implementation of ordered hashes (a hash in which the order of the keys is determined by order of addition) has become available:

use v6.e.PREVIEW;
my %h is Hash::Ordered = "a".."e" Z=> 1..5;
say %h.keys;    # [a b c d e]
say %h.values;  # (1 2 3 4 5)

This will probably get some easier syntactic sugar. Until then, the above syntax can be used.

Language changes in RakuAST

The following changes are only seen when using RakuAST (by calling raku with the RAKUDO_RAKUAST=1 environment variable set), and thus available by default when the next language level is released.

Setting default language version

The RAKU_LANGUAGE_VERSION environment variable can be used to indicate the default language level with which to compile any Raku source code. Note that this does not affect any explicit language versions specified in the code.

$ RAKUDO_RAKUAST=1 RAKU_LANGUAGE_VERSION=6.e.PREVIEW raku -e 'say nano'
1766430145418821670
$ RAKUDO_RAKUAST=1 RAKU_LANGUAGE_VERSION=6.e.PREVIEW raku -e 'use v6.d; say nano'
===SORRY!=== Error while compiling -e
Undeclared routine:
    nano used at line 1

Although of limited use while RakuAST is not yet the default, it will make it a lot easier to check the behaviour of code at different language levels, e.g. when running tests in the official test-suite (aka roast).

$?SOURCE, $?CHECKSUM

The compile-time variables $?SOURCE and $?CHECKSUM have been added. The $?SOURCE compile-time variable contains the source of the current compilation unit. If for some reason one doesn’t want that to be included in the bytecode, then the RAKUDO_OMIT_SOURCE environment variable can be set.

The $?CHECKSUM compile-time variable contains a SHA1 digest of the source code.

$ RAKUDO_RAKUAST=1 raku -e 'say $?SOURCE'
say $?SOURCE
$ RAKUDO_RAKUAST=1 raku -e 'say $?CHECKSUM'
81892BA38B9BD6930380BD81DB948E4D7A9C14E7
$ RAKUDO_RAKUAST=1 RAKUDO_OMIT_SOURCE=1 raku -e 'say $?SOURCE'
Nil

These additions are intended to be used by the MoarVM runtime debugger, as well as by packagers for verification.

Localization

Most of the localization work has been removed from the Rakudo core and put into separate Raku-L10N project. And there it gained a few new contributors! For a progress report, checkout out habere-et-dipertire‘s advent blog post titled Hallo, Wêreld!

To make it easier to work with code in a specific localization, each localization comes with a “fun” command line script, and an official one. So for instance, the Dutch localization has a “dutku” (for dutch raku) executable (well, actually a CLI script), but also a “kaas” one. Same for French (“freku” and “brie”), etc. The fun one is usually associated with a favourite foodstuff of the language in question.

$ dutku -e 'zeg "foo"'
foo
$ kaas -e 'zeg "foo"'
foo
$ freku -e 'dis "foo"'
foo
$ brie -e 'dis "foo"'
foo

RakuDoc

The RakuDoc v2.0 specification was completed in December 2024, and 2025 was spent implementing it. A compliant renderer is now available by installing the Rakuast::RakuDoc::Render distribution. Work then began on a document management system called Elucid8 which renders the whole Raku documentation (development preview).

From September onwards, Damian Conway and Richard Hainsworth worked on a enumeration system (originally envisioned for RakuDoc v3.0) so that any block – meaning any paragraph, heading, code snippet, formula, etc – can be enumerated simply by prefixing the blocktype with num. It is a far more flexible system than anything encountered in the editor space.

The RakuDoc specification is now at version 2.20.2. The new enumeration specification has not yet been merged to main because work is still being done on getting Rakuast::RakuDoc::Render to implement the new standard.

It really looks like RakuDoc has the potential to becoming the markup language for any type of serious documentation, and a direct competitor to markdown.

Ecosystem

The Raku ecosystem has seen quite a lot of developments. Many modules got moved to the Raku Community Modules Adoption Center where they got a new lease on life. But there were also quite a few new modules, and interesting updates to existing modules in 2025.

Statistics

According to raku.land in 2025, 503 Raku modules have been updated (or first released): up from 367 in 2024 (an increase of 37%). There are now 2431 different modules installable by zef by just mentioning their name. And there are now 13808 different versions of Raku modules available from the Raku Ecosystem Archive, up from 12181 in 2024, which means more than 4.4 module updates / day on average in 2025 (up from 3.9 updates / day).

Interesting new modules

The modules that yours truly found interesting, so a very personal list! In alphabetical order:

Modules with notable updates

Also in alphabetical order:

Bots

A new experimental bot has appeared on the #raku-dev IRC channel: rakkable. It is basically an interactive front end for the new “rakudo-xxx” features of App::Rak. Which in turn is based on the new Ecosystem::Cache module. This allows easy searching in all most current versions of modules in the ecosystem.

For instance: look in the Raku ecosystem for code mentioned in “provides” sections that contain the string “Lock.new” and which also have the string “$!lock”:

<lizmat> rakkable: eco-provides Lock.new --and=$!lock
<rakkable> Running: eco-provides Lock.new --and=$!lock, please be patient!
<rakkable> Found 30 lines in 25 files (24 distributions):
<rakkable> https://gist.github.com/fa2424aebf085ea656b436c63722bf9d

The bot currently only lives on the #raku-dev, but can also be accessed directly without needing the “rakkable:” prefix.

Non-technical stuff

Yes, there’s also non-technical stuff in the Raku world!

Websites

The raku.org website has been completely renewed, thanks to Steve Roe who has taken that on. It’s now completely dogfooded: hypered with htmx. Aloft on Åir. Constructed in cro. Written in raku.  &  Styled by picocss.

Documentation

The Raku Documentation Project has gained quite a few collaborators, who are working on making the Raku documentation more accessible to new users. One factor making this easier, is that the CI testing for the documentation has become about 4x as fast by using RakuAST RakuDoc parsing.

Social Media

Yours truly stopped using what is now X (formerly Twitter). It hurt. But Bluesky and Mastodon are good alternatives, and the people important to yours truly have moved to them. If you haven’t yet, you probably should. As well as the people important to you.

Please be sure to mention the #rakulang tag when posting about the Raku Programming Language!

Conference / Core Summit

Sadly it has turned out to be impossible to organize a Raku Conference (neither in-person or online) this year. Hoping for better times next year! It just really depends on people wanting to put in the effort!

It was possible to organize a second Raku Core Summit in 2025!

Weekly

The Rakudo Weekly News has been brought to you by Steve Roe in the 2nd half of 2025 (and for the foreseeable future). With some new features, such as code gists! Kudos!

Problem Solving

The Problem Solving repository has seen an influx of 36 new issues the past year. They all deserve your attention and your feedback! Some of them specifically ask for your ideas, such as:

Please, don’t be shy and have your voice heard!

Raku Steering Council

Sadly, Vadim Belman and Stefan Seifert have indicated that they wanted to step down from the Raku Steering Council. They are thanked for all that they have done for Raku, the Raku Community in general, and the Raku Steering Council in particular.

John Haltiwanger has accepted an invitation to join the Raku Steering Council. The seat opened by Stefan Seifert will not be filled for at least the coming 6 months.

The Raku Foundation

After writing a blog post about a Raku Foundation, a problem solving issue and many discussions at the second Raku Core Summit, there finally is a version of the Raku Foundation Documents that everybody can agree on (at least for now).

Yours truly urges the readers to check out The Articles Of Association, as these will be very hard to change once the foundation is established.

If you are really interested in this kind of stuff, please check out the Regulations for the operation of the Raku Foundation as well.

And if you would like to become part of the initial Executive Board or the Supervisory Board, please send an email to founding@raku.foundation.

Summary

Looking back, again an amazing amount of work has been done in 2025! And not only on the technical side of things!

Hopefully you will all be able to enjoy the Holiday Season with sufficient R&R. Especially Kane Valentine (aka kawaii) who is still going strong in their new role:

And on that note: Слава Україні!  Героям слава!

The next Raku Advent Blog is only 340 days away!

Day 24 – Maze Making Using Graphs

Introduction

This document (notebook) describes three ways of making mazes (or labyrinths) using graphs. The first two are based on rectangular grids; the third on a hexagonal grid.

All computational graph features discussed here are provided by “Graph”, [AAp1]. The package used for the construction of the third, hexagonal graph is “Math::Nearest”, [AAp2].

TL;DR

Just see the maze pictures below. (And try to solve the mazes.)

Procedure outline

The first maze is made by a simple procedure which is actually some sort of cheating:

  • A regular rectangular grid graph is generated with random weights associated with its edges.
  • The (minimum) spanning tree for that graph is found.
  • That tree is plotted with exaggeratedly large vertices and edges, so the graph plot looks like a maze.
    • This is “the cheat” — the maze walls are not given by the graph.

The second maze is made “properly”:

  • Two interlacing regular rectangular grid graphs are created.
  • The second one has one less row and one less column than the first.
  • The vertex coordinates of the second graph are at the centers of the rectangles of the first graph.
  • The first graph provides the maze walls; the second graph is used to make paths through the maze.
    • In other words, to create a solvable maze.
  • Again, random weights are assigned to edges of the second graph, and a minimum spanning tree is found.
  • There is a convenient formula that allows using the spanning tree edges to remove edges from the first graph.
  • In that way, a proper maze is derived.

The third maze is again made “properly” using the procedure above with two modifications:

  • Two interlacing regular grid graphs are created: one over a hexagonal grid, the other over a triangular grid.
    • The hexagonal grid graph provides the maze walls; the triangular grid graph provides the maze paths.
  • Since the formula for wall removal is hard to derive, a more robust and universal method based on nearest neighbors is used.

Setup

Packages

Here are the packages loaded for use in the rest of the notebook:

use Graph;
use Graph::Classes;

use Math::DistanceFunctions;
use Math::Nearest;

use Data::TypeSystem;
use Data::Translators;
use Data::Generators;

Conversion

This sub is used to invoke the Graphviz graph layout engines:

sub dot-svg($input, Str:D :$engine = 'dot', Str:D :$format = 'svg') {
    my $temp-file = $*TMPDIR.child("temp-graph.dot");
    $temp-file.spurt: $input;
    my $svg-output = run($engine, $temp-file, "-T$format", :out).out.slurp-rest;
    unlink $temp-file;
    return $svg-output;
}


Simple Maze

In this section, we create a simple, “cheating” maze.

Remark: The steps are easy to follow, given the procedure outlined in the introduction.

# Maze dimensions
my ($n, $m) = (10, 25);

# Base grid graph
my $g = Graph::Grid.new($n, $m, :!directed);

# Using the edges of the grid graph make a new graph assigned random edge weights
my $gWeighted = Graph.new($g.edges(:dataset).map({ $_<weight> = random-real([10,1000]); $_}))

# Graph(vertexes => 250, edges => 465, directed => False)

Find the spanning tree of the graph:

my $mazePath = $gWeighted.find-spanning-tree

# Graph(vertexes => 250, edges => 249, directed => False)

Shortest path from the first vertex (bottom-left) to the last vertex (top-right):

my @path = $mazePath.find-shortest-path('0_0', "{$n-1}_{$m-1}");
@path.elems

# 56

Graph plot:

#% html
my $simpleMaze = Graph.new($mazePath.edges);
$simpleMaze.vertex-coordinates = $g.vertex-coordinates;

my %opts =
    background => "Black",
    :!node-labels,
    :!edge-lables,
    node-shape => 'square', 
    node-width => 0.7, 
    node-color => 'SteelBlue',
    node-fill-color => 'SteelBlue',
    edge-thickness => 52, 
    edge-color => 'SteelBlue',
    size => '10,6!',
    engine => 'neato';

$simpleMaze.dot(|%opts):svg

The “maze” above looks like a maze because the vertices and edges are rectangular with matching sizes, and they are thicker than the spaces between them. In other words, we are cheating.

To make that cheating construction clearer, let us plot the shortest path from the bottom left to the top right and color the edges in pink (salmon) and the vertices in red:

#%html
my $gPath = Graph::Path.new(@path);
$simpleMaze.dot(highlight => {Salmon => $gPath.edge-list, Red => $gPath.vertex-list}, |%opts):svg


Proper Maze

proper maze is a maze given with its walls (not with the space between walls).

Remark: For didactical reasons, the maze in this section is small so that the steps—outlined in the introduction—can be easily followed.

Make two regular graphs: one for the maze walls and the other for the maze paths.

# Maze dimensions
my ($n, $m) = (6, 12) »*» 1;

# Walls graph
my $g1 = Graph::Grid.new($n, $m, prefix => 'w');

# Space graph
my $g2 = Graph::Grid.new($n-1, $m-1);
$g2.vertex-coordinates = $g2.vertex-coordinates.map({ $_.key => $_.value >>+>> 0.5 }).Hash;

$g2

# Graph(vertexes => 55, edges => 94, directed => False)

Maze Path Graph:

my $mazePath = Graph.new($g2.edges(:dataset).map({ $_<weight> = random-real([10,1000]); $_}));
$mazePath = $mazePath.find-spanning-tree;
$mazePath.vertex-coordinates = $g2.vertex-coordinates;

$mazePath

# Graph(vertexes => 55, edges => 54, directed => False)

Combined Graph:

my $g3 = Graph.new([|$g1.edges, |$mazePath.edges]);
$g3.vertex-coordinates = [|$g1.vertex-coordinates, |$mazePath.vertex-coordinates].Hash;

$g3

# Graph(vertexes => 127, edges => 180, directed => False)

Plot the combined graph:

#% html
$g3.dot(
    highlight => $mazePath,
    :node-labels,
    background => "Black",
    node-width => 0.45,
    node-height => 0.2,
    edge-width => 4,
    size => '10,10!',
    engine => 'neato'
):svg

Remove wall edges using a formula:

my $g4 = $g3.clone;
for $mazePath.edges -> $e {

    my ($i, $j) = |$e.key.split('_')».Int;
    my ($i2, $j2) = |$e.value.split('_')».Int;
    
    if $i2 < $i || $j2 < $j { 
        ($i2, $j2, $i, $j) = ($i, $j, $i2, $j2)
    }

    # Horizontal
    if $i == $i2 && $j < $j2 {
        $g4 = $g4.edge-delete( "w{$i2}_{$j2}" => "w{$i2+1}_{$j2}")
    }

    # Vertical
    if $j == $j2 && $i < $i2 {
        $g4 = $g4.edge-delete( "w{$i2}_{$j2}" => "w{$i2}_{$j2+1}")
    }
}

$g4

# Graph(vertexes => 127, edges => 126, directed => False)

Plot wall graph and maze space graph:

#% html
$g4.dot(
    highlight => $mazePath,
    :!node-labels,
    background => "Black",
    node-width => 0.25,
    node-height => 0.2,
    edge-width => 4,
    size => '10,10!',
    engine => 'neato'
):svg

Fancier maze presentation with rectangular vertices and edges (with matching sizes):

#% html
my $g5 = $g4.subgraph($g1.vertex-list);

my @path = $mazePath.find-shortest-path('0_0', "{$n-2}_{$m-2}");
say @path.elems;

my @mazeStartEnd = "w0_0", w0_0 => "w0_1", w0_0 => "w1_0", "w{$n-1}_{$m-1}", "w{$n-1}_{$m-1}" => "w{$n-1}_{$m-2}", "w{$n-1}_{$m-1}" => "w{$n-2}_{$m-1}";

$g4.dot(
    highlight => {'#1F1F1F' => [|$mazePath.vertex-list, |$mazePath.edge-list, |@mazeStartEnd], Orange => @path},
    #highlight => {'#1F1F1F' => @mazeStartEnd},
    background => '#1F1F1F',
    size => '10,10!',
    :!node-labels,
    node-shape => 'square',
    node-color => 'SteelBlue',
    node-fill-color => 'SteelBlue',
    node-width => 0.4,
    edge-width => 30,
    engine => 'neato'
):svg


Hexagonal Version

Let us create another maze based on a hexagonal grid. Here are two grid graphs:

  • The first is a hexagonal grid graph representing the maze’s walls.
  • The second graph is a triangular grid graph with one fewer row and column, and shifted vertex coordinates.
# Maze dimensions
my ($n, $m) = (6, 10) »*» 2;

# Walls graph
my $g1 = Graph::HexagonalGrid.new($n, $m, prefix => 'w');

# Space graph
my $g2 = Graph::TriangularGrid.new($n-1, $m-1);
$g2.vertex-coordinates = $g2.vertex-coordinates.map({ $_.key => $_.value >>+<< [sqrt(3), 1 ] }).Hash;

$g2

Graph(vertexes => 240, edges => 657, directed => False)

#% html
$g1.union($g2).dot(
    highlight => $g2,
    :!node-labels,
    background => "#1F1F1F",
    node-width => 0.85,
    node-height => 0.85,
    node-font-size => 28,
    node-shape => 'hexagon',
    edge-width => 7,
    size => '10,10!',
    engine => 'neato'
):svg

Maze Path Graph:

my $mazePath = Graph.new($g2.edges(:dataset).map({ $_<weight> = random-real([10,1000]); $_}));
$mazePath = $mazePath.find-spanning-tree;
$mazePath = Graph.new($mazePath.edges);
$mazePath.vertex-coordinates = $g2.vertex-coordinates;

$mazePath

# Graph(vertexes => 240, edges => 239, directed => False)

Combine the walls-maze and the maze-path graphs (i.e., make a union of them), and plot the resulting graph:

#% html

my $g3 = $g1.union($mazePath);

$g3.dot(
    highlight => $mazePath,
    :node-labels,
    background => "#1F1F1F",
    node-width => 0.85,
    node-height => 0.85,
    node-font-size => 28,
    node-shape => 'hexagon',
    edge-width => 7,
    size => '10,10!',
    engine => 'neato'
):svg

Make a nearest neighbor points finder functor:

my &finder = nearest($g1.vertex-coordinates.Array, method => 'KDTree');


Math::Nearest::Finder(Algorithm::KDimensionalTree(points => 544, distance-function => &euclidean-distance, labels => 544))

Take a maze edge and its vertex points:

my $e = $mazePath.edges.head;
my @points = $g2.vertex-coordinates{($e.kv)};

# [[-3.4641016151381225 -2] [-1.7320508075691228 1]]

Find the edge’s midpoint and the nearest wall-graph vertices:

my @m = |((@points.head <<+>> @points.tail) >>/>> 2);
say "Mean edge point: {@m}";
my @vs = |&finder.nearest(@m, 2, prop => <label>).flat;
say "To remove: {@vs}";

# Mean edge point: -2.5980762113536224 -0.5
# To remove: w3 w6

Loop over all maze edges, removing wall-maze edges:

my $g4 = $g1.clone;
for $mazePath.edges -> $e {
    my @points = $g2.vertex-coordinates{($e.kv)};
    my @m = |((@points.head <<+>> @points.tail) >>/>> 2);
    my @vs = |&finder.nearest(@m, 2, prop => <label>).flat;
    $g4 = $g4.edge-delete(@vs.head => @vs.tail);
}

$g4

# Graph(vertexes => 544, edges => 544, directed => False)

The start and end points of the maze:

my ($start, $end) = $g4.vertex-list.head, $g4.vertex-list.sort({ $_.substr(1).Int }).tail;

# (w0 w543)

Finding the Maze Solution:

my $solution = Graph::Path.new: $mazePath.find-shortest-path(|$mazePath.vertex-list.sort(*.Int)[0,*-1]);
$solution.vertex-coordinates = $mazePath.vertex-coordinates.grep({$_.key ∈ $solution.vertex-list }).Hash;

$solution

# Graph(vertexes => 50, edges => 49, directed => False)

Plot the maze:

#% html

my @mazeStartEnd = $start, $end, |$g4.neighborhood-graph([$start, $end]).edges;

my $g5 = $g4.union($solution);

my %opts = 
    :!node-labels,
    background => "#1F1F1F",
    node-width => 0.8,
    node-height => 0.8,
    node-shape => 'circle',
    edge-width => 40,
    size => '10,10!',
    engine => 'neato';

$g4.dot(highlight => {'#1F1F1F' => @mazeStartEnd}, |%opts):svg

(Here is the solution of the maze).


Additional Comments

  • The initial (and unfinished) version of this document was created 13 months ago.
    • Its completion was postponed because the blog post “Day 12 – Graphs in Raku”, [AA1], featured many of the graph operations in “Graph”, [AAp1].
      • (Well, until this Raku Advent effort…)
  • The document demonstrates how feature-rich the package “Graph” is.
  • Here are the special graph functionalities used to create the mazes:

References

Articles, Blog Posts

[AA1] Anton Antonov, “Day 12 – Graphs in Raku”, (2024), Raku Advent Calendar at WordPress.

Packages

[AAp1] Anton Antonov, Graph, Raku package, (2024–2025), GitHub/antononcube.

[AAp2] Anton Antonov, Math::Nearest, Raku package, (2024), GitHub/antononcube.

Videos

[AAv1] Anton Antonov, “Graphs” videos playlist, (2024-2025), YouTube/@AAA4prediction.

Day 23 – A Day Late and A Fish Short

Hello again! I return during this week of winter solstice to tell you about my experience participating in the Langjam Gamejam. I planned to use Raku, partially so that you could have an advent blogpost to read today, but also because Raku’s builtin support for grammars ensure that I would not get stuck when writing my parser.

Preparation

I did a few things to ensure that I would be able to complete the game jam. The first was to be realistic about what I could achieve; with seven days of time, I could realistically expect to produce about three weekends worth of working code. This is only going to be a few hundred lines, maybe one or two files total, and prioritizing basic functionality over any fancy features.

I decided to make an idle game.

There’s no rule against deciding that ahead of time. I also decided to use Raku. That’s allowed too. I read the source of a couple different idle games too, mostly to get ideas of what not to do. In particular, big thanks to Idle Game Maker by Orteil, Antimatter Dimensions by Ivar K., and Trimps by the Trimp authors for publishing reasonably-readable source code. All three of these have some notion of programmability, although I ended up doing something quite different.

Sunday

The first place to start when making a game is with the scenario that the
player will be asked to experience. Games are fundamentally about roleplay,
and roles only exist within scenarios. Fortunately, I didn’t have to come up
with any of this.

Instead, I asked my friend Remi to come up with something interesting. We spent a couple hours adapting an idea for a Zelda-style fishing minigame, with the novel twist that the game would mechanically be an idle game; instead of putting in the athletic effort to retrieve the fish, the player manages a fishing business and delegates the work to employees.

It’s important to allow a genuine brainstorm at the beginning of the process.
Yes-and reasoning is essential for developing concepts. At the same time, it was important that we not plan for work that we couldn’t schedule. Remi committed to drawing a few icons and I committed to carrying out the core of the gamejam objectives:

  • Design and implement a programming language
  • Design and implement a game using that language

Monday

The first place to start when making a language is with the objects of that language. I don’t mean object-oriented design but the idea that the language expresses some sort of manipulation of reality. What are we manipulating? How do we manipulate them?

At a high level, idle and incremental games are about resource management. They can also include capacity planning. Thus, I decided that resources are the main objects of study. I also needed some way for the player to interact with the game world, and clickable buttons are a traditional way to express idle games. For each button, I’d thought to have a corresponding action in the game, which I’d also express in language.

I also needed to figure out what substrate I’m going to use. I mean, of course I’m using Raku, but how deeply do I want to embed the game? At one end, I could imagine compiling the game into a single blob which runs wholly in the end user’s windowing system or Web browser, so that there’s nothing of Raku in the end product.

At the other end, I could imagine shallowly embedding the game by writing some Raku subroutines and having the game developer write in ordinary Raku. I initially decided to go with the shallowest embedding that would still allow me to use Raku’s syntax for arithmetic: a Raku sublanguage, or Raku slang. Technically, a slang
is its own programming language, or so I was prepared to argue.

One open question concerned the passage of time. A resource evolves in time, perhaps growing or shrinking; it also has some invariants in time, particularly identity. What did I actually want to store internally? All hand-coded games devolve into a soup of objects cross-referenced by string-keyed maps, or at least that was the case for a half-dozen idle games that I’ve looked at. Maybe we can organize all of that into one big string-keyed map?

Another question is how the game will be experienced. I’d assumed that it should be possible to put up some simple HTTP server and run it locally. My notes are unclear, but I think that this is around the first time that I took a serious look at Humming-Bird.

Tuesday

Let’s actually write some code. I started by writing a slang that abused the Raku metamodel. I was inspired by OO::Actors, which introduces an actor keyword, as well as the implementation of the standard grammar keyword. I can just introduce my own resource and action keywords which manage some subexpressions, including Raku arithmetic, and that’ll be my language. To prototype this, I first wrote out a file which I want to be able to load, and then I wrote the parser which handles it.

Here’s a snippet from that first prototype:

resource fishmonger {
  flavor-text "employee with knives and a booming voice for telling stories";
  eats seafood by 0.017 per second;
  eats bux by 15 per second;
  converts from seafood into bux by 75 per second;
}
action hire-fishmonger {
  flavor-text "employ a fishmonger to sell seafood";
  costs 10 bux;
  pays 1 fishmonger;
}

Several features are very important here. One big deal is that flavor text is inalienable from the resources and actions. I was very conflicted about this. Languages like Idle Game Maker are basically enlightened CSS and HTML; they are extremely concerned with presentation details rather than getting to the essential mechanics and handling time.

At the same time, Remi and I are both big fans of flavor text both for its immersive value as well as for its ability to create a memorable experience. Another important idea here is that costs and pays are two distinct attributes in concrete syntax, even though they’re going to be implemented as the same underlying sort of amount-and-currency pair.

This syntax is a little heavy. I was imagining that this would be a sort of Ruby or Tcl DSL where each command takes a row of arguments, some of which are literal tokens, and imperatively builds the corresponding resources and actions.

At this point, the scenario is complete. The game will have a few natural resources, like plankton, fish, and sharks; a conversion from fish to seafood; employees like fishers, fishmongers, and white mages; and an enhancement that white mages apply to fishers. There is no objective; it’s a population-dynamics sandbox.

Wednesday

After a day of trying to understand Raku’s metamodel, I concluded that a slang is the wrong layer of integration. I really wanted to run an input file through a parser in order to build a small nest of Resource and Action objects in memory, set up an HTTP server displaying them, and repeatedly take one tick per second, integrating changes over all of those objects.

This was a gumption trap for me; I completely lost motivation for a few hours. In those times, it is essential to allow one’s emotions to flow in order to move past them, and also essential to rest in order to restore energy.

After recovering a bit, I cleaned up my repository and thought about what I should do next. I might as well write a proper grammar. What should that language look like? I agonized over this for a few minutes, went through the possibilities of fixity and bracketing, and eventually decided that a nice little S-expression language would work for my needs.

This did mean that I would need to internalize arithmetic, but I also knew one of the standard cheats of game development: it’s okay to not implement arithmetic operations which aren’t actually used. Consider the following snippet:

(resource plankton 1e15
    "little crunchy floaty things"
    (growth 0.004 /s)
    (view water-color (if (< .count 1e20) "clear" "cloudy"))
    (view concentration (str (/ .count 2e14)))
)
(action look-at-water-color
    "gaze at the ocean"
    (enables view plankton water-color)
)
(action measure-plankton
    "buy a plankton meter and put it in the water"
    (enables view plankton concentration)
    (costs 10 bux)
)

This is from my prototype. The only arithmetic that’s required is in the views, which format internal numbers about resources into strings. For those, I have a mini-language which allows the user to specify any arithmetic they want, as long as it’s either division or less-than comparisons. The formatting language is strongly typed; the parser won’t allow a non-Boolean operation as an if conditional, for example.

Some other design decisions stand out. Flavor text is now required. Resources have starting counts, which are also required. Rates always end with “/s”, an abbreviation for “per second” that is supposed to easily distinguish them from non-rates.

Gumption management requires not just succeeding, but having a feeling of understanding and competence. I probably could have started on the parser that night, but instead I walked to the bar and speedran Zelda 3, doing any% No Major Glitches and finishing in about two hours and change. Not a superb time, because I grabbed quite a few backups, including an entire extra heart and two bottles; but I didn’t die. The parser can wait until tomorrow.

Thursday

Parsing an S-expression is really easy, especially when the list of special forms (the words that can legally follow an opening parenthesis) is short. For each special form, we have a rule that parses each of the required components in order, followed by an optional zero-or-more collection of modifiers / attributes / members / components / accessories. The resource special form from Wednesday is parsed with a rule like:

    rule thing:sym { '(resource'   <s> * ')' }

The parser bottoms out on some very simple tokens. For parsing numbers, we parse a subset of what Raku accepts and then use .Rat or .Num methods to convert those strings to live values by reusing Raku’s parser. I may not have been able to reuse arithmetic but I was certainly able to reuse the numerals!

    token id { + }
    token s { '"' + '"' }
    token n { + ('.' +)? ('e' +)? }

As I wired up the parser, I also set up a Humming-Bird application. I’m a fan of Ruby’s Sinatra and Python’s Flask, so it seemed like Humming-Bird would be a good fit for me. It doesn’t come with a preferred HTML-emitting library, so I tried a few options. I started with HTML::Tag, which I had added to the project on either Tuesday or Wednesday, but after a few minutes of practical usage, it became totally unusable due to syntactic zones of ceremony (Subramaniam, Seemann, myself): making a fresh HTML tag requires many source characters. I ended up using HTML::Functional, which is much lighter-weight but occasionally allows me to misuse Nil as a string.

I’m hacking out two roles. I’m presenting them here in their final versions; initially they didn’t take any parameters, which was too restrictive. One role is for rendering HTML and the other role is for evolving with each tick.

The %context is all of the resources and actions, and the $resource is the resource currently being acted upon. This sort of late-bound approach is technically too flexible for what I’m building, but I don’t feel like restricting it.

    role Render { method html(%context, $resource) { ... } }
    role Evolve { method tick(%context, $resource) {;} }

I committed, pushed, and asked Remi for feedback.

Friday

Remi approves! They’ll make a few cute little icons for some resources. At this point, I stopped and reflected upon what I’d made so far. Pastafarians typically take Fridays off, and I’m not about to work when I could rest instead. What works? What doesn’t work? Where should I spend the rest of my time? What should I have for dinner?

The parser works. The Resource and Action objects operate as nodes of an AST. Exporting the AST as HTML with Render.html() works. Traversing the AST for a tick with Evolve.tick() also works. The Nix environment, which Ihaven’t mentioned yet, also works; I’m using direnv to configure the environment and
install Raku packages.

Arithmetic operations don’t work yet. Actions don’t actually act on anything. Remi’s artwork isn’t visible and I haven’t split out the CSS yet.

I should spend the rest of my time getting the core mechanics of rendering and evolution to work properly. Easy to say; harder to do.

For dinner, I’ll have noodles of some sort, because it’s Friday. I ended up having spaghetti and meatballs in red sauce.

Before dinner, I went to the bar and speedran Mario 1. I played for about 90 minutes but I didn’t finish a single run. On 8-3, the penultimate level, I was repeatedly defeated by a gauntlet of tough enemies.

Saturday

Humming-Bird got in my way a little; it blocks by default and the
documentation doesn’t explain how to fix it. After reading the relevant code,
I had to change this line:

    listen(8080);

To have a non-blocking annotation:

    listen(8080, :no-block);

I also explore how to perform ticks in the background. I do find Supply.interval, but that will let the interpreter exit. Instead, I end up with the following hack:

    while 1 { sleep 1; $venture.tick };

As I wired up operations and fixing display bugs, I became increasingly stressed as my CSS changes aren’t being applied. By doing some testing, I discovered that the Humming-Bird convenience helper for attaching static directories is not working. I had initially written:

    static('/static', 'static');

But this doesn’t work, or it had worked on Friday but not on Saturday, or I had somehow mistyped “static”, or any of a dozen other impossible considerations. I knew that I can’t get distracted by this, and I finished up all of the rest of the functionality; the game works, but it’s not styled and Remi’s artwork isn’t visible.

That’s the end of the game jam. I produced a language and a game. However, the game doesn’t display properly and I wouldn’t consider it to be playable. What a frustration.

Sunday

I’m not done yet! I want to ensure that the release version has Remi’s artwork displayed. First, I hand-wrote the static routes; these do correctly route the CSS and images.

    $router.get('/static/style.css', -> $req, $resp {
        $resp.file('static/style.css');
    });
    $router.get('/static/icons/:icon', -> $req, $resp {
        my $name = $req.param('icon');
        1$name.contains('..') ?? $resp.status(400) !! $resp.file('static/icons/' ~ $name);
    });

I found a few holes in our scenario. For example, there’s no way to see how many Bux the user has. By default, resource amounts are hidden both to keep the UI uncluttered and to provide a sense of mystery; however, for Bux or seafood, we want to give the user precise numbers. Our existing syntax can fully accommodate this! The view is enabled by an action which doesn’t have any line items (costs or pays) and it prints the .count variable as-is.

(resource bux 1000
    "wireless cash"
    (view cash-on-hand (str .count))
)
(action check-balance
    "become aware of our earnings"
    (enables view bux cash-on-hand)
)

With these two fixes, we now have a working interface that allows the scenario to be fully accessed! The new view looks like this:

Finally, I’m not going to be able to deploy this version as written. I’ll have to do some reading on production HTTP setups for Raku. When the game loads, it tries to load one image for every visible resource. If more than about five images are requested then the page fails to load. Every action is an HTTP POST which causes everything to reload again.

I imagine that this is properly fixed by adding entity tags to the HTTP backend so that images can be cached. For example, I took the screenshot in the header of this blog post while Firefox was still considering whether it could load the image for plankton; it eventually gives up:

Every load of the page causes a different set of images to not load. This is an unacceptable game experience.

Closing thoughts

The idea of a declarative idle-game maker is reasonable and it was only a week’s effort to prototype a basic interpreter which simulates a simple scenario. I think that the biggest time sinks were trying to make a slang instead of a deeper language with a parser, fighting with Humming-Bird, and generally trying to keep code clean. On that last point: I found that cleaning up my code was necessary to let bugs and mistakes become visible.

The game comes out to about three hundred lines of Raku and fewer than one hundred lines of S-expressions. It’s within my coding budget for sure. I don’t think that I went for more than I could reasonably accomplish. The entire code is available in this gist. Remi quite reasonably doesn’t want their art uploaded to GitHub, but you can check out more of their work at their website.

Have a happy winter solstice and Holiday!

Day 22 – Numerically 2026 Is Unremarkable Yet Happy

Introduction

This document (notebook) discusses number theory properties and relationships of the integer 2026.

The integer 2026 is semiprime and a happy number, with 365 as one of its primitive roots. Although 2026 may not be particularly noteworthy in number theory, this provides a great excuse to create various elaborate visualizations that reveal some interesting aspects of the number.

The computations in this document are done with the Raku package  “Math::NumberTheory”, [AAp1].


Setup

use Math::NumberTheory;
use Math::NumberTheory::Utilities;

use Data::Importers;
use Data::Translators;
use Data::TypeSystem;

use Graph;
use Graph::Classes;
use JavaScript::D3;

Notebook priming code:

#%javascript
require.config({
     paths: {
     d3: 'https://d3js.org/d3.v7.min'
}});

require(['d3'], function(d3) {
     console.log(d3);
});


2026 Is a Happy Semiprime with Primitive Roots

First, 2026 is obviously not prime—it is divisible by 2—but dividing it by 2 gives a prime, 1013:

is-prime(2026 / 2)

# True

Hence, 2026 is a semiprime. The integer 1013 is not a Gaussian prime, though:

is-prime(1013, :gaussian-integers)

# False

happy number is a number for which iteratively summing the squares of its digits eventually reaches 1 (e.g., 13 → 10 → 1).
Here is a check that 2026 is happy:

is-happy-number(2026)

# True

Here is the corresponding trail of digit-square sums:

is-happy-number(2026, :trail).tail.head(*-1).join(' → ')

# 2026 → 44 → 32 → 13 → 10 → 1

Not many years in this century are happy numbers:

(2000...2100).grep({ is-happy-number($_) })

# (2003 2008 2019 2026 2030 2036 2039 2062 2063 2080 2091 2093)

The decomposition of 2026 as 2 * 1013 means the multiplicative group modulo 2026 has primitive roots. A primitive root exists for an integer n if and only if n is 1, 2, 4, p^k, or 2 p^k, where p is an odd prime and k > 0.

We can check additional facts about 2026, such as whether it is “square-free”, among other properties. However, let us compare these with the feature-rich 2025 in the next section.


Comparison with 2025

Here is a side-by-side comparison of key number theory properties for 2025 and 2026.

Property20252026Notes
Prime or CompositeCompositeCompositeBoth non-prime.
Prime Factorization3⁴ × 5² (81 × 25)2 × 10132025 has repeated small primes; 2026 is a semiprime (product of two distinct primes).
Number of Divisors15 (highly composite for its size)4 (1, 2, 1013, 2026)2025 has many divisors; 2026 has very few.
Perfect SquareYes (45² = 2025)NoMajor highlight for 2025—rare square year.
Sum of CubesYes (1³ + 2³ + … + 9³ = (1 + … + 9)² = 2025)NoIconic property for 2025 (Nicomachus’s theorem).
Happy NumberNo (process leads to cycle including 4)Yes (repeated squared digit sums reach 1)Key point for 2026—its main “happy” trait.
Harshad NumberYes (divisible by 9)No (not divisible by 10)2025 qualifies; 2026 does not.
Primitive RootsNoYesThis is a relatively rare property to have.
Other Notable Traits– (20 + 25)² = 2025
– Sum of first 45 odd numbers
– Deficient number
– Many pattern-based representations
– Even number
– Deficient number
– Few special patterns
2025 is packed with elegant properties; 2026 is more “plain” beyond being happy.
Overall “Interest” LevelHighly interesting—celebrated in math communities for squares, cubes, and patternsRelatively uninteresting—basic semiprime with no standout geometric or sum propertiesReinforces blog’s angle.

To summarize:

  • 2025 stands out as a mathematically rich number, often highlighted in puzzles and articles for its perfect square status and connections to sums of cubes and odd numbers.
  • 2026, in contrast, has fewer flashy properties — it’s a straightforward even semiprime — but it qualifies as a happy number and it has a primitive root.

Here is a computationally generated comparison table of most of the properties found in the table above:

#% html
my &divisors-count = { divisors($_).elems };
<is-prime is-composite divisors-count prime-omega euler-phi is-square-free is-happy-number is-harshad-number is-deficient-number primitive-root>.map({ %(sub => $_, '2025' => ::("&$_")(2025), '2026' => ::("&$_")(2026) ) })
==> to-html(field-names => ['sub', '2025', '2026'], align => 'left')

sub20252026
is-primeFalseFalse
is-compositeTrueTrue
divisors-count154
prime-omega62
euler-phi10801012
is-square-freeFalseTrue
is-happy-numberFalseTrue
is-harshad-numberTrueFalse
is-deficient-numberTrueTrue
primitive-root(Any)3

Phi Number System

Digits of 2026 represented in the Phi number system:

my @res = phi-number-system(2026);

# [15 13 10 6 -6 -11 -16]

Verification:

@res.map({ ϕ ** $_ }).sum.round(10e-11);

# 2026


Happy Numbers Trail Graph

Let us create and plot a graph showing the trails of all happy numbers less than or equal to 2026. Below, we identify these numbers and their corresponding trails:

my @ns = 1...2026;

my @trails = @ns.map({ is-happy-number($_):trail }).grep(*.head);

deduce-type(@trails)

# Vector((Any), 302)

Here is the corresponding trails graph, highlighting the primes and happy numbers:

#% html
my @prime-too = @trails.grep(*.head).map(*.tail.head).grep(*.&is-prime);
my @happy-too = @ns.grep(*.&is-harshad-number).grep(*.&is-happy-number);

my %highlight = '#006400' => @prime-too».Str,    # Deep Christmas green for primes
                'Blue' => [2026.Str, ],            # Blue for the special year
                '#fbb606ff' => @happy-too».Str;  # Darker gold for joyful numbers

my @edges = @trails.map({ $_.tail.head(*-1).rotor(2 => -1).map({ $_.head.Str => $_.tail.Str }) }).flat;

my $gTrails = Graph.new(@edges):!directed;

$gTrails.dot(
    engine => 'neato', 
    graph-size => 12, 
    vertex-shape => 'ellipse', vertex-height => 0.2, vertex-width => 0.4,
    :10vertex-font-size,
    vertex-color => '#B41E3A',
    vertex-fill-color => '#B41E3A',
    arrowsize => 0.6,
    edge-color => '#B41E3A',
    edge-width => 1.4,
    splines => 'curved',
    :%highlight
):svg


Triangular Numbers

There is a theorem by Gauss stating that any integer can be represented as a sum of at most three triangular numbers. Instead of programming such an integer decomposition representation in Raku, we can simply use Wolfram|Alpha, [AA1, AA3], or wolframscript to find an “interesting” solution:

#% bash
wolframscript -code 'FindInstance[{2026 == PolygonalNumber[i] + PolygonalNumber[j] + PolygonalNumber[k], i > 10, j > 10, k > 10}, {i, j, k}, Integers]'

# {{i -> 11, j -> 19, k -> 59}}

Here, we verify the result using Raku:

say "Triangular numbers : ", <11 19 59>.&polygonal-number(:3sides);
say "Sum : ", <11 19 59>.&polygonal-number(:3sides).sum;

# Triangular numbers : (66 190 1770)
# Sum : 2026


Chord Diagrams

Here is the number of primitive roots of the multiplication group modulo 2026:

primitive-root-list(2026).elems

# 440

Here are chord plots [AA2, AAp1, AAp2, AAv1] corresponding to a few selected primitive roots:

#% js
my $n = 2026;
<339 365 1529>.map( -> $p { 
    my &f = -> $x { power-mod($p, $x, $n) => power-mod($p, $x + 1, $n) };
    
    my @segments = circular-chords-tracing($n, with => &f, :d);
    
    @segments .= map({ $_<group> = $_<group>.Str; $_ });
    
    js-d3-list-line-plot(
        @segments,
        stroke-width => 0.1,
        background => '#1F1F1F',
        :300width, :300height,
        :!axes,
        :!legends,
        :10margins,
        color-scheme => 'Ivory',
        #:$title-color, title => $p
        )
}).join("\n")

Remark: It is interesting that 365 (the number of days in a common calendar year) is a primitive root of the multiplicative group generated by 2026. Not many years have this property this century; many do not have primitive roots at all.

(2000...2100).hyper(:4degree).grep({ 365 ∈ primitive-root-list($_) })

# (2003 2026 2039 2053 2063 2078 2089)


Quartic Graphs

The number 2026 appears in 18 results of the search “2026 graphs” in «The On-line Encyclopedia of Integer Sequences». Here is the first result (from 2025-12-17): A033483, “Number of disconnected 4-valent (or quartic) graphs with n nodes.” Below, we ingest the internal format of A033483’s page:

my $internal = data-import('https://oeis.org/A033483/internal', 'plaintext');
text-stats($internal)

# (chars => 2928 words => 383 lines => 98)

Here, we verify the title:

with $internal.match(/ '%' N (<-[%]>*)? <?before '%'> /) { $0.Str.trim }

# Number of disconnected 4-valent (or quartic) graphs with n nodes.

Here, we get the corresponding sequence:

my @seq = do with data-import('https://oeis.org/A033483/list', 'plaintext').match(/'[' (.*) ']'/) {
    $0.Str.split(',')».trim
}

# [0 0 0 0 0 0 0 0 0 0 1 1 3 8 25 88 378 2026 13351 104595 930586 9124662 96699987 1095469608 13175272208 167460699184 2241578965849 31510542635443 464047929509794 7143991172244290 114749135506381940 1919658575933845129 33393712487076999918 603152722419661386031]

Here we find the position of 2026 in that sequence:

@seq.grep(2026):k

# (17)

Given the title of the sequence and the extracted position of 2026, this means that the number of disconnected 4-regular graphs with 17 vertices is 2026. Let us create a few graphs from that set by using the 5-vertex complete graph (K₅) and circulant graphs.
Here is an example of such a graph:

#% html
reduce(    
    { $^a.union($^b) },
    [
        Graph::Complete.new(5),
        Graph::Complete.new(5).index-graph(6),
        Graph::Circulant.new(7,[1,2]).index-graph(11)
    ]
).dot(engine => 'neato'):svg

And here is another one:

#% html
my $g = reduce(    
    { $^a.union($^b) },
    [
        Graph::Complete.new(5).index-graph(13),
        Graph::Circulant.new(12, [1, 5]).index-graph(1),
    ]
);
$g.dot(engine => 'neato', splines => 'curved'):svg

Here, we check that all vertices have degree 4:

$g.vertex-degree.sum / $g.vertex-count

# 4

Remark: Note that although the plots show disjoint graphs, each graph plot represents a single graph object.


Ways to compute 2026

Here are a few ways to compute 2026:

sub postfix:<!>(UInt:D $n) { [*] 1..$n }

[ 
    2**11 - 22,
    1 + 2 * 3 / 4 / 5 / 6 * 7! * 8 + 9,
    1 + 2 + 3 * 4! + 5 / 6! * 7 * 8! - 9,
    1 + 2 + 345 * 6 - 7 * 8 + 9,
    1 - 2 * 3! * 456 * 7 + 8! + 9,
    12 / 3! * 4**5 + 67 - 89,
    12**3 + 45 / 6! * 7! - 8 - 9,
    123 / 4! * 56 * 7 + 8 + 9,
    9! / 8 / 7 / 6 + 5**4 + 321,  
    9 * 8 - 7 + 654 * 3 - 2 + 1,
    987 + 6! + 5 * 4**3 - 2 + 1
]

# [2026 2026 2026 2026 2026 2026 2026 2026 2026 2026 2026]


Additional Comments

This section has a few additional (leftover) comments.

  • After I researched and published the blog post “Numeric properties of 2025”, [AA1], in the first few days of 2025, I decided to program additional Number theory functionalities for Raku — see the package “Math::NumberTheory”, [AAp1].
  • Most notably, “Math::NumberTheory” is extended to work with Gaussian integers, and the operators GCD and LCD are also extended to work with rationals.
  • One of my current goals is to make Raku extremely good for researching Number theory phenomena.
    • One of the most significant features of Raku is its bignum arithmetic and its good built-in numeric functions applicable in Number theory.
  • Number theory provides many opportunities for visualizations, so I included utilities for some of the popular patterns in “Math::NumberTheory”, [AAp1, AAp2].
    • I use the package “JavaScript::D3”, [AAp3], for almost all Number theory visualizations with Raku.
    • Often, I visualize associated graphs using the DOT language specs provided by the package “Graph”, [AAp4] (as seen above).
  • The number of years in this century that have primitive roots and have 365 as a primitive root is less than the number of years that are happy numbers.
  • I would say I spent too much time finding a good, suitable, Christmas-themed combination of colors for the trails graph.
  • To get the quartic graph counting sequence A033483, I tried to use “Math::Sequences”, but since that package does not provide the sequence, I used an ad hoc retrieval (for which Raku is perfect.)
  • While working on this document, I implemented in “Math::NumberTheory” a set of new functions: integer-digitsis-happy-numberis-harshad-numberis-abundant-numberis-perfect-numberis-deficient-numberabundant-numberdeficient-number, and perfect-number.
    • All of these functions — except integer-digits — had lower implementation priority.
  • Another implementation effort was to finally come up with a Command Line Interface (CLI).
    • I advocate that a CLI should be considered for all Raku packages, and most should have one.

#%bash
number-theory is happy number 2026

# True

#%bash
number-theory primitive root list 2026 | grep 365

# 365


References

Articles, blog posts

[AA1] Anton Antonov, “Numeric properties of 2025”, (2025), RakuForPrediction at WordPress.

[AA2] Anton Antonov, “Primitive roots generation trails”, (2025), MathematicaForPrediction at WordPress.

[AA3] Anton Antonov, “Chatbook New Magic Cells”, (2024), RakuForPrediction at WordPress.

[EW1] Eric W. Weisstein, “Quartic Graph”. From MathWorld–A Wolfram Resource.

Notebooks

[AAn1] Anton Antonov, “Primitive roots generation trails”, (2025), Wolfram Community. STAFFPICKS, April 9, 2025​.

[EPn1] Ed Pegg, “Happy 2025 =1³+2³+3³+4³+5³+6³+7³+8³+9³!”​Wolfram Community, STAFFPICKS, December 30, 2024​.

Packages, paclets

[AAp1] Anton Antonov, Math::NumberTheory, Raku package, (2025), GitHub/antononcube.

[AAp2] Anton Antonov,
NumberTheoryUtilities, Wolfram Language paclet, (2025), Wolfram Language Paclet Repository.

[AAp3] Anton Antonov, JavaScript::D3, Raku package, (2021-2025), GitHub/antononcube.

[AAp4] Anton Antonov, Graph, Raku package, (2024-2025), GitHub/antononcube.

Videos

[AAv1] Anton Antonov, Number theory neat examples in Raku (Set 3), (2025), YouTube/@AAA4prediction.

Day 21 – A Terminal’s Tale

Today’s story is about my naiveness and terminals.

The original task is simple. Show the output of some program in a sub frame of a TUI application. (TUI == Text User Interface, i.e. apps like tmux, vim, k9s)
For those that wonder, there is a yet to be finished TUI debugger application I’m working on, the child program is the thing to debug, the TUI debugger should put the child’s output in a frame.

So without knowing what I was getting into, I tried the obvious and retrieved the program output via Proc::Async. This looks something like this:

my Proc::Async $proc .= new: 'raku';
my Supply $receive = $proc.stdout(:bin);
$receive.tap: -> $v {
    # Write program output somewhere on my screen.
    say $v;
};

But when doing the above, the usual thing that happens, is that one receives no output. This is because of output buffering. Most programming languages, including Raku automatically detect whether the output goes to a display or some other place. Whenever the output does not go to a display, it’s buffered, i.e. not written immediately, but in larger chunks for better performance. Sometimes it’s possible to turn off output buffering on the client side. In Raku this is done with:

$*OUT.out-buffer = False;

But oftentimes we can’t modify the child. So this is not a general solution. A better idea would be to somehow tell the child that the output is a display, while in reality we are intercepting it. Then we’ll receive the output just like a display would. This is indeed possible. But since program input and output are things that happen on the operating system side, this requires support by the OS. The name for that feature is Pseudo Terminal or PTY for short. In recent years even Microsoft Windows gained support for it. In general the idea is to request such a PTY from the OS and then start the child with that PTY attached to the in and out pipes. (Side note: In contrast to the standard process pipes, terminals and in extension pseudo terminals do not distinguish between standard out and standard error.)

But (this is the third paragraph starting with a “But”) Rakudo does not provide support for starting a process with a pseudo terminal attached. My initial idea was to create a Raku module to create a PTY and connect that to the pipes via Proc::Async.

But (that’s the next one) that turned out to be impossible, as on Windows, PTYs are coupled to process creation. Process and PTY can only be created together. So it’s impossible to do as a module. The next escalation step is implementing PTY support in MoarVM. That’s what I actually started doing. I managed to make it work just fine on Unix. Since PTY and process creation are coupled on Windows, it’s not possible to utilize the process creation machinery of the platform abstraction C library MoarVM uses – libuv. We have to do process creation ourself to utilize a PTY.

But (really?!) looking at how libuv creates processes on Windows, I noticed that this involves many hundred lines of code to get process creation right. I absolutely oppose copying over hundreds of lines of code from libuv just for the PTY special case on Windows.

The Solution

So the final (finally!) solution is to add PTY support to libuv itself. The work on this is mostly finished, the PR is up and went through a few rounds of review.

Until that PR is merged we now use a temporary fork libuv in MoarVM. Two PRs, one in MoarVM, one in Rakudo, later, it’s finally possible to create processes with a PTY in front with Rakudo. All of this was released in Rakudo 2025.12 yesterday.

But (what?!?) just putting a process behind a PTY is not enough to allow robust embedding of the process’ output in a frame. To understand the issue we have to learn a bit about computer history.

Background

There is a partly standardized, partly shaped by convention, protocol of how applications can command the terminal to do interesting things. Terminals are hardware devices consisting of a keyboard, a monitor and some state handling logic. These devices were the normal way to use computers a few decades ago.

Such a terminal is wired up to a computer via a serial cable. One wire for input (the keyboard) and one wire for output (the display). These devices fundamentally receive bytes representing characters on the wire and put those on the screen. They keep a history of lines allowing scroll back, have two screen buffers to allow saving and restoring screen content (e.g. to restore the screens content after a vim or tmux or less session), and introduce a pretty large set of special commands to make the terminal do all sorts of interesting things.

These special commands are sequences of characters mixed into the output. To give an example: ESC[1mHelloESC[m world! (With ESC being the actual escape byte – 0x1B) will print “Hello world!” on the screen. Some were introduced (in many iterations) by the original hardware terminals, some were added later. Some examples of features some of these original terminals featured:

  • Putting the cursor some place on the screen.
  • Scrolling up and down to see the history.
  • Erasing parts of the screen.
  • Changing the color of the background cells and characters to be written.
  • A speaker that can beep.

VT100

These terminals always represented the screen as a grid of character cells of a fixed width. The arguably most influential of these terminals was the VT100.

CC-BY Jason Scott

Many of today’s terminal emulators are compatible with what a VT100 could do. But they usually sport many additional, sometimes obscure, features, e.g. colored text (very common) or drawing pictures (kinda obscure).

These special commands are standardized. That’s the ANSI Escape Codes.

So when we don’t pay attention to all this and trying to embed the output of some program that puts any of these escape codes in its output it will quickly mess up our terminal. Just imagine the child frame’s top left corner to be at row 15, column 40 and the child moving the cursor to row 5, column 32 and then writing some text. That text will happily be written outside of its frame.

Emulating

What we’ll have to do is creating a virtual representation of a terminal and applying the output of the child process to this virtual terminal. This includes implementing a parser that can detect escape codes in the stream of output text and providing implementations for many of them. The screen of that virtual terminal can then be copied into the frame we want the output of the child to appear.

Every current day’s Terminal Emulator is – as the name suggests – doing just that, it emulates in software a terminal device including the screen grid, history and many of the escape codes.

So to finally be able to embed the child program’s output in a frame of the debugger I’m working on, I had to write a terminal emulator! Luckily there already is a module – Terminal::ANSIParser in the ecosystem for extracting escape codes from a stream of text (which is a pretty complex task all by itself, requiring a mid sized state machine that you have to get just right). Thanks go to Geoffrey Broadwell (japhb) for this module! So that’s at least one part of the task out of the way.

Anolis

The module that fell out of the effort is named Anolis in reference to the color-changing lizards. It’s a pure Raku terminal emulation library.

Using it is pretty simple:

use Anolis::Interface;
use Anolis;

class Interface does Anolis::Interface {
    method heading-changed(Str $heading) {}
    method log(Str $text) {}
    method grid-changed(@changed-areas) {
        # Do stuff with the changes.
        # Or access $anolis.screen.grid to read from the virtual screen.
        
    }
}

my Proc::Async $proc .= new: :pty(:rows(32), :cols(72)), 'bash';
my Interface $interface .= new;
my $anolis = Anolis.new: :$proc-async, :$interface;

$anolis.send-text('ls\n');

To use Anolis you need to provide an implementation of Anolis::Interface. Via that interface you’ll be notified whenever the screen contents change. In the simplest form one can just copy the entire screen contents over to wherever one wants the content to appear whenever it changes.

The implementation is far from complete, but enough escape codes are implemented to get bash and vim running.

In the Terminal-Widgets-Plugins-Anolis distro I’ve implemented a minimal integration of Anolis and Terminal-Widgets, a pretty epic TUI widget toolkit (also by japhb++). I’ve created a small script that demonstrates Anolis and the Terminal-Widgets integration. That same script is also pretty helpful in implementing further escape codes.

On the left it shows a frame with the client screen, on the right a log of unknown escape codes. So the process is usually as simple as running that script, interacting with the child and whenever a log entry shows up, implement the escape code that is reported as unknown.

I’ll conclude with a list of resources on escape codes:

Have a merry Christmas Time!

Day 20 – We’re Walking On The Air

…continuing from Day 8 – HARC The Herald Angels Sing

We left Rudi hanging by his hooves on Day 8. He had quickly whipped up his first website, but felt that more could be done to add some Christmas Decorations.

A snowman perhaps? His mind wandered to the iconic Snowman tale by Raymond Briggs and the accompanying song (1982) which was sung by Peter Auty, who was a choirboy at St Paul’s Cathedral at the time.

We’re walking in the air
We’re floating in the moonlit sky
The people far below
Are sleeping as we fly

Walking

Here’s where we left off – with a complete single page website:

Now Vixen, the sharp-eyed little doe, noticed that Rudolph had recut the code to use the markdown() routine where the content was mostly text, instead of sprinkling functional HMTL tags. Even cleaner. No surprise, since she knew that…

John Gruber created Markdown in 2004 with the explicit goal that people could write in an easy‑to‑read, easy‑to‑write plain text format and then convert it to structurally valid HTML or XHTML.

Here’s a reminder of how that looks:

Floating

Yes, yes – but clean code won’t make it look pretty. How can we make it look more festive?

Rudi’s nose lit up – maybe a snowman background! In a whirr of hooves, he found a Community Commons licensed image and added two lines of code:

my &shadow = &background.assuming(
    :url<https://freesvg.org/img/1360260438.png>
);

plumbing in the background element like so:

my $rudi = site :register(shadow), page [
...
main [
        shadow;
        article markdown q:to/END/;
        ## About Me

Close, but no cigar.

Need to float those blocks in the Air he pondered and sprinkled some CSS into his Raku:

Note IntelliJ RIP shown here with folding

Okaaay – getting there.

Moonlit

Vixen tapped Rudi on the haunch, “remember that Santa and Mrs Claus prefer their screen in light mode” she whinnied. Rudi had forgotten all about this – he needed dark mode to keep his night vision ready for flying in the dark.

Again, the hooves clattered away.

Here is the final result:

And, so with a tap on the moon:

All the content is done, but the style is a bit so-so…

Sleeping

Rudolph looked on with quiet satisfaction at his work – certainly worth a cigar before his preparation sleep for the big night.

Rudolph, bright and cozy,
beneath the northern star,
draws slow on a mellow cigar
that glows like Yule afar.

~librasteve


Day 19 – Welcome to the Megadungeon

Welcome, weary traveler. It is known that you come bearing earnest questions and we congratulate you on managing to arrive here, on this precipice of doom. Not a brutal doom, nor even a fearful doom, but a doom as in a fate open to you.

See, your arrival here stands foretold. In fact, we built this very precipice in anticipation of you. Since we expected you to come from many different paths, we laid out long and sturdy carpets in untallied directions.

There has been a long-standing contrast observed, that Perl was the Hobbit to Raku’s Lord of the Rings. While somewhat amusing, I have always felt that this analogy failed in two key ways.

First, it fails to give Perl’s legacy its due. Perl’s presence is felt in the deepest firmaments of our operating systems. If it must play the role of a ‘smaller sibling,’ using the Hobbit as reference just feels too under-stated. It’s an amazing book, but especially with today’s often almost anti-rose-tinted perspective on Perl’s contributions on everything from build systems, bug report tools, testing frameworks, centralized distribution of modules, even to our very understanding of DNA… I think we can do better by Perl.

Second, comparing Raku to the Lord of the Rings… while it is an indisputably impressive work of singular imagination, this comparison nevertheless fails to describe the sheer, nigh unbelievable audacity that the project — the dream — that is now known as Raku actually represents.

I’d like to take this opportunity to explore a new spin on all of this. If Perl 1-4 represents the Hobbit, then Perl 5 (now, at-last-and-once-again known as simply Perl) is the Lord of the Rings. Perl has certainly gone to Mount Doom and back. And when it finally arrived back home, it found that its position in the world had been irrevocably changed.

That is also to say that Perl represents an achievement of similar scope and magnitude as to Tolkien’s genre-defining series. Nothing that came after it ever looked or felt quite the same. But none would ever again dare to arrive without (hash)maps in place!

But where does that leave Raku? Raku is Dungeons and Dragons. D&D’s very existence hinges on works like Lord of the Rings (see Appendix N for the larger context of influences). Dungeons and Dragons is also certainly audacious: it’s essentially an audacity amplifier! Whatever scope you can envision, you can make possible. That fits pretty well into our famous position: “make the hard things easy and the impossible things feasible.” (How’s that for audacity? Also, this is my personal paraphrase. Use responsibly!)

All you need is a group of like-minded people who agree to both take it seriously and have a whole lot of fun while doing so.

It is understandable if you feel a bit trepidatious. It’s a known crossover effect from the fact that you may have already made the choice, though it is only now presented before you… beyond the threshold, into the darkness of the deep… some greater force urges you on, compelling you to strive forth and make a place for your name in the Great Hall of CONTRIBUTORS.

A megadungeon is a specific type of role-playing “adventure path” designed to be a permanent fixture of a larger campaign. Player characters can dip in and out of the megadungeon as they please, exploring it to whatever depth feels appropriate. But a key feature of a megadungeon is that it (almost) always has another level, another staircase leading into seemingly-unknown-yet-also-clearly-already-ventured-upon corridors and chasms. (Maybe I just took the first step in creating my own heartbreaker RPG system called Chasms & Corridors?)

The “almost” qualifier is that every megadungeon does have a last level, at least as designed. You can theoretically get there. But the chances of Real Life allowing it are minimal. And so the comparison to D&D also fits the historical record of Raku, especially if you consider core development to be a megadungeon.

It is not uncommon for our core developers to reach a point in their journey where they find they must turn back in order to find their way again. Their paths may or may not ever venture back into Mount Camelia, but the marks they left remain and endure and shape the experience of all who come after. We are all so very grateful to have had the time with you that you chose to share, and wish you nothing but the best in all of your new adventures.

At the risk of belaboring this metaphor, I’ll leave on a note of highlighting the stratification of core development in this context. Work can be done by those lovely creatures blessed with darksight (read: C skills and compiler mind) at the MoarVM level. The fact that they can get into the lowest areas of Mount Camelia and emerge with treasure is a testament to either their fortitude, their foolhardiness, or both.

Meanwhile, there has been a years long project to create an entirely new compiler front-end, essentially building a brand new way of existing and traveling throughout the megadungeon that is Raku core development. And all the while, progress and improvements have been made to the implementation of the core setting.

With regards to the new front end: We are close, maybe very close. But even so… for those of us still delving the nooks and crannies of our respective levels, we know that once we get there, it will be as it ever was: just as we put the last of our current efforts to polish, we will inevitably see some glimmer in the dark to once again drive us on.

You will then, as always, be welcomed to come along, for a spell or for a song, as we carve a dream of it.

This post is dedicated to all of us who have put heart, mind, and/or life into this audacious dream of ours. We love you, we miss you, we are happy for you, wherever you are.

Happy holidays!

Day 18 – Hallo, Wêreld!

The first elf language is the sharing of emotions. Next comes their mother tongue. When elves start speaking their first programming language, then they are already onto their third language!

Santa imagines raku draws inspiration from many principles of Unix philosophy In particular, localization of raku plays into the robustness principle.

When writing to Santa, it doesn’t matter which language you write your wishlist in — for Santa is learning all the languages he can:

say 'Hello, World!' # English, or
sê 'Hallo, Wêreld!' # Afrikaans

It is kinder thought Santa — especially to those writing their first list — to get their feet wet by writing in a language closer to their heart.

TLDR

use L10N::AF;

# Trim whitespace (leading and trailing)
sit .sny vir '/pad/na/lêer'.IO.lyne

# List lines containing specified text
.sit as .bevat: 'soekteks' vir '/pad/na/lêer'.IO.lyne

Install

To speak with the alf elf, the summoning charm is via zef:

$ zef install L10N::AF

To write your wishlist in Afrikaans, don’t forget to include use L10N::AF near the top of your list. Or use the prepaid postage envelopes labelled afrku or lekker:

$ lekker -e 'sê "Hallo, Wêreld!"'
Hallo, Wêreld!

Other languages

The talkative elves have discovered that between them they already speak eleven languages. To find an elf that speaks your language, replace XX in use L10N::XX with the elf’s nickname:

LanguageISO 639 Set 1
AfrikaansAF
DutchNL
EsperantoEO
FrenchFR
GermanDE
HungarianHU
ItalianIT
JapaneseJA
PortuguesePT
ChineseZH
WelshCY

Preservation Of Huffmanization

Santa doesn’t much like chimneys as they are too long. Why huff and puff when he can send his smaller elves down, like , so, vra and met:

use L10N::AF;

#| Pangram test / Toets vir 'n pangram
sub is-pangram ($sin = vra 'Sin: ') {
    sê so .kl.bevat: alle 'a'..'z' met $sin
}

is-pangram q :tot /GEDIG/;
    Bask with grammars,
    Coax via allomorphs,
    Sequences of huffmanized joy.
GEDIG
# True

Decorations

Festive elves sprinkled decorations on some of the presents, like (say) and reël (rule). This did not slow Santa as the first four things he gathered from the explosion at the old ASCII factory were U, T, F and 8. Diacritics are also fun to input when you configure your keyboard to use a compose key. The keystroke sequences often form a visual composition then, for example:

CharacterKeystrokes
êCompose + e + ^`
ëCompose + e + "

Dual Translations

Some elves couldn’t make up their minds what they wanted for Xmas. Santa has allowed them to wrap their own gifts twice which has made them happy again for now:

AfrikaansEnglish
basisnaambasename
aanpas-lêermoduschmod
aanpas-lêerskapchown
gidsdir
gidsnaamdirname
sifgrep
kophead
koppellink
maak-gidsmkdir
drukfprintf
skrap-gidsrmdir
slaapsleep
sorteersort
sterttail
ontkoppelunlink

( Words in common shell usage have been kept as alternate translations — either can be used. )

Arrays

Some elves need help remembering which end of the sleigh they are servicing and whether they are loading or unloading:

Santa decided to label both ends of the sleigh and split the elves into fore and aft teams:

EnglishAfrikaans
pop, pushtrek-einde, druk-einde
shift, unshifttrek-begin, druk-begin

Composable

Raku and Afrikaans share a composable aspect.
Santa’s elves giggle when making associations between concepts:

AfrikaansEnglish
Waar, OnwaarTrue, False
skrap, skrap-nl, skrap-gidsdelete, chomp, rmdir
maak-oop, maak-toe, maak, maak-gidsopen, close, make, mkdir
gee, teruggee, opgee, gegewe, vergewe*return, returns, fail, given, forgiven*
afrond, afrondaf, afrondopround, floor, ceiling
stop, stopseindie, fatal

Sensitivity

Abuse by coercive control is not yet addressed by the Inclusive Naming Initiative. It is recognized in law in England and Wales (2015) and Ireland (2019). Santa is peaceable and so with his awareness of escalatory metaphors and imprecise terminology of the non-elves, he has tried to find translations that are more descriptive, less disproportionate and better suited to a programming language.

In programming context, the word “coerce” is used to change a value between types.
Here Santa chose herskep (recreate) rather than “dwang” (coerce). This ties in with the root term skep (create) and with omskep (map/convert). Santa wondered if his de-escalations mattered:

EnglishAfrikaansEnglish re-translation
CONTROLBESTUURmanage
diestophalt
fatalstopseinstop signal
snitchaanskouto witness

But was rather happy to express himself…

Day 17 – An issue with evaluation

Lizzybel was walking the corridors of North Pole Central when Steve, the Physics Elf, came up to her. “Have you seen my issue on this very nice module of yours?”, he asked.

“Oof, I guess I must have missed that, sorry!”, said Lizzybel, while thinking to herself “I really should look more at my modules every now and then”. “What is the issue about?” “Well, you know my nice App::Crag module? Well, in some cases it just generates some warnings that do not make a lot of sense in that context, so I’d like to be able to get rid of them. But I can’t”, said Steve, “It’s the CodeUnit module”.

Lizzybel tried to hide her blush, but to no avail. Steve saw that, and said: “No worries, it’s but a nuisance” and hurried away smiling, as all of the working at the North Pole don’t just work out by themselves.

CodeUnit

“Ah, yes, CodeUnit, a module that provides a unit for execution of code”, thought Lizzybel, “that cute one that I abstracted from the REPL module I worked on earlier this year. Ah, and there’s Steve‘s issue!”.

Time to look at the code! So why did setting $*ERR to /dev/null not work? Aaah… found it:

CONTROL {
    when CX::Warn {
        .gist.say;
        .resume;
    }
}

“Stupid Lizzybel“, she thought. Control messages (of which warnings are of type CX::Warn) where caught in the CONTROL block, but then output on STDOUT with .say. That should have been .note! Ok, that’s easy enough to fix.

More Options

Thinking about this some more, Lizzybel decided to make it slightly more configurable. “Why not add a :keep-warnings flag and attribute to indicate that you don’t want warnings to be shown immediately, but when you want it?”, she thought to herself. Why not indeed!

A little bit of hacking later, that piece of the code now read:

CONTROL {
    when CX::Warn {
        $!keep-warnings ?? @!warnings.push($_) !! .gist.note;
        .resume;
    }
}

So, now if we don’t want to keep warnings, they are shown on STDERR (because of .note). And if we want to keep warnings, the warning in $_ will be added to the warnings. Now for a way to get the warnings from the CodeUnit object.

method warnings() {
    @!warnings ?? @!warnings.splice.List(:view) !! BEGIN ()
} 

And with that it’s also possible to obtain any warnings, and reset them at the same time!

Now the only thing to do, was to update the documentation, add some more tests and release the new version of the CodeUnit module to the Raku ecosystem with mi6 release! And so it was done.

But, but, but, how?

A few hours later, Steve came running to Lizzybel, alerted by the closing of his issue, and a little short of breath, said: “That’s all very nice, and thank you. But what magic is .splice.List(:view) and BEGIN ()?”.

“Ah, yes. Maybe a little bit too core-ish for a module. But then again, that module is already bound tightly to the Rakudo implementation of the Raku Programming Language“, she answered, “so it felt ok to do that here”.

“So the .splice method without any arguments is an optimized way to both return the whole invocant array and reset that array to not having any elements. The :view named argument to the .List method is an implementation detail that provides an optimized way to change an Array into an immutable List. Provided, of course, that the underlying Array doesn’t change, and no attempts are made to change any element in the List.”, she continued. “Hmm…”, said Steve, not entirely convinced.

Lizzybel continued further: “If there are no warnings, then we could also convert the Array to a List, but that felt a bit wasteful. So why not return an empty List with () in that case?”.

“But why the BEGIN then?”, asked Steve. “Well, an empty List currently in Rakudo is equivalent to List.new, so creates a new object each time. But conceptually speaking, all empty Lists are the same. So why not always return the same one, created at compile time with the BEGIN phaser”, Lizzybel lectured. “That feels like that should be done automatically by the compiler”, said Steve, while wandering away again, busy as ever. “You’re probably right”, thought Lizzybel, “I should make that happen for RakuAST“.

Inspiration

And while Steve was walking away, Lizzybel realized she finally had a subject to write a Raku Advent post about!

Day 16 – Melian and the Helpers of Evergreen

by Sawyer X

Operation Evergreen began quietly that winter.

Santa had announced it as a workshop-wide initiative to improve the speed and efficiency of analytics across every team (logs, metrics, scheduling, routing, and storage). The elves who maintained the Workshop’s growing systems found themselves gathered around long benches, examining slow queries, performance charts, and the output of tools that strained under seasonal load.

One of the key engines behind Evergreen was Melian, a high-performance caching service designed to answer structured queries faster than any relational database or caching server could hope to. Melian’s protocol was compact and binary; queries were made of tables, indexes, and tight payloads. Round-trip times were measured in single microseconds.

Melian already had clients in Perl, Python, Node.js, PHP, and C. But Evergreen wasn’t only about speed, it was also about making engineering work pleasant. In the workshop, comfort matters, even when you’re racing the calendar.

So one team of elves wondered: Could Raku be the next great way to talk to Melian?

They suspected it could. And they were right.

Enter Raku

These elves were familiar with Raku‘s reputation: expressive, concurrent, type-driven, joyful with binary data, and backed by a runtime that had been quietly optimized for real workloads. But this was the first time they’d attempt to pair Raku with a high-throughput caching server.

To their surprise, Raku behaved as if it had been waiting for precisely this job.

Why Raku Was a Natural Fit for Melian

Melian’s goals were simple: serve rows faster than anything else and handle extremely high request rates while avoiding unnecessary overhead and keeping the client interface small and predictable.

That meant the client language needed to handle structured bytes, concurrency, efficient encoding/decoding, and predictable performance without becoming tedious for the elves writing the code.

Raku checked every box.

Bufs, Blobs, and Native Binary Handling

The elves quickly discovered that Raku treats binary data as a first-class citizen.

For a protocol like Melian’s, you don’t want ceremony. You want to construct frames, inspect bytes, slice, encode, decode, and rebuild messages without tripping over your language.

Raku’s built-in buf8, Blob, and typed native arrays made that simple. These weren’t add-ons or foreign modules; they were part of the core. Conversions between Blobs, Bufs, native arrays, and flat integer lists were natural and efficient.

This meant the elves could remain close to the metal, constructing packets in the exact format Melian required, while still enjoying Raku’s expressive and readable style. Nothing felt clumsy or forced.

It was the first moment the elves realized: this language might actually be built for this kind of work.

Types That Make the Interface Behave

The next advantage became clear when designing the interface.

Raku lets you describe the shape of your data directly in method signatures:

method fetch-by-int(
  UInt:D $table-id, UInt:D $index-id, Int:D $key --> Promise
) { ...

If a caller passes the wrong type, Raku catches it immediately. No mysterious errors. No silent failures. The elves loved this because Evergreen required reliability as much as raw speed.

Of course, Raku could have made this even fancier. Its multi-method dispatch system could distinguish between fetch(Int), fetch(Str), fetch(Blob), or even custom types. Many languages struggle with this; Raku does it effortlessly and beautifully.

But for clarity (and in keeping with other Melian client libraries), the elves chose to expose explicit methods such as fetch-by-int-from() and fetch-by-string-from().

The key point remained: Raku let them decide the interface, and it supported both elegant magic and predictable simplicity.

Promises, Auto-Scheduling, and Combinators

Melian encourages parallelism. Most workloads involve fetching many keys at once. That meant concurrency mattered.

The elves knew Raku had Promises, but they didn’t realize how good they were.

Raku’s Promises:

  • Auto-schedule onto the thread pool.
  • Requires no executor setup.
  • Integrate cleanly with typed signatures.
  • Compose using Promise.allof, Promise.anyof, and then chains.
  • Behave predictably even with deep asynchronous pipelines.

The elves had spent years managing thread pools manually in other languages. Here, the threading happened automatically. Not only did this simplify Evergreen’s code, but it made Melian’s parallelism feel lightweight.

Even better, Raku offered concurrency tools (like react, whenever, supplies, and channels) that served as powerful constructs, letting elves orchestrate constant streams of cache traffic if the need arose.

They didn’t use these for the basic client, but knowing the tools existed gave the elves confidence: Raku scales with you, not against you.

Parsing Strength: Grammars and Beyond

Melian’s wire protocol is intentionally small, but some elves wondered if future versions might include richer metadata or structured commands.

Raku’s grammars reassured them.

Raku is one of the strongest parsing languages ever built. Grammars are not a library. They are a language feature that provides tokenization, backtracking, parse trees, and consistent error reporting. All are built directly into the syntax.

Even though Melian doesn’t require grammars today, the elves knew that if Evergreen ever needed a more expressive protocol, Raku would handle it without friction.

Rakudo and MoarVM

Finally, the elves examined how this all actually ran.

The client was implemented in Rakudo, powered by MoarVM. MoarVM shines when dealing with:

  • native buffers
  • concurrent workloads
  • object specialization
  • byte-level operations
  • predictable execution of Promise chains

The elves noted something unusual: Raku’s abstractions didn’t cost them performance. MoarVM carried its weight quietly and efficiently.

Melian was fast, and Raku let the elves keep it fast.

A Walk Through the Raku Client

With these features combined, the Raku Melian client ended up being compact and comfortable:

  • binary frames built from buf8
  • typed method signatures
  • Promise-based fetch operations
  • lazy schema bootstrapping
  • readable byte decoding

The elves could issue dozens (or hundreds) of concurrent requests, await them, and process results transparently. Where other languages required scaffolding, Raku needed only a few lines.

Evergreen’s goal was speed without sacrificing happiness. Here, Raku delivered both.

A Greener Christmas

By the time Operation Evergreen wrapped up, the Workshop’s analytics systems were faster than ever. Melian handled impossible workloads with ease. And the Raku client provided an expressive, type-safe, concurrent interface that let elves build tools quickly and confidently.

The elves didn’t replace any existing languages. They simply added another one: one that complemented Melian beautifully.

This was the true spirit of Evergreen: enhancing what worked, exploring what could, and giving the workshop more ways to succeed.

Melian stayed fast. Raku made using it pleasant. And somewhere between them, Christmas became just a little greener.

Do It Yourself: Try Melian with Raku

Step 1: Create a SQLite Database

Create a file called workshop.db and load this schema:

CREATE TABLE production (
    id INTEGER PRIMARY KEY,
    elf_name TEXT NOT NULL,
    toys_made INTEGER NOT NULL
);

INSERT INTO production (elf_name, toys_made) VALUES
    ('Elanor', 42),
    ('Tarn',   44),
    ('Pip',    39);

You can inspect it with:

sqlite3 workshop.db 'SELECT * FROM production'

Step 2: Point Your Melian Daemon at the Table

Run Melian in the command line with:

MELIAN_DB_DRIVER=sqlite MELIAN_SQLITE_FILENAME=workshop.db MELIAN_TABLE_TABLES='production#0|60|id#0:int;elf_name#1:string' ./melian-server

This gives us a SQLite DB driver, using the production table, defining two queryable columns (id and elf_name), assuming each will return only one row.

Step 3: Query the Data from Raku

Here is a small example that fetches entries for one elf across multiple hours:

use Melian;

my $melian   = Melian.new;
my @names    = <Elanor Pip>;
my @promises = @names.map({
    $melian.fetch-by-string-from( 'production', 'elf_name', $_);
});

await Promise.allof(@promises);
@promises>>.result.say;

From here, you can extend the example to compute averages, compare elves, or schedule periodic lookups using concurrency constructs.

Letters from the North Pole

Operation Evergreen pushed the Workshop to explore new territory: faster caching, better concurrency, and cleaner interfaces. Melian provided the performance. Raku provided the language that made the client both fast and pleasant to use.

With strong binary types, a real type system, Promises and combinators, a parsing engine ready for any protocol – all built on Rakudo + MoarVM’s efficient execution model.