← All questions

What Raku 6.e adds to 6.d

Raku the language is versioned (6.c, 6.d, 6.e, …) independently of Rakudo the compiler (2026.08). A given compiler release implements one or more language revisions, and a program picks one with a use v6.X pragma on its first line. Rakudo 2026.08 defaults to 6.d and offers 6.e behind a PREVIEW marker; Raku++ does the same.

This page is the missing changelog: everything 6.e changes relative to 6.d, with both outputs shown for each item, plus where Raku++ stands on each.

Verified on Rakudo v2026.07 (MoarVM) and Raku++ v3.14.0+, against rakudo main @050230cb2 and roast @b2cbe8a42. Every snippet below was run on both engines under both revisions; the outputs are pasted, not predicted. Re-verified on Rakudo v2026.08: every pasted 6.e output still reproduces; four items changed against what this page had recorded — EVAL's inner pragma is inert, next(42) under 6.d is a compile error, a failed Grammar.parse answers Nil, and §10's 1 mod 0 claim was wrong — each corrected where it occurs. Where the two engines differ, §12 says so.


1. The short version #

6.e is not a redesign. It is roughly four kinds of change:

  1. New things — about a dozen subs/terms/operators and ~25 methods that simply do not exist in 6.d (snip, snitch, nomark, stem, prefix //, term nano, rotor as a sub, …).
  2. Fixes that could not be made without breaking codesqrt of a negative number returns a Complex instead of NaN, so (5..1) is finally False, string ranges iterate by .succ instead of a per-position cross product, sprintf sign/flag handling follows C.
  3. Object-model changes — a role's submethods are no longer composed into the class; a package no longer silently replaces an enclosing package of the same name; shaped hashes default to Mu rather than Any.
  4. Tightenings — things that were silently ignored are now compile-time errors (unknown regex boundaries, sub foo; without unit, macros).

Nothing is removed except .pm as a module file extension, and the multi-path forms of unlink/rmdir/chmod/chown (deprecated, still working).


2. Turning it on #

use v6.e.PREVIEW;

The rules, all verified:

RuleRakudo 2026.08Raku++
use v6.e.PREVIEW;worksworks
use v6.e; (no PREVIEW)Raku v6.e requires PREVIEW modifieraccepted (divergence)
Not the first statementToo late to switch language version. Must be used as the very first statement.divergence — accepted anywhere in the unit, and the whole unit becomes 6.e, including the lines above the pragma
Default for a file with no pragma, -e, REPL6.d6.d
EVAL can raise the revisionnot since 2026.08 — the pragma is accepted but stays inert: EVAL q[use v6.e.PREVIEW; rotor(2, 1..6)]Undeclared routine: rotor, and sqrt keeps its 6.d answer (2026.07 answered yes)yes (divergence)
Revision is per compilation unita module keeps its own revisionsame

$*RAKU.version reports the revision in force (v6.d / v6.e), which is the easiest runtime check.


3. Where the documentation is — and isn't #

There is no complete, official "6.e changelog". What exists:

Fixups.rakumod (augmented methods), Formatter.rakumod, PseudoStash.rakumod, array_multislice.rakumod, hash_multislice.rakumod, hash_hyperslice.rakumod, Grammar.rakumod;

sprintf-*.t, multislice-*.t, hyperslice.t, …).


4. New syntax #

prefix // — "is defined" #

say //42, " ", //Any;
6.dNull regex not allowed. Please use .comb if you wanted…// is an empty regex. Raku++ under 6.d neither errors nor answers: say //42 reads // as an empty regex match against $_, which yields no values, so it prints an empty line and exits 0. In other term positions (say (//42).raku) Raku++ does refuse
6.eTrue False

term nano #

Nanoseconds, as an Int, next to the existing time and now.

say nano ~~ Int;   # 6.d: compile error   6.e: True

q:o / q:format — a Format literal #

A compile-time-parsed sprintf template that is a callable object.

my $f := q:o/%5s/;      # or q:format/%5s/
say $f.^name;           # Format
say $f("foo");          # "  foo"
say (1,2,3).fmt(q:o/%3d/);   # "  1   2   3"

In Rakudo this needs the RakuAST frontend as well as 6.eq:o is only in src/Raku/Grammar.nqp, so RAKUDO_RAKUAST=1 raku … is required; without it you get Unrecognized adverb: :o even under 6.e. The Format type itself is available under plain 6.e (Format.new("%5s")("hi")" hi"). Raku++ has both without an environment variable.

.fmt(Format) candidates were added to Bag, BagHash, List, Map, Mix, MixHash, Pair, Seq, Set, SetHash.

RakuAST #

The RakuAST:: package is available under 6.e without a pragma; under 6.d it requires use experimental :rakuast.

say RakuAST::IntLiteral.new(42).DEPARSE;
6.dUse of RakuAST is experimental; please 'use experimental :rakuast;'
6.e42

Raku++ gates the namespace by exactly that rule, with that message, and its classes carry Rakudo's own ancestry — RakuAST::IntLiteral.^mro is the same thirteen names, so ~~ RakuAST::Expression and .does answer as they do there. .DEPARSE is a later step of the campaign, so the line above still reports "no such method" here; what a 6.d program sees is the refusal, which is the half this table is about.

Multislices and hyperslices #

@a[0;1;2] exists in 6.d, but 6.e adds the slipped form — a list of indices spliced in with || — and the {**} hyperslice over nested associatives.

my @a = [[1,2],[3,4]],; my @i = 0,1,0;
say @a[||@i];
6.d([[1 2] [3 4]] (Any) [[1 2] [3 4]]) (three ordinary index lookups)
6.e3 (one multi-dimensional lookup)
my %h = A => { B => 1, C => 2 }, D => 3;
say %h{**}:k.sort.join(",");      # 6.d: (empty)   6.e: B,C,D
say %h{'A';'B'};                  # 6.d: (1)       6.e: 1   ← no longer a 1-element list

unit sub foo; #

The semicolon form of a routine declaration. 6.c/6.d allowed it only for MAIN; 6.e allows any sub but requires unit scope.

sub foo;          # 6.d: X::UnitScope::Invalid   6.e: X::UnitScope::MustHaveUnit
unit sub foo;     # 6.d: error                   6.e: fine

5. New terms, subs and operators #

From src/core.e/additions.rakumod — all of these are compile errors under 6.d:

SubExample6.e output
rotorrotor(2, 1..6)((1 2) (3 4) (5 6))
rotor(2, 1, 1..6)((1 2) (3) (4 5) (6)) — cycle first, list last
snipsnip(* < 3, 1,2,3,4)((1 2) (3 4))
snitch(1,2).snitchnotes the value to $*ERR, returns it unchanged
transtrans("a" => "b", "banana")bbnbnb
comb w/ Paircomb(2 => 1, "abcdef")(ab de) — size => step, like rotor
next/last with a value(1,2,3).map({ $_ == 2 ?? next(42) !! $_ })(1 42 3) (6.d: compile error, Calling next(Int) will never work… — this page originally said (1 3), which is Raku++'s 6.d answer, not Rakudo's)
prefix ////$x$x.defined
term nanonanoInt nanoseconds

infix:<~> on two Blobs, and infix:<div> / infix:<mod> with native int/uint candidates, are also (re)defined in core.e, but behave the same as 6.d on this Rakudo build.


6. New methods #

From src/core.e/Fixups.rakumod. All of these are No such method under 6.d.

MethodExample6.e
Any.snip(1,2,3,4,5).snip(* < 3)((1 2) (3 4 5))
Any.snitchmy $x = (1,2).snitchnotes, returns unchanged
Any.skip(list)(1..10).skip(2,3)(1 2 6 7 8 9 10) — alternates produce N, skip N
Cool.nomark / Str.nomark"élan vitál".nomarkelan vital
IO::Path.stem"foo.tar.gz".IO.stemfoo; .stem(1)foo.tar
Complex.sign(3+4i).sign0.6+0.8i (6.d: throws — cannot convert to Real)
Int.roll / Int.pick6.pick(3)short for (^6).pick(3)
Mu.Callable($name)42.Callable("Str") ~~ MethodTrue; Failure if not found
Str.comb(Pair)"abcdef".comb(2 => 1)(ab de)
Str :smartcase"Hello World".contains("world", :smartcase)True (6.d: False)
on contains, starts-with, ends-with, index, indices, rindex, substr-eq
Date.DateTime(:timezone)Date.new(2026,1,1).DateTime(:timezone(3600)).timezone3600 (6.d: named arg ignored → 0)
Instant.DateTime(:timezone)same3600 (6.d: 0)
.fmt(Format)see §4on 10 container types
Int.uninameunassigned codepointreturns a Failure instead of <unassigned> (source-only: no codepoint in the current UCD tables showed the difference)

:smartcase means: match case-insensitively unless the needle contains an uppercase character.


7. Changed behaviour — the ones that bite #

These are the items to read before turning 6.e on for existing code.

sqrt/log of a negative number → Complex #

say (-4).sqrt;              # 6.d: NaN          6.e: 0+2i
say (-4e0).sqrt;            # 6.d: NaN          6.e: 0+2i
say (-1e0).log;             # 6.d: NaN          6.e: 0+3.141592653589793i

Range.Bool is emptiness, not "has endpoints" #

say so (5..1);              # 6.d: True         6.e: False
say so ("b".."a");          # 6.d: True         6.e: False

String ranges iterate by .succ #

say ("az".."bc").join(",");
6.daz,ay,ax,…,ac,bz,by,… — a per-position cross product, 48 elements
6.eaz,ba,bb,bc

sprintf follows C on signs and flags #

say sprintf("%#x", -256);        # 6.d: 0x-100     6.e: -0x100
say sprintf("[%+b][% b]", 5, 5); # 6.d: [+101][ 101]  6.e: [101][101]
say sprintf("[%#.0f]", 1);       # 6.d: [1]        6.e: [1.]
say sprintf("[%G]", NaN);        # 6.d: [NaN]      6.e: [NAN]

(%g keeps NaN; only %G upper-cases. roast covers this in eleven S32-str/sprintf-*.t files, all use v6.e.PREVIEW.)

A role's submethods are no longer composed into the class #

role R { submethod s { 42 } }
class C does R { }
say C.new.s;
6.d42
6.eNo such method 's' for invocant of type 'C'

BUILD/TWEAK/DESTROY from roles still run — 6.e adds them to the class's BUILDPLAN and finalization list explicitly instead of relying on composition — so object construction is unaffected; only calling a role's submethod as a method on the class changes.

A package no longer silently replaces an enclosing package of the same name #

module A::B { class A::B { } }
say A::B::A::B.^name;
6.dPotential difficulties: Declaring class 'A::B' inside an enclosing module of the same name…, then the inner class replaces the outer stash
6.eA::B::A::B — it nests, no warning

Shaped hashes default to Mu #

my %h{Str}; say %h<nope>.WHAT.^name;   # 6.d: Any   6.e: Mu

Placeholder @_ is per-block #

sub f(*@_) { my &c = { @_.elems }; say c(1,2,3) }; f(7,7,7,7);
6.dToo many positionals passed; expected 0 or 1 arguments but got 3 — the inner block's @_ is the enclosing routine's
6.e3 — each block gets its own implicit *@_

Pseudo-packages return Failure, and LEXICAL:: is stricter #

my $r = MY::<$nosuchvar>; say $r.^name;   # 6.d: Any   6.e: Failure
my $*dyn = 42; sub f { say LEXICAL::<$*dyn> }; f;
6.d42
6.eCannot access '$*dyn' through LEXICAL, because it is not declared as lexical

Per the roast announcement, 6.e also specifies that LEXICAL:: sees dynamics from the caller chain, SETTING:: sees symbols from all available COREs, and binding works on every pseudo-package.

Grammar.parse fails instead of returning Nil #

6.e gives grammars a new base class (src/core.e/Grammar.rakumod), so a failed parse carries an X::Syntax::Confused with pre/post context:

grammar G { token TOP { \d+ } }; say G.parse("abc").^name;
6.dNil (this page originally said Any — that is what you see after my $r = G.parse("abc"), where the container defaults the Nil)
6.eFailure

6.e also gives grammars a private default-parent HOW rather than binding the name Grammar itself, which is what roast S05-grammar/namespace-6e.t covers (grammar Grammar { }; this Rakudo accepts it under 6.d too).

splice can insert an itemized array as one element #

my @a = 1,2,3; @a.splice(1,1,$[8,9]); say @a.raku;
6.d[1, 8, 9, 3]
6.e[1, [8, 9], 3]

.pm is no longer a module file extension #

CompUnit::Repository::FileSystem looks for .rakumod .pm6 .pm in 6.c/6.d, and only .rakumod .pm6 in 6.e.

use OldMod;   # lib/OldMod.pm
6.dloads, with Saw 1 occurrence of deprecated code (Rakudo). Raku++ has no deprecation machinery and loads silently
6.eCould not find OldMod in: …

subset records the language version #

subset Even of Int where * %% 2; say Even.^ver;   # 6.d: 6.d   6.e: 6.e

8. New errors #

Things that were silently accepted and now fail at compile time:

Code6.d6.e
/<|f> abc/matches (unknown boundary is a no-op)Unrecognized regex boundary '<|f>'. The known boundaries are '<|w>' (word) and '<|c>' (codepoint).
macro m() { … }works under use experimental :macrosExperimental macros are no longer supported in Raku 6.e. Raku++ has no macros in any revision, so neither cell applies: the declaration parses as a call to an undefined routine and the program dies at run time
sub foo;X::UnitScope::Invalid (only MAIN allowed)X::UnitScope::MustHaveUnit — needs unit sub foo;. Raku++ raises X::UnitScope::Invalid in both revisions; it has no MustHaveUnit

9. Deprecations #

is revision-gated("6.e") candidates in src/core.c/io_operators.rakumod keep the old forms working but emit a deprecation notice at exit, and add single-path candidates:

say unlink("/tmp/no-such-file-abc123").raku;
6.d["/tmp/no-such-file-abc123"] — list of paths that worked
6.eBool::True — one path, one boolean

The same applies to rmdir, chown, and chmod (which additionally gains chmod($path, :$mode) and deprecates chmod($mode, @paths)). Multi-path use is deprecated in favour of @paths.grep(*.IO.unlink).


10. Declared in core.e, but not observably different (yet) #

Worth knowing so you do not go looking for a change that is not there. On Rakudo 2026.08 these behave identically under 6.d and 6.e, either because the fix was made unconditionally or because the core.e version is a re-implementation:


11. How Rakudo implements this #

Useful when you want to check something yourself rather than trust a list:

To find every gate in a rakudo checkout:

grep -rn "language_revision" src/ | grep -w 3

12. Raku++ status #

Raku++ implements 6.d, and 6.e when a program asks for it with the pragma — rakupp --version says exactly that. The revision is a property of the code, not of the process: a module compiled under use v6.e.PREVIEW keeps 6.e semantics when a 6.d program calls it, and a 6.d mainline does not acquire 6.e semantics by loading a 6.e module. Rakudo differs on that second half — use-ing a 6.e module there loads CORE.e into the process, so the importing 6.d mainline starts returning Complex from sqrt as well, including on lines above the use. That divergence is deliberate.

The scoreboard is the support matrix at raku.online/spec/6e, which runs every change on this page four ways — Rakudo 6.d, Rakudo 6.e, Raku++ 6.d, Raku++ 6.e — and scores each from those outputs rather than from a claim. As of this writing (Rakudo 2026.08) it reads 52 of 53 full, 0 divergent, 0 partial, 1 not implemented, with 47 of them gated on the pragma.

Four things are deliberately not done, and are worth knowing rather than discovering:

Three general divergences turned up while this page was being verified, none of them about 6.e, all filed on their own: block arity is not checked at all, Instant.from-posix(0) is ten seconds off, and .subparse on a failed match answers Nil where Rakudo answers with the grammar object.

13. Checking any of this yourself #

Every line above came out of the same runs — each snippet on both engines under both revisions — which is two commands per engine:

raku -e 'CODE'
raku -e 'use v6.e.PREVIEW; CODE'

For the q:o/Format/multislice syntax on Rakudo, add the RakuAST frontend:

RAKUDO_RAKUAST=1 raku -e 'use v6.e.PREVIEW; my $f := q:o/%5s/; say $f("foo")'