Slangs

Slangs in Raku

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:

$~MAIN the current main language (e.g., Raku statements)
$~Quote the current root of quoting language
$~Quasi the current root of quasiquoting language
$~Regex the current root of regex language
$~Trans the current root of transliteration language
$~P5Regex the 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.

See Also

Classes and objects

A tutorial about creating and using classes in Raku

CompUnits and where to find them

How and when Raku modules are compiled, where they are stored, and how to access them in compiled form.

Concurrency

Concurrency and asynchronous programming

Command line interface

Creating your own CLI in Raku

Grammar tutorial

An introduction to grammars

Input/Output

File-related operations

Inter-process communication

Programs running other programs and communicating with them

Iterating

Functionalities available for visiting all items in a complex data structure

Doing math with Raku

Different mathematical paradigms and how they are implemented in this language

Module packages

Creating module packages for code reuse

Core modules

Core modules that may be useful to module authors

Module development utilities

What can help you write/test/improve your module(s)

Modules

How to use, create, and distribute Raku modules

Creating operators

A short tutorial on how to declare operators and create new ones.

Regexes: best practices and gotchas

Some tips on regexes and grammars

REPL

Interactive Raku, read-eval-print loop

Entering unicode characters

Input methods for unicode characters in terminals, the shell, and editors

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.