Version 2.0.0 · an independent Raku implementation in C++17

Raku++ An interpreter, a compiler and a toolchain for Raku

Grammars in the syntax, multiple dispatch, lazy infinite lists, exact rationals, Unicode that counts characters the way you do — Raku is a large language, and Raku++ is a single self-contained tool that runs, compiles and inspects all of it, with nothing underneath it to install. About two milliseconds to the first line of output; a standalone executable when you want to hand a program to someone; a linter, a profiler, a syntax highlighter and an AST dump in the same place. It builds to WebAssembly as well — and that build is what runs the editor below.

Open the playground Install it Put it on your site Learn the language

my @fib = 1, 1, * + * ... *;        # lazy, and infinite
say @fib[^12];

say 0.1 + 0.2 == 0.3;               # exact rationals, not floats

grammar Version {                   # parsing is part of the language
    token TOP   { <major> '.' <minor> '.' <patch> }
    token major { \d+ }
    token minor { \d+ }
    token patch { \d+ }
}
my $m = Version.parse('2.0.0');
say "major $m<major>, minor $m<minor>";

Press ▶ Run — the interpreter is already loading in the background, so it should answer straight away, and it answers here: nothing is sent to a server, and the same WebAssembly serves every code block on this page. Change any of them and run it again.

~2 mscold start of the native binary
90%of the tests Roast declares, passing
50 / 59ecosystem distributions passing their own suites
0servers, accounts or runtime dependencies

The shop floor

Five ways to run the same language

One implementation, five shapes, and the semantics do not change between them: the browser engine is the command-line interpreter compiled with Emscripten, and every standalone mode either carries that interpreter or transpiles the program it would have walked. Which one you want depends on what you are handing to whom.

The three standalone modes all shell out to a C++ compiler when you build, and none of them needs one afterwards. --bundle and --aot run at interpreted speed, since the interpreter is still what walks the program; --exe is the one that removes it.

The goods

What you get for learning it

Seven of them, anyway — the ones that fit on a page. Each is something Raku has in the language, where most others send you to a library, a code generator or a workaround, and each is runnable right here. The tour and the specification cover the rest.

One name, many implementations

Multiple dispatch picks the body from the arguments' types, shapes and values, so each case says what it means instead of being unpicked inside one function by hand.

multi greet(Int $n)              { "the number $n" }
multi greet(Str $s)              { "the string '$s'" }
multi greet(@list)               { "a list of {@list.elems}" }
multi greet(Int $n where $n < 0) { "a negative number" }

say greet($_) for 42, "hi", [1, 2, 3], -7;

Grammars are part of the language

Parsing is not a library and not a separate file to generate. A grammar is a kind of class, its tokens are named, reusable and inheritable, and what comes back is a match tree you can index into.

grammar INI {
    token TOP     { \n* <section>+ }              # a blank line is only ever more
    token section { '[' <name> ']' \n+ <pair>* }  # newlines: \n* before the first
    token name    { <-[\]]>+ }                    # section, \n+ under a header,
    token pair    { <key> '=' <value> \n* }       # \n* after any pair
    token key     { \w+ }
    token value   { \N+ }
}

# a heredoc: the indentation of the terminator is stripped from every line
my $config = q:to/CONFIG/;
    [server]
    host=example.com

    port=8080

    [limits]
    timeout=30
    retries=3
    CONFIG

for @(INI.parse($config)<section>) -> $s {
    say "[$s<name>] " ~ $s<pair>.map({ "$_<key> = $_<value>" }).join(', ');
}

Sequences that never end

The ... operator continues a sequence from the pattern you started, and lists are lazy — so an infinite one costs nothing until you ask it for a piece.

my @powers = 1, 2, 4 ... *;
say @powers[^10];

say (1..∞).grep(*.is-prime)[^8];

my @triangular = [\+] 1..*;         # running sums of an endless list
say @triangular[^8];

Decimals that add up

0.1 + 0.2 == 0.3 is True. Decimal literals are kept as exact rationals rather than binary floating point, so money and measurements behave the way you were taught they would.

say 0.1 + 0.2 == 0.3;

say (1/3).nude;      # numerator and denominator, exactly
say 1/3 + 1/6;
say (2/3).raku;

One value that is several values

A junction holds many values at once and collapses to a single answer when you compare against it — no loop, no grep, no boolean bookkeeping.

my $n = 42;

say so $n == any(3, 42, 100);
say so $n  > all(1, 2, 3);
say so "camelia" ~~ all(/a/, /e/, /l/);

Operators that operate on operators

[+] folds an operator across a list, [\+] keeps the running results, »*» applies one to every element. They are built out of ordinary operators rather than being special cases, so they work with the ones you define yourself.

my @r = 1, 2, 4, 8;

say [+] @r;          # reduce with +
say @r »*» 2;        # apply * to each
say [\+] @r;         # running totals
say (1..5).map(* ** 2);

Characters, the way a reader counts them

A string is a sequence of graphemes, not bytes and not code points, so an accented letter and a family emoji each count as the one character you can see. The character database is in the language too: names in both directions, properties, numeric values, and Unicode script classes usable straight from a regex.

say "Zoë".chars;              # graphemes, not bytes or code points
say "👨‍👩‍👧".chars;              # one family, one character
say "ß".fc eq "SS".fc;        # real case folding
say "π".uniname;
say so "Ωμέγα" ~~ /^<:Script<Greek>>+$/;

And roles, phasers, custom operators with their own precedence, native types, a bignum tower, promises and channels, NativeCall into C. The tour walks through them in about an hour; the specification is the reference.

The party trick

Interpreters all the way down

The playground's dropdown carries five complete interpreters for other languages, each one written in Raku. Pick the Python one, put a Python program in the standard-input box and press Run: what answers you, in a second or so, is three interpreters stacked on top of each other inside a browser tab, with no server anywhere in the picture.

  1. fizzbuzz.py your Python program, typed into the input box
  2. is parsed and executed by
  3. python.raku a Python 3 interpreter — 1,472 lines of Raku
  4. which is itself parsed and executed by
  5. Raku++ a Raku interpreter, written in C++17
  6. compiled ahead of time to
  7. WebAssembly running in the tab you are reading this in, with no server behind it
ShowcaseWritten in RakuInterprets
Lisp ↗ 432 lines a Scheme subset, closures and an exact numeric tower included
Forth ↗ 223 lines Forth — a stack machine and a word dictionary
Perl ↗ 1,635 lines Perl 5, with its own regex engine
Python ↗ 1,472 lines Python 3, off-side rule and all
JavaScript ↗ 2,167 lines JavaScript and TypeScript, classes and closures included

Every name above is a link: it opens the playground with that interpreter loaded and a program already in the input box, and runs it.

Run a Python program on it →

They are not toys built to look good on a landing page: each one is a mid-size program that exercises a different corner of Raku — grammars and a tree-walking evaluator, a full precedence ladder, sigil variables and context, an INDENT/DEDENT tokenizer — and each was written partly to find bugs in Raku++, which it duly did. The long version, with where every millisecond goes: Interpreters all the way down.

The numbers

What it costs to run

Cold start is about 2 ms — best of a 200-spawn loop, 1.8 ms — because there is no VM to bring up first. After that, the same program can be interpreted or compiled with --exe, and what compiling is worth depends entirely on what the program spends its time doing: nothing at all on one of the nine benchmarks, 9.6× on another. Best of six runs, process start included; lower is better.

Benchmark Interpreted Compiled Speed-up
string building16.0 ms4.8 ms3.3×
hashes37.9 ms17.4 ms2.2×
tight loop204.6 ms28.9 ms7.1×
regex91.8 ms72.0 ms1.3×
naive recursion671.4 ms159.6 ms4.2×

Appending to a string with ~= appends in place in every mode, so building a large string is linear rather than quadratic work. The browser engine is the same interpreter again, and lands within 1.3–6.8× of the native interpreter on these kernels. All nine benchmarks, the machine they were measured on, and the method: BENCHMARKS.md.

More than one core

Threads that actually use them

start and await, promises, supplies, channels and react { whenever … } — the same shapes as the reference implementation. By default worker threads coordinate under a global lock, which keeps shared state honest; RAKUPP_PARALLEL=1 lifts it and lets independent work actually run at once.

Four independent workers, no shared state between them
my @results = await (^4).map: -> $n {
    start { (1 .. 300_000).map({ $_ * $n }).sum }
};
say @results.sum;
Threads RAKUPP_PARALLEL=1 default (locked)
11.00×0.96×
21.95×0.96×
43.72×0.99×
84.22×0.95×

Speed-up over the same work in a plain loop, on a contention-free fan-out. Change the shape and the number changes with it: four threads hammering one shared counter give 1.45×, because the process is in the kernel arbitrating rather than computing — which is exactly why the method page insists you report the thread count beside the ratio. This is also the one section here you cannot try in the editors above: a browser tab runs the interpreter single-threaded.

What you can build with it

Servers, not just scripts

Asynchronous sockets are in the box: IO::Socket::Async for clients and servers, signal() for a shutdown that closes cleanly, and TLS through IO::Socket::Async::SSL when the system has an OpenSSL to talk to. A server is a react block.

A TCP echo server, whole — save it, run it, leave it running
react {
    whenever IO::Socket::Async.listen('127.0.0.1', 15480) -> $conn {
        whenever $conn.Supply(:bin) -> $data {
            await $conn.write("echo: ".encode ~ $data);
            $conn.close;
        }
    }
}
…then, from another terminal, talk to it with nc
$ printf 'hello' | nc 127.0.0.1 15480
echo: hello

Each of these is a real program in the repository, and each compiles to a standalone binary with --exe. The tiles link to the source on GitHub — these are programs to read and run yourself, not demos running somewhere:

All of them, with what each one is meant to prove: the showcase directory. The networking guide — clients, servers, graceful shutdown, HTTPS — is NETWORKING.md.

What else is in the binary

It is a toolchain, not just a runtime

A linter, a syntax highlighter, an AST dump, a profiler and a native compiler — in the one executable, with no plugins to install and nothing to configure. Every transcript below is real output from this version.

--lint — ten rules, before it runs at all

$ rakupp --lint app.raku
app.raku:2: warning: '$n' is declared but never used [unused-variable]
app.raku:3: warning: condition is a constant; the branch never runs [constant-condition]
app.raku:4: note: 'return' as the final statement is redundant; the block's last value is returned automatically [redundant-return]
rakupp --lint: 2 warnings, 1 note in app.raku

Eight warnings and two advisory notes, deliberately built to under-report rather than cry wolf: faced with EVAL or a symbolic reference, a rule switches itself off rather than guess. Warnings exit non-zero, so CI can gate on it; notes do not.

--highlight — the highlighter this page is using

$ rakupp --highlight --ansi -e 'my $x = 42; say "x is $x";'
my $x = 42; say "x is $x";

$ rakupp --highlight --html -e 'my $x = 42; say "x is $x";'
<div class="highlight"><pre><span></span><span class="k">my</span> <span class="nv">$x</span> = <span class="mi">42</span>; <span class="nb">say</span> <span class="s">&quot;x is $x&quot;</span>;</pre></div>

The terminal form paints straight in, as above; the HTML form emits standard Pygments classes, so it drops into a stylesheet you already have. The same highlighter colours this site, the course, and — compiled to WebAssembly along with everything else — the editors on this page, live, as you type into them.

--ast — what it made of your program

$ rakupp --ast -e 'say 1 + 2 * 3'
Program
  Call say
    Binary +
      IntLit 1
      Binary *
        IntLit 2
        IntLit 3

For answering "did it parse that the way I meant?" without a debugger. -c checks the syntax without running anything, and --cpp prints the C++ that --exe would compile — add -O to read the optimized codegen instead.

--exe — one file, 9 MB, 2 ms to start

$ rakupp --exe hello.raku -o hello
Compiled (native) hello.raku -> hello

$ ls -lh hello
-rwxr-xr-x  9.3M  hello

About a second to compile, and the size barely moves with the program: a 46-line Mandelbrot and a 106-line JSON grammar both come out at 9.4 MB, because what dominates is the runtime linked in beside them. The machine that runs the result needs nothing installed — not even Raku++.

Also in there: --profile, which prints a routine-level wall-time profile after the run (or JSON, for a tool to read); --bundle and --aot, which produce standalone binaries that carry the interpreter rather than replacing it; and the perl one-liner family — -n, -p, -a, -i.bak — which cluster the way you expect. CLI.md is the full list; the install page has the short one.

Take one home

Install it

One binary, no runtime to install beside it, no package manager required if you would rather not use one.

macOS, with Homebrew

Apple Silicon gets a prebuilt binary
brew tap ash/rakupp
brew install rakupp

Every other way to get it →

Then run something

A file, a one-liner, or standard input
rakupp program.raku
rakupp -e 'say (1..100).grep(*.is-prime).sum'
rakupp -ne '.say if /error/' server.log

Task-shaped answers in the FAQ →

Self-contained archives for macOS (universal), Linux (x86-64, static) and Windows (x64, static CRT) are on the releases page; there are also Guix and Nix channels, and it builds from source with CMake and any C++17 compiler — no third-party libraries.

Other people's code

The ecosystem comes with it

Raku++ reads the same installation store zef populates. There is no separate registry, no re-packaging and no rakupp-flavoured fork of anything: a distribution installed once is what use picks up, unmodified. zef itself runs under Raku++ end to end, too.

Installed once, in the usual way
zef install JSON::Fast
…and then, under rakupp — with nothing in between
use JSON::Fast;
say to-json({ name => 'Ada' }, :!pretty);   # {"name":"Ada"}

use XML;
say from-xml('<r><i>hi</i></r>').elements[0].contents[0];   # hi

use MIME::Base64;
say MIME::Base64.encode-str('raku');        # cmFrdQ==

50 of the 59 most depended-on

The working set is the ecosystem's top distributions, ranked by how many other distributions depend on them. Fifty of the fifty-nine pass their own install-time test suites — their tests, not ours.

Including the ones that call C

Roughly a quarter of that set uses NativeCall. It works interpreted and compiled, through one marshaller, and finds libffi at run time — so there is still nothing for you to install.

Your own lib/, as usual

-I, RAKULIB and use lib all work the way you expect, and a use that cannot be found is fatal rather than quietly skipped.

That set is also where a great many of this release's fixes came from: running other people's tests finds what a suite written alongside an implementation never will. The per-distribution picture, including what is still failing and why, is in the roadmap; the how-to is on the install page and in MODULES.md.

Take it with you

Put a running Raku editor on your own page

One script tag. Your readers get a real interpreter in their own browser, and you get nothing to run, host or pay for.

Load it once, anywhere in the page
<script src="https://raku.online/raku.js"></script>
Then any element carrying data-raku becomes an editor
<pre data-raku>say "Hello from someone else's website!";</pre>

Which renders exactly this — and it runs:

say "Hello from someone else's website!";

One interpreter per page

Ten editors share a single WebAssembly instance and one worker — one download, not ten.

It cannot break your CSS

Every editor lives in its own Shadow DOM. Your styles cannot reach in and ours cannot leak out, so it survives WordPress.

Nothing to operate

Code runs in your visitors' browsers. There is no server in the picture, so there is nothing to secure, scale or bill.

Already have <pre><code class="language-raku"> blocks from a highlighter? Add data-auto to the script tag and they become runnable with no other change. The builder turns pasted code into a snippet to copy; the demo page shows every pattern side by side; the guide lists the options. The largest example is this site: the tour, the drills and the specification all run their examples through the same script.

The rest of the shop

Learn it, and look things up

The course ↗

The Complete Course of the Raku Programming Language

From your first line of code to grammars, concurrency and web services: five parts of theory, each section ending in exercises you are meant to attempt before reading the answer, and a hundred-question final test over the whole thing. Free, open source, supported by The Perl & Raku Foundation, and translated into nine languages.

5parts
384topics
217quizzes
346exercises
9languages
Start the course →

Its code samples are syntax-highlighted by Raku++, and it sends its readers here to run them. What follows is the shorter material, on this site.

The deck ↗

Thirteen slides on Raku++ and its ecosystem

The quickest visual tour: what it is, how much of Raku it runs, how fast, what it compiles to, and where the edges still are. Keyboard-navigable, with a light and a dark theme.

Open the deck → · Download the PDF (792 KB)

The label on the tin

What this is, and how it is counted

Raku++ is an independent implementation, written from scratch: a hand-written lexer, parser and evaluator in C++17, with no third-party libraries underneath it and no borrowed code inside it. What it targets is the language, and what it is measured against is Roast — the official test suite — plus the examples in the official documentation and the test suites that ecosystem modules ship with themselves.

Those measurements are run on every release and published in full, losses included. Nothing on this page is an estimate, and every figure below has a page behind it saying exactly what it counts.

Where it stands, at 2.0.0Count
Roast tests passing, of the ~218,700 the suite declares197,090 (90%)
Roast files in which every assertion passes, of 1,462594 (41%)
Ecosystem distributions passing their own install-time test suites50 / 59
Examples from the official documentation reproduced exactly952
Built-in subroutines implemented198
Built-in methods implemented667

How each of those is counted, including what deliberately does not count: COUNTING.md. The live picture, release over release, is on the conformance pages and the dashboard. Where a feature behaves differently here than the specification describes, the page for that feature says so in as many words.

Open the playground Start the tour Install it