The term "Slang" is used two different ways within the Raku community:

  • To refer to sublanguages within Raku, such as the quoting sublanguage or the regex sublanguage.

  • To refer to variants of Raku that are created by adding in a slang module that modifies the grammar of the language.

Sublanguages within Raku§

The ~ twigil is for referring to sublanguages (called slangs). The following are useful:

$~MAINthe current main language (e.g., Raku statements)
$~Quotethe current root of quoting language
$~Quasithe current root of quasiquoting language
$~Regexthe current root of regex language
$~Transthe current root of transliteration language
$~P5Regexthe current root of the Perl regex language

You augment these languages in your current lexical scope.

use MONKEY-TYPING;
augment slang Regex {  # derive from $~Regex and then modify $~Regex
    token backslash:std<\Y> { YY };
}

The MONKEY-TYPING pragma is a necessary prerequisite to using the augment keyword. It can be thought of as a reminder that the following code is of a type that's usually discouraged. More documentation on augment is at the augment declarator.

Slangs that Modify the Language§

Existing Slangs§

Many slangs are made available as modules through zef. These are useful for:

  • Using directly, so you don't have to write your own slang

  • As examples, for inspiration in writing your own slangs

To see the existing Slang modules, we can look at:

Avoiding Slangs§

Writing slangs is a beautiful and powerful tool, but sometimes, there's no point killing a mosquito with a flamethrower. Sometimes, it's best to see if you can achieve something slang-like with simpler tools.

Before diving into creating a slang, consider these alternatives:

  • Custom Operators allow you to define new operators (prefix, infix, postfix, circumfix, or postcircumfix) that can be used throughout your code. This is often sufficient when you just need a new syntactic construct for operations.

  • Custom Traits (created using trait_mod) let you add compile-time behavior to declarations. Traits can modify classes, routines, variables, and other constructs without changing the grammar.

  • Custom Declarators (like the model declarator used in Red ORM) allow you to create new keywords that behave similarly to built-in declarators like class or sub. While this is a little advanced, it's still less complex than creating a full slang, as it typically involves working with the grammar at a more focused level.

Creating Custom Declarators§

The most direct way to create a custom declarator that works like built-in declarators (such as class, role, or sub) is to use the EXPORTHOW::DECLARE mechanism. This is the approach used by Red ORM for its model declarator.

Using EXPORTHOW::DECLARE§

The EXPORTHOW::DECLARE mechanism allows you to register a custom HOW (Higher Order Workings) class that the compiler will use when it encounters your declarator keyword. Here's a complete example:

# lib/DeclaratorOne.rakumod
# Custom declarator-one implementation
# Similar to how Red ORM implements the "model" declarator

class MetamodelX::DeclaratorOne is Metamodel::ClassHOW {}

my package EXPORTHOW {
    package DECLARE {
        constant declarator-one = MetamodelX::DeclaratorOne;
    }
}

To use this declarator:

use DeclaratorOne;

declarator-one Foo {
    has $.name;
    method greet { say "Hello from $.name()!" }
}

my $foo = Foo.new(name => "World");
$foo.greet;  # OUTPUT: «Hello from World!␤»

How it works:

  • You create a HOW class that extends an appropriate base HOW (such as Metamodel::ClassHOW for class-like declarators, or Metamodel::ParametricRoleHOW for role-like declarators).

  • You export it via the EXPORTHOW::DECLARE package, where the constant name matches your desired declarator keyword.

  • When the compiler encounters your declarator keyword, it uses your HOW to construct the type, just as it would use Metamodel::ClassHOW for class declarations.

This approach integrates seamlessly with Raku's grammar and type system, making your custom declarator work exactly like built-in declarators.

For a real-world example, see how Red ORM implements the model declarator in its source code.

Custom Attribute Classes§

You can also customize what class is used when attributes are created with has. This is done by exporting a custom attribute class via EXPORTHOW::DECLARE. Unlike class declarators which extend a HOW class, custom attribute classes extend the Attribute class itself.

This allows you to change the behavior of has attributes within classes that use your custom class declarator. You still use has to declare attributes, but they will be instantiated as instances of your custom attribute class instead of the default Attribute class.

Here's an example showing just the custom attribute class part (based on ValueClass):

# Custom attribute class example
# Note: This is just the attribute class part. You would also need to define
# a HOW class for the class declarator (like MetamodelX::ValueClassHOW).

class ValueClass::Attribute is Attribute {
    method compose(|) {
        # Custom behavior during attribute composition
        # For example, validate that attributes can't be rw
        die "Attributes { $.name } can't be rw on a value-class" if $.rw;
        nextsame
    }

    method container_initializer(|c) {
        # Custom initialization logic
        # This can change default values or types
        if $.name.starts-with: '@' {
            return -> { Tuple.new }
        } elsif $.name.starts-with: '%' {
            return -> { ValueMap.new }
        }
        nextsame
    }
}

# Export the attribute class via EXPORTHOW::DECLARE
# (The class declarator would also be exported here)
my package EXPORTHOW {
    package DECLARE {
        # constant value-class = MetamodelX::ValueClassHOW;  # Not shown here
        constant value-class-attr = ValueClass::Attribute;
    }
}

To use this:

use ValueClass;

value-class MyClass {
    has $.name;    # Regular scalar attribute (with ValueClass validation)
    has @.items;   # This becomes a Tuple
    has %.data;    # This becomes a ValueMap
}

my $obj = MyClass.new(name => "Test", items => [1,2,3]);
say $obj.name;   # OUTPUT: «Test␤»

How it works:

  • You create a class that extends Attribute, which is the base class for all attributes in Raku.

  • You can override methods like compose and container_initializer to customize the behavior of attributes created with has.

  • You export it via the EXPORTHOW::DECLARE package, where the constant name (typically ending in -attr) maps to your custom attribute class.

  • When you use your custom class declarator (like value-class), any has attributes declared within that class will be instantiated as instances of your custom attribute class instead of the default Attribute class.

  • You still use has to declare attributes; the custom attribute class simply changes what class those attributes are instantiated as.

For a real-world example, see how ValueClass implements custom attributes in its source code.

Prerequisites for Understanding Slangs§

If all you wish to do is use existing Slangs, you can stop reading now. However, if you wish to understand better how to make or modify Slangs, then the recommended reading before continuing is:

A Slang Example§

To get a good view of a nice, simple Slang, the best candidate would be the source for Slang::Lambda.

Slang::Lambda modifies the Raku grammar to make it possible to use the λ (lambda) symbol as the starter symbol of a pointy block (which is usually ->). The source is so short we can include it here:

my role RakuLambda {
    token pointy-block-starter { '->' | '' | '<->' | '' | 'λ' }
}

my role LegacyLambda {
    token lambda { '->' | '<->' | 'λ' }
}

use Slangify:ver<0.0.4+>:auth<zef:lizmat> RakuLambda, Mu, LegacyLambda;

Then to use it, we do:

use Slang::Lambda;

say (1,2,3).map: λ $x { $x+1 }  # (2 3 4)

The code is self-evident to those familiar with Raku's grammars. This is all well and good, but sometimes there's a large gap between being able to read code, and being able to write it. How did Elizabeth (the author of Slang::Lambda) know to use Slangify? How did she know what token names to declare? In her case, it was through extensive study of Raku and how it works, but in order to avoid this, we're going to step through some of these things now.

Slangify§

To ease the process of creating Slangs, Elizabeth Mattijsen also created the Slangify module (that's how she knew about it!) The Slangify module is a module intended for slang developers.

It abstracts the internals of slang creation and activation so that module developers of slangs have a consistent interface they can work with across current and future versions of the Raku Programming Language.

To use it you do the following:

use Slangify Foo::Grammar, Foo::Actions;

Either the Grammar or the Actions can be replaced with Mu to indicate that they should remain unchanged.

Two additional parameters, for legacy grammars and legacy actions, are also accepted.

While using Slangify is logically the last step in your code, it's worth understanding it somewhat before you begin, so that you can align your code with it. For further details, read the documentation for Slangify.

The Raku Grammar§

This is the point where you need to do the most digging. The Raku grammar is not small, and nor is it simple; its extensive error messages, which are great when programming, hinder the readability.

The sources for the Raku grammar are:

Unfortunately there's no substitute for digging through the grammar and locating the tokens/rules which you wish to override.

The Slang Grammar§

Now you need to write the slang grammar, much like Slang::Lambda. The links above to the grammar documentation should help here.

The Slang Actions§

Many slangs will also need an Actions class backing them. This is also documented in the grammar documentation linked above.

Publishing the module§

You'll want to get your module published too. To do this, follow the instructions under Distributions: An Introduction and related pages.