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.
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.
rakupp app.raku
The tree-walking interpreter, and the default: no build step, about
2 ms to first line. It takes files, -e one-liners and
stdin, and clusters perl's flags — -nlae,
-i.bak.
Install it →
The playground
That same interpreter as WebAssembly, in a Web Worker on the reader's machine. Editor, live output, standard input, shareable links and every example from the repository built in.
Open it →
--exe
Transpiled to C++ and compiled — about a second, roughly 9 MB out. No interpreter inside, and the only mode that changes how fast the program itself runs.
See the toolchain →
--bundle
Embeds the program's source and links it against the runtime library: one executable, nothing needed on the target, about 10 ms cold. It re-parses and walks that source at run time, so it covers the whole language.
How the modes differ →
--aot
Parses at build time — so parse errors surface then, not on your user's machine — and emits C++ that rebuilds the exact tree at startup for the interpreter to walk. Whole language too, grammars included.
How the modes differ →
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.
- fizzbuzz.py your Python program, typed into the input box
- is parsed and executed by
- python.raku a Python 3 interpreter — 1,472 lines of Raku
- which is itself parsed and executed by
- Raku++ a Raku interpreter, written in C++17
- compiled ahead of time to
- WebAssembly running in the tab you are reading this in, with no server behind it
| Showcase | Written in Raku | Interprets |
|---|---|---|
| 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.
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 building | 16.0 ms | 4.8 ms | 3.3× |
| hashes | 37.9 ms | 17.4 ms | 2.2× |
| tight loop | 204.6 ms | 28.9 ms | 7.1× |
| regex | 91.8 ms | 72.0 ms | 1.3× |
| naive recursion | 671.4 ms | 159.6 ms | 4.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.
my @results = await (^4).map: -> $n {
start { (1 .. 300_000).map({ $_ * $n }).sum }
};
say @results.sum;
| Threads | RAKUPP_PARALLEL=1 |
default (locked) |
|---|---|---|
| 1 | 1.00× | 0.96× |
| 2 | 1.95× | 0.96× |
| 4 | 3.72× | 0.99× |
| 8 | 4.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.
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;
}
}
}
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:
A pastebin
An HTTP server written straight onto raw sockets — no framework underneath, no framework needed.
Read the source ↗
A static file server
rakus: point it at a folder, open a browser. It is what this site is developed against locally.
Read the source ↗
A chat server
Many clients at once, one thread each, driven from as many terminals as you care to open.
Read the source ↗
A key-value store
Redis-shaped, with its own text protocol on TCP — the "can it hold a connection open and talk" test.
Read the source ↗
A Markdown converter
A grammar with an actions class that emits HTML as it parses: Markdown in, a styled page out.
Read the source ↗
A distribution inspector
Seventeen ecosystem distributions doing the work: dependency graphs, validation, reports.
Read the source ↗
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">"x is $x"</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
brew tap ash/rakupp
brew install rakupp
Then run something
rakupp program.raku
rakupp -e 'say (1..100).grep(*.is-prime).sum'
rakupp -ne '.say if /error/' server.log
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.
zef install JSON::Fast
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.
<script src="https://raku.online/raku.js"></script>
<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.
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.
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.
A Tour of Raku
Eighteen short lessons, every example runnable on the page. About an hour, start to finish.
Raku Drills
Practise what you have just read, one snippet at a time, with the answer a click away.
The specification
How Raku++ behaves, feature by feature, with examples verified against Raku++, Rakudo and the browser engine.
Raku Rules
The exhaustive rulebook: every construct, numbered rule by numbered rule, including the traps.
The FAQ
Task-shaped answers — running a command, why a list has one element, compiling to a binary.
The source
C++17, no third-party dependencies, and the documentation that goes with it: reference, cookbook, internals.
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.0 | Count |
|---|---|
| Roast tests passing, of the ~218,700 the suite declares | 197,090 (90%) |
| Roast files in which every assertion passes, of 1,462 | 594 (41%) |
| Ecosystem distributions passing their own install-time test suites | 50 / 59 |
| Examples from the official documentation reproduced exactly | 952 |
| Built-in subroutines implemented | 198 |
| Built-in methods implemented | 667 |
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.