A Whirlwind Tour of Raku's Best Features
It’s rare that I find a language that I truly feel innovates upon established conventions and features. Raku came out in late 2015 to little fanfare – it was written off by the Perl community as unnecessary change and by the larger programming community as, well, still Perl. I feel that Raku fills the same niche that Lisp does: there are a hundred short and elegant ways to do the same thing, and it’s up to the programmer to decide which one works best for them. It’s extensible and customizable so therefore it’ll last a long time.
In the spirit of Perl, Raku is extremely terse and can be fairly hard to read. There’s a lot that goes on behind the scenes that you would hardly catch if you weren’t acquainted with the language. To get up to speed, I found the Learn X in Y minutes page for Raku extremely helpful. Before continuing, you should skim through that page.
I’ve only been writing Raku for a couple of days, so please excuse any errors & message me using the links at the bottom or the discord at the top so I could correct them.
The Whatever Star #
(yes, that’s its real name.)
The whatever star inhereits its namesake from the wildcard character, which gets replaced by “whatever” when evaluated by your shell. In Raku, the whatever star creates an implicit block which can be used in place of anything that expects a Callable
. This process is called whatever-currying. Here are the docs: https://docs.perl6.org/type/WhateverCode.
This is useful for a wide variety of reasons. Here’s an example I feel is relatively succinct:
1,1,*+* ...^ * >= 100
I won’t delve too deeply into breaking this down, but this is a list representing every fibonacci number less than 100. ...^
is the sequence operator which takes a list on its left side ending with a generator function and an ending condition on the right side. Once the number generated by the generator function smart matches against the ending condition, the list is complete. You can also pass a whatever star in the right side of ...^
to create an infinite lazy list. To do something like this in another language, you’d have to pass a bogus parameter or call a function dedicated to constructing a list of this type. But, the whatever star allows the programer to explicitly write out cases of this form where arguments are unneeded or special functionality is required.
Implicit Chaining #
There is a special variable in Raku named $_
. It has type (Any)
and essentially takes on whatever value that would make sense in the moment. In for loops it becomes the looping parameter, and in blocks of code it becomes the implicit parameter. It can be set explicitly using given
constructs (but more on that later). A special feature of Raku’s syntax is that the .
operator implicity calls functions on $_
if a left operand isn’t passed into it. Along with implicit return statements, this encourages a rather elegant form of method chaining – akin to Ruby’s tap or pointfree style Haskell.
Here’s a contrived example: given a list of lists, filter the lists down to only their even elements and print the new lists.
> (1..2 X 1..2).map: { .grep(* %% 2).say }
()
(2)
(2)
(2 2)
The significant code here is { .grep(* %% 2).say }
. This is a block that takes an implicit argument (passed in as $_
), filters out eveything that isn’t divisible by 2 (.grep(* %% 2)
), and prints it to standard output. We can use this block as a standalone callable to see this in action:
> { .grep(* %% 2).say }(1..10)
(2 4 6 8 10)
In my opinion, this idea of implicit chaining opens the opportunity to write elegant, process oriented code.
Redefinable Operators #
“The clearest functions are niladic [take no arguments], the second clearest functions are monadic [take one argument], and the third clearest functions are dyadic [take two arguments].”
I don’t remember who this quote is attributed to, but I believe I heard it while hanging around APL folks. It rings true – functions with tons of arguments become less and less clear the more arguments you have. It’s sort of like the unix philosophy: do one thing and do it well. Massive monolithic functions (or objects, or classes…) are a significant code smell.
Raku encourages the programmer to dispose of massive functions like this by providing an extremely flexible way to define operators. The operators page in the Raku docs is extraordinarily long but very worth it to scroll through.
Most languages that provide the ability to define custom operators have a notion of precedence (sometimes called fixity or strength). Haskell, for example, allows you to define operators as infixl
(left associative infix) or infixr
(right associative infix) along with a number 0 through 9 describing how tightly they bind to their arguments. What’s unique about Raku though is the fine grained control that it offers the programmer over the associativity rules of the operator. In addition to left associative and right associative, Raku allows you to specify whether an operator is non-associative, list associative, or chain associative. The specific rules for these are listed in the docs so I won’t bother rehashing it here, but like I said before, it’s very much so worth reading if you’re unfamiliar.
Here’s a real world example of where this has come in handy for me. Raku has no predefined operator to string lines of text together, so I defined one myself:
sub infix:<~~~>($a, $b) { $a ~ "\n" ~ $b };
Then, I used it to write a multiline string without having to fudge with things like heredocs or the like:
say 'trepl version 0. https://github.com/aearnus/'
~~~ "running the language { $*PERL.gist } on { $*PERL.compiler.gist }"
~~~ 'type `;help` for help, or type `;quit` or `;exit` to leave.'
~~~ '';
WHO, WHAT, WHERE, WHICH, WHY, and HOW #
I love how Raku names things. It’s cute and silly enough to be memorable, and descriptive enough to never leave me confused.
WHO
, WHAT
, WHERE
, WHICH
, WHY
, and HOW
are methods that are defined on every object by default. They facilitate much of the reflection and dynamicism that is possible in Raku. They’re all part of the MOP, Raku’s meta object protocol.
WHO
returns the package that the object was defined in.
WHAT
returns the type of the object.
WHERE
returns the address of the object.
WHICH
returns a unique identifer to the object.
WHY
returns documentation about the object.
HOW
is important: it returns the metaclass object, which the developers have slyly named the Higher Order Workings object. It provides an interface to all the runtime dynamicism that Raku allows.
There is special syntax to call a method on an object’s HOW
object, and that syntax is .^
.
Here are a few fun things that you can do with a HOW
object. Using my $x = "Hello, world!"
, we can introspect it to find its name,
> $x.^name
Str
what methods you can call on it,
> $x.^methods
(BUILD Capture Int Num chomp starts-with ends-with substr-eq contains indices index rindex pred succ comb match subst-mutate subst lines parse-base samecase samemark samespace word-by-word trim-leading trim-trailing trim words WORDS_AUTODEREF encode NFC NFD NFKC NFKD unival univals wordcase trans parse-names uniparse indent codes chars uc lc tc fc tclc flip ord WHY WHICH Bool Str Stringy DUMP ACCEPTS chop Numeric gist perl ords split substr substr-rw BUILDALL)
its method resolution order (what classes are traversed to call a method on this object?),
> $x.^mro
((Str) (Cool) (Any) (Mu))
and much more. You can even introspect a HOW
object:
> $x.HOW.^methods
(archetypes new new_type add_fallback compose roles role_typecheck_list is_composed setup_junction_fallback find_method_fallback has_fallbacks set_name set_shortname name shortname WHY set_why ver auth api set_ver set_auth set_api add_stash add_attribute compose_attributes set_rw rw get_attribute_for_usage attributes add_method methods method_table submethod_table declares_method lookup cache cache_get cache_add add_private_method private_method_table find_private_method set_autogen_proto add_multi_method multi_methods_to_incorporate incorporate_multi_candidates add_meta_method meta_method_table compose_meta_methods add_role roles_to_compose exclude_parent add_parent parents hides hidden set_hidden set_default_parent_type has_default_parent_type get_default_parent_type compute_mro c3_merge mro mro_unhidden find_method find_method_qualified can publish_method_cache isa does type_check publish_type_cache add_trustee trusts is_trusted create_BUILDPLAN BUILDPLAN BUILDALLPLAN set_is_mixin is_mixin set_mixin_attribute mixin_attribute flush_cache setup_mixin_cache mixin generate_mixin mixin_base is_array_type array_type set_array_type get_boolification_mode set_boolification_mode publish_boolification_spec compose_repr repr_composed set_default_invoke_handler set_invocation_attr set_invocation_handler has_invocation_attr invocation_attr_class ...)
By encapsulating all these metamethods into one object, Raku makes an obvious delineation between methods that you should use and methods that you can use. Having the power to do whatver you want is a good thing – but should be used with caution. Other languages (looking at you, Ruby) don’t make this distinction, and thus often expose these unsafe functions as first order methods of every single object.
Traits (particularly, is rw
) #
Every language should have traits, a strong type system, subset types, etc, etc. It makes programming easier and it means you can offload much of the required mental energy onto the compiler. Raku’s gradual typing is a perfect example of this – but I have so many things to say about the type system that it would be enough to fill 10 blog posts in their entirety. Instead, I’ll focus on one specific example where the type system comes in handy: the is rw
trait.
Consider this snippet of Ruby code. This isn’t some contrived example, I’ve actually run into this problem in real life before. It sneaks up in unsuspecting ways.
class UhOh
attr_accessor :value
def floor
@value = @value.floor
end
end
def nasty_function(x)
return x.floor
end
my_num = 0.5
my_uhoh = UhOh.new
my_uhoh.value = 0.5
nasty_function(my_num) # After this, my_num still equals 0.5
nasty_function(my_uhoh) # After this, my_uhoh.value now equals 0!
Despite the fact that we called the exact same function on what seemed to be the exact same kind of object, duck typing betrayed us and obfuscated away a mutation that would come back to bite us later.
It comes down to how Ruby handles copy semantics: with a bare type argument like Num, passing it into a function copies the argument into the scope of the function. With a more complicated type such as a user defined class or a list, passing it into a function copies a reference of the argument into the scope of the function. Ruby handles this implicitly, trying to do the right thing without much programmer intervention. Unfortunately, this level of implicit-ness often leads to hard-to-spot bugs. I may love Ruby, but this is a glaring and fatal flaw with the language.
Raku takes a note from the Book of Rust and implements a trait which allows the programmer to annotate whether a type implements copy semantics or move semantics. By default, all types in Raku implement some form of immutable move semantics – passing a class like UhOh
into a function will not work, as neither value
nor UhOh
are rw
, and thus this bug is negated entirely. To achieve the reference passing behavior exhibited above, one must explicitly implement the is rw
trait, saying that every instance of that class is readable and writable.
Nothing will explain this better than a code example, so without further ado:
class Okay {
has $.value is required;
}
class UhOh is rw is Okay { }
sub nasty_function($klass) {
$klass.value = 0;
}
my $okay = Okay.new: value => 10;
my $uhoh = UhOh.new: value => 10;
nasty_function($okay); # This doesn't work!
# Cannot modify an immutable Int (10)
# in sub nasty_function at test.p6 line 8
# in block <unit> at test.p6 line 14
nasty_function($uhoh); # After this, $uhoh.value now equals 0.
Smartmatch #
This is a feature that is so inherently Perl that I cannot imagine it ever being implemented in another language. Smartmatch is the embodiment of “this does what you expect it to do”.
It is an operator, ~~
, that calls .ACCEPTS
on the right hand argument and aliases $_
to its left hand argument. Every object implements .ACCEPTS
in some way, shape, or form; and its sole purpose is to do exactly what it should do.
Sound vague? It is. Smartmatch is used in two important ways in Raku: to power the given... when
construct, and to match type signatures.
Given… When #
This is Raku’s form of a case statement, but calling it a case statement wouldn’t do it justice. Here’s an example of how it can be used:
given $str {
when /hello/ { say 'user typed in "hello"' }
when /world/ { say 'user typed in "world"' }
}
This block runs the first block if $str ~~ /hello/
returns True
, and the second block if $str ~~ /world/
returns True
. In this very specific case, this is akin to a switch/case block but matching on regexes instead. We’re not limited to just regexes though, we can mix and match with whatever type smartmatch accepts:
given $str {
when 10 { say 'user typed in the number 10' }
when /hello/ { say 'user typed in "hello"' }
when /world/ { say 'user typed in "world"' }
}
In this case, we first match against $str ~~ 10
to see if the new first block will run. Smartmatching a string against a number is defined as returning True
only if the string coerces into the number, so that block only runs if $str
is '10'
.
As you could imagine, this is a very powerful and extendable operator. There are plenty more things going on here that I haven’t even touched on: smartmatching against booleans, against callables, etc.
Type Signatures #
In Raku, type signatures are treated the same as any other object. A function’s type signature can be inspected using .signature
, and you can do runtime type signature matching using the smartmatch operator ~~
on a Capture
object.
Check out this example:
sub f(Int $x --> Int) {
$x + 1
}
say &f.signature; # => (Int $x --> Int)
say \(10.WHAT) ~~ &f.signature; # => True
say \('hello'.WHAT) ~~ &f.signature; # => False
Read more about smartmatching here or here.
P6CRE and the Grammar Engine #
I haven’t quite had an opportunity to play around with this yet, but Raku revamped the way that it handles regexes. In the spirit of cute names, I’ll refer to it as P6CRE (pronounced picture?): the Raku Compatible Regex Engine. They’ve revamped how regexes traditionally work, allowing for spaces between identifiers, full unicode class support, extra quantifiers, readability improvments, so on and so forth.
Along with P6CRE, an entire grammar system reminiscent of PEG grammars is provided to you for free. This system is extremely powerful… powerful enough that Raku’s parser is actually implemented using the Raku grammar engine. I can’t say much more about it considering I haven’t gotten to use it, but I’m eager to take on a project where I can put it to good use.
In Conclusion… #
If you haven’t gotten a chance to check out Raku, here’s your opportunity. If you want to read more code examples, check out the examples tab on the website or visit their page at RosettaCode.
Thanks to the Raku discord server at https://discord.gg/gg2a3T6 and #perl6 at freenode for helping me through some confusing moments. Special thanks to the Raku developers and timotimo from #perl6 for being extra patient with me while I was frustrated :)
Have any favorite Raku features that I missed out on? Want to leave a comment? Message me using the contact info below.
UPDATE: as per AlexDaniel’s suggestion on #perl6, changed !(* % 2)
to * %% 2
Comments #
Rory O’Kane writes (formatting slightly modified):
Ruby and avoiding accidental mutation #
I think you characterize Ruby wrong in your Traits section. The reason
my_uhoh.value
changes andmy_num
doesn’t has nothing to do with copy semantics. Ruby passes bothmy_num
andmy_uhoh
by reference – it doesn’t implicitly copy only the number as you claim. The reasonmy_uhoh.value
changes is that theUhOh#floor
method explicitly mutates@value = @value.floor
.I think your confusion is due to an assumption that Float#floor mutates the number it is called on. You think that
my_num
was only left unchanged because a copy was provided tonasty_function
. In fact,#floor
does not mutate the number. A demonstration in irb:my_num = 0.5 my_num # => 0.5 my_num.floor # => 0 my_num # => 0.5
If
floor
mutated the number, I would expect it to be calledfloor!
according to Ruby convention. That is why your example feels to contrived to me, despite your assurance that it is not. If I had written theUhOh
class, I would have named the methodfloor!
, and the bug never would have happened.Apart from naming the method differently, if you wanted to achieve the same effect in Ruby as the Raku
rw
trait that you demonstrate, you could call the#freeze
method on the object after constructing it:class Mutable attr_accessor :value def change @value += 1 end end mut = Mutable.new # => #<Mutable:0x00007fc5ae26cc78> mut.value = 3 # => 3 mut.change # => 4 mut.value # => 4 mut.freeze # => #<Mutable:0x00007fc5ae26cc78 @value=4> mut.change # Traceback (most recent call last): # … # FrozenError (can't modify frozen Mutable) mut.value # => 4
I called
mut.freeze
manually above, but you can even override.new
so that#freeze
is called automatically on every new instance of your object.Anyway, that is a corrected description of how Ruby can help you prevent unintended mutation. It’s good that Raku has a solution for that problem too.
Ruby and Smartmatch #
This is a feature that is so inherently Perl that I cannot imagine it ever being implemented in another language.
In fact, Ruby has a very similar feature too – probably inspired by Perl, as many of Ruby’s features are. Ruby’s
case
…when
construct calls a custom operator===
on each “when” expression to compare it to the “case” value. This sounds the same as your description of Perl 6 calling.ACCEPTS
on the “when” expression to compare it to the “given” value.Ruby’s
#===
method (because operators are just method calls in Ruby) is defined for various types as doing some sort of matching – for example, equality for strings, inclusion for ranges, and matching for regexes. And it can be used for type matching as well. Thanks to theModule#===
implementation,MyClass === some_obj
will return true only ifsome_obj
is an instance ofMyClass
or one of its descendants. Ruby doesn’t have an object representation of function signatures, though, so you can’t check whether an object would be accepted as a parameter to a method as you demonstrated you can in Raku.[snipped all but the quote – my response to the comment about
#===
]For example,
#===
is supposed to be symmetric and associative, which could limit the operator’s flexibility.~~
doesn’t have those same best practice constraints.Despite its use of the equals sign,
#===
in Ruby is actually not supposed to be symmetric or associative. For example,/oo/ === ‘cool’
evaluates totrue
(via theRegex#===
method), while'cool' === /oo/
evaluates tofalse
(via theString#===
method).As far as I can tell, Ruby’s
===
really is equivalent in meaning to Raku’sACCEPTS
/~~
. The only functional difference I can see is that Raku defines customACCEPTS
implementations for a few more classes than Ruby does. For example, Ruby doesn’t overrideArray#===
orEnumerable#===
, treating them the same as#==
, while Raku’sACCEPTS
implementation forList
has custom handling of “Whatever” elements and lazyIterable
s.