Beginner

Variables and Types in Raku

Learn how Raku uses sigils, gradual typing, type coercion, constants, and constrained subsets to describe data with elegance and precision.

Raku approaches variables differently from most languages you may have seen. Rather than variable type determining the sigil (as in Perl 5), Raku uses sigils to identify the kind of container a name refers to: a scalar, an array, a hash, or a callable. The actual type of the value inside can be constrained separately — or left entirely flexible.

This freedom is a direct consequence of Raku’s gradual typing model. A variable can be fully dynamic (no type annotation), partially typed, or fully constrained with a user-defined subset. Whichever style you choose, the runtime enforces the constraints you opt into. Raku also distinguishes between several numeric types — most notably Int (arbitrary precision integers), Rat (exact rationals), and Num (double-precision floats) — giving you more accurate arithmetic than many mainstream languages provide out of the box.

In this tutorial you’ll learn how to declare variables with my, how the sigil system works across container types, how to constrain variables with type annotations and subsets, how to convert between types via coercion methods, and how Raku represents “no value yet” using type objects.

Sigils and Basic Variables

Raku has four sigils, each indicating a different container:

  • $ — scalar (one value)
  • @ — positional (ordered collection)
  • % — associative (keyed collection)
  • & — callable (a function or block)

Unlike Perl 5, the sigil stays the same whether you access the whole container or a single element: @languages[0], not $languages[0].

Create a file named variables.raku:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Scalars hold a single value of any type
my $name   = "Camelia";
my $year   = 2015;
my $pi     = 3.14;
my $active = True;

say "Name: $name";
say "Year: $year";
say "Pi: $pi";
say "Active: $active";

# Arrays (@) hold an ordered list of values
my @languages = "Perl", "Raku", "Python";
say "Languages: @languages[]";
say "First: @languages[0]";
say "Count: {@languages.elems}";

# Hashes (%) map keys to values
my %versions = Perl => 5, Raku => 6, Python => 3;
say "Raku is version %versions<Raku>";
say "Keys: {%versions.keys.sort}";

# Callables (&) hold a block or subroutine
my &doubler = -> $n { $n * 2 };
say "Double of 7: {doubler(7)}";

A few things worth noticing:

  • my introduces a lexically scoped variable — the most common declarator.
  • Inside double-quoted strings, @array[] interpolates the whole array separated by spaces, and %hash<key> interpolates a specific value.
  • Use { ... } inside a string to interpolate any expression, like a method call or arithmetic.
  • The & sigil lets you store and call functions as values — doubler(7) invokes the block.

Typed Variables, Conversions, and Constraints

Raku’s gradual typing means you can opt into as much type discipline as you like. Add a type name between my and the variable to enforce that constraint at runtime. You can also define entirely new constrained types using subset.

Create a file named types.raku:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# --- Optional type annotations ---
my Int  $count    = 42;
my Str  $greeting = "Hello";
my Rat  $fraction = 1/4;
my Bool $ready    = True;

say "count ({$count.WHAT.^name}): $count";
say "greeting ({$greeting.WHAT.^name}): $greeting";
say "fraction ({$fraction.WHAT.^name}): $fraction";
say "ready ({$ready.WHAT.^name}): $ready";

# --- Type coercion via methods ---
my $text    = "123";
my $as_int  = $text.Int;
my $as_num  = "2.5".Num;
my $as_str  = 42.Str;
my $as_bool = 0.so;

say "\"$text\".Int = $as_int (now {$as_int.WHAT.^name})";
say "\"2.5\".Num = $as_num";
say "42.Str = \"$as_str\" (now {$as_str.WHAT.^name})";
say "0.so = $as_bool";
say "1.so = {1.so}";

# --- Constants: immutable, no sigil required ---
constant PI       = 3.14159;
constant GREETING = "Hello, World!";

say "PI = $PI";
say "GREETING = $GREETING";

# --- Subsets: constrained types with a where-clause ---
subset PositiveInt of Int where * > 0;
subset ShortStr    of Str where .chars <= 5;

my PositiveInt $age  = 30;
my ShortStr    $code = "RAKU";

say "age = $age (constrained to positive integers)";
say "code = $code (constrained to max 5 chars)";

# --- Undefined values: type objects stand in for "no value yet" ---
my $maybe;
say "maybe defined? {$maybe.defined}";
say "maybe type: {$maybe.WHAT.^name}";

my Int $typed_maybe;
say "typed_maybe defined? {$typed_maybe.defined}";
say "typed_maybe type: {$typed_maybe.WHAT.^name}";

Some important ideas on display here:

  • .WHAT returns the type object of a value, and .^name asks the metaclass for its textual name — a common idiom for inspecting types at runtime.
  • The coercion methods (.Int, .Num, .Str, .so, .Bool) are the standard way to convert values. They’re just methods on every type, not special syntax.
  • Rat preserves exact rational values — 1/4 stays exactly 0.25, not a floating-point approximation.
  • constant produces a true compile-time binding. Constants have no sigil and cannot be reassigned.
  • subset T of BaseType where CONDITION defines a new type that the runtime will check on assignment. Assigning a value that fails the where clause throws a type error.
  • When you declare my Int $x; without a value, the variable holds the Int type object — an undefined value still tagged with a type. Use .defined to tell a value from a type object.

Running with Docker

1
2
3
4
5
6
7
8
# Pull the official Rakudo Star image
docker pull rakudo-star:alpine

# Run the first example
docker run --rm -v $(pwd):/app -w /app rakudo-star:alpine raku variables.raku

# Run the second example
docker run --rm -v $(pwd):/app -w /app rakudo-star:alpine raku types.raku

Expected Output

Running variables.raku:

Name: Camelia
Year: 2015
Pi: 3.14
Active: True
Languages: Perl Raku Python
First: Perl
Count: 3
Raku is version 6
Keys: Perl Python Raku
Double of 7: 14

Running types.raku:

count (Int): 42
greeting (Str): Hello
fraction (Rat): 0.25
ready (Bool): True
"123".Int = 123 (now Int)
"2.5".Num = 2.5
42.Str = "42" (now Str)
0.so = False
1.so = True
PI = 3.14159
GREETING = Hello, World!
age = 30 (constrained to positive integers)
code = RAKU (constrained to max 5 chars)
maybe defined? False
maybe type: Any
typed_maybe defined? False
typed_maybe type: Int

Key Concepts

  • Sigils describe containers, not value types$, @, %, & stay the same whether you access the whole container or a single element.
  • Gradual typing — add a type name between my and the variable when you want the runtime to enforce a constraint; omit it for fully dynamic behavior.
  • Coercion is method-based.Int, .Str, .Num, .Bool, .so convert between types without special cast syntax.
  • Int, Rat, and Num are distinctInt is arbitrary-precision, Rat keeps exact fractions, and Num is double-precision floating point. Choose deliberately.
  • constant for compile-time bindings — constants use no sigil and are fixed at declaration time.
  • subset creates validated types — combine a base type with a where clause to define custom constraints the runtime enforces on every assignment.
  • Type objects stand in for “undefined” — an unset typed variable holds the type object itself; use .defined to check whether a real value is present.
  • Expression interpolation with { } — inside double-quoted strings, any Raku expression can be embedded in curly braces, including method calls and arithmetic.

Running Today

All examples can be run using Docker:

docker pull rakudo-star:alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining