Contexts and contextualizers

What are contexts and how to switch into them

Contexts interpret the value of a container. In Raku, we use the active context to coerce the value of a container into a type or class, or to decide what to do with it. Usually, a context receiving an object will, if necessary, coerce the object by implicitly calling a specific method on it.

Sink

The sink context is equivalent to what other languages call void context. It is the context which does nothing with the result or return of any code: a term, an operation or a block. In general, when this context consumes a value or variable a warning or error is issued because the value is being ignored. Mnemonics for sink relate to being rid of something: water down a sink's drain; a ship sinking; a heatsink removing warmth.

my $sub = -> $a { $a² };
$sub; # OUTPUT: «WARNINGS:␤Useless use of $sub in sink context (line 1)␤»

You can force that sink context on Iterators, by using the sink-all method. Procs can also be sunk via the sink method, forcing them to raise an exception and not return anything.

Most blocks will warn if evaluated in sink context; however, gather/take blocks are explicitly evaluated in sink context, with values returned explicitly using take:

my @results = gather for 1..1 { ^10 .map: *.take };
say @results; # OUTPUT: «[0 1 2 3 4 5 6 7 8 9]␤»

In this example, for is run in sink context, and within it, map is too. Results are taken explicitly from the loop via gather/take.

In sink context, an object will call its sink method if present:

sub foo {
    return [<a b c>] does role {
        method sink { say "sink called" }
    }
}
foo; # OUTPUT: «sink called␤»

Number

This context, and probably all other contexts except sink above, are conversion or interpretation contexts in the sense that they take an untyped or typed variable and duck-type it to whatever is needed to perform the operation. In some cases that will imply a conversion (from Str to Numeric, for instance); in other cases simply an interpretation (IntStr will be interpreted as Int or as Str).

Number context is called whenever we need to apply a numerical operation on a variable.

my $stringone = "1                 ";
my $stringthree = "3                        ";
say $stringone + $stringthree; # OUTPUT: «4␤»

In the code above, strings will be interpreted in numeric context as long as there are only a few digits and no other characters. It can have any number of leading or trailing whitespace, however.

Numeric context can be forced by using arithmetic operators such as + or -. In that context, the Numeric method will be called if available and the value returned used as the numeric value of the object.

my $t = True;
my $f = False;
say $t + $f;      # OUTPUT: «1␤»
say $t.Numeric;   # OUTPUT: «1␤»
say $f.Numeric;   # OUTPUT: «0␤»
my $list= <a b c>;
say True + $list; # OUTPUT: «4␤»
say +"  \n ";     # OUTPUT: «0␤»

Whitespace in any quantity will be converted to 0, as is shown in the last statement. In the case of listy things, the numeric value will be in general equivalent to .elems; in some cases, like Thread, it will return a unique thread identifier.

String

In a string context, values can be manipulated as strings. This context is used, for instance, for coercing non-string values so that they can be printed to standard output.

put $very-complicated-and-hairy-object; # OUTPUT: something meaningful

Or when smartmatching to a regular expression:

put 333444777 ~~ /(3+)/; # OUTPUT: «333␤»

In general, the Str routine will be called on a variable to contextualize it; since this method is inherited from Mu, it is always present, but it is not always guaranteed to work. In some core classes it will issue a warning.

~ is the (unary) string contextualizer. As an operator, it concatenates strings, but as a prefix operator it becomes the string context operator.

my @array = [ [1,2,3], [4,5,6]];
say ~@array; # OUTPUT: «1 2 3 4 5 6␤»

This will happen also in a reduction|/language/operators#Reduction_operators context, when [~] is applied to a list

say [~] [ 3, 5+6i, Set(<a b c>), [1,2,3] ]; # OUTPUT: «35+6ic a b1 2 3␤»

In that sense, empty lists or other containers will stringify to an empty string:

say [~] [] ; # OUTPUT: «␤»

Since ~ acts also as buffer concatenation operator, it will have to check that every element is not empty, since a single empty buffer in string context will behave as a string, thus yielding an error.

say [~] Buf.new(0x3,0x33), Buf.new(0x2,0x22);
    # OUTPUT: «Buf:0x<03 33 02 22>␤»

However,

my $non-empty = Buf.new(0x3, 0x33);
my $empty = [];
my $non-empty-also = Buf.new(0x2,0x22);
say [~] $non-empty, $empty, $non-empty-also;
# OUTPUT: «Cannot use a Buf as a string, but you called the Stringy method on it

Since ~ is putting in string context the second element of this list, ~ is going to be using the second form that applies to strings, thus yielding the shown error. Simply making sure that everything you concatenate is a buffer will avoid this problem.

my $non-empty = Buf.new(0x3, 0x33);
my $empty = Buf.new();
my $non-empty-also = Buf.new(0x2,0x22);
say [~] $non-empty, $empty, $non-empty-also; # OUTPUT: «Buf:0x<03 33 02 22>␤»

In general, a context will coerce a variable to a particular type by calling the contextualizer; in the case of mixins, if the context class is mixed in, it will behave in that way.

my $described-number = 1i but 'Unity in complex plane';
    put $described-number; # OUTPUT: «Unity in complex plane␤»

but creates a mixin, which endows the complex number with a Str method. put contextualizes it into a string, that is, it calls Str, the string contextualizer, with the result shown above.

Boolean

This context will force a variable to be interpreted as True or False.

say "Hey" if 7;  # OUTPUT: «Hey␤»
    say "Ahoy" if "";

This context appears in expressions such as if or while, and is equivalent to calling so on these values.

say "Hey" if 7.so;          # OUTPUT: «Hey␤»
say "Ahoy" if not set().so; # OUTPUT: «Ahoy␤»

In general, non-zero, non-empty will be converted to True; zero or empty will be equivalent to False. But .so can be defined to return any Boolean value we want, so this is just a rule of thumb.

The ? Boolean context operator and the ! negated Boolean context operator will force the Boolean context on an object.

say ? 0i;    # OUTPUT: «False␤»
say ! :true; # OUTPUT: «False␤»

List

There are actually several different list contexts, which are better explained in that page. In general, the list contextualizer is the comma ,

say (3,).^name; # OUTPUT: «List␤»

and the method called in that case is also .list

Any.list.^name;   # OUTPUT: «List␤»
say 3.list.^name; # OUTPUT: «List␤»
say (^3).list;    # OUTPUT: «(0 1 2)␤»

Item context

Item or scalar context will deal with complex pieces of data as if they were a single item. It is forced when you try to assign to a scalar variable

my $scalar-context = <1 2 3>;
say "→ $_" for $scalar-context; # OUTPUT: «→ 1 2 3␤»

It can be induced using the $ operator, that acts as the contextualizer operator by calling item as a method or routine

.say for $(1,2,3);      # OUTPUT: «(1 2 3)␤»
.say for (1,2,3).item;  # OUTPUT: «(1 2 3)␤»
.say for item( 1..3 );  # OUTPUT: «1..3␤»

Itemization affects only their behavior in list context; however, they will still keep their Positional role or other roles they might have:

$(1,2,3).elems.say;          # OUTPUT: «3␤»
say (1,2,3).item[2];         # OUTPUT: «3␤»
say $( key => 'value')<key>; # OUTPUT: «value␤»

Itemization containerizes values in a data structure keeping them, for instance, from being flattened into the surrounding list or data structure:

.say for (1, $(2,3), 4).flat; # OUTPUT: «1␤(2 3)␤4␤»
say (1, $<a b>, 2).elems; # OUTPUT: «3␤»

The itemizer operator will call the .item method on the object; it can also be called as a subroutine. Since that is a method inherited from Mu, objects of any class can be itemized.

See Also

Containers

A low-level explanation of Raku containers

Control flow

Statements used to control the flow of execution

Enumeration

An example using the enum type

Exceptions

Using exceptions in Raku

Functions

Functions and functional programming in Raku

Grammars

Parsing and interpreting text

Hashes and maps

Working with associative arrays/dictionaries/hashes

Input/Output the definitive guide

Correctly use Raku IO

Lists, sequences, and arrays

Positional data constructs

Metaobject protocol (MOP)

Introspection and the Raku object system

Native calling interface

Call into dynamic libraries that follow the C calling convention

Raku native types

Using the types the compiler and hardware make available to you

Newline handling in Raku

How the different newline characters are handled, and how to change the behavior

Numerics

Numeric types available in Raku

Object orientation

Object orientation in Raku

Operators

Common Raku infixes, prefixes, postfixes, and more!

Packages

Organizing and referencing namespaced program elements

Performance

Measuring and improving runtime or compile-time performance

Phasers

Program execution phases and corresponding phaser blocks

Pragmas

Special modules that define certain aspects of the behavior of the code

Quoting constructs

Writing strings and word lists, in Raku

Regexes

Pattern matching against strings

Sets, bags, and mixes

Unordered collections of unique and weighted objects in Raku

Signature literals

A guide to signatures in Raku

Statement prefixes

Prefixes that alter the behavior of a statement or a set of them

Data structures

How Raku deals with data structures and what we can expect from them

Subscripts

Accessing data structure elements by index or key

Syntax

General rules of Raku syntax

System interaction

Working with the underlying operating system and running applications

Date and time functions

Processing date and time in Raku

Traits

Compile-time specification of behavior made easy

Unicode versus ASCII symbols

Unicode symbols and their ASCII equivalents

Unicode

Unicode support in Raku

Variables

Variables in Raku

Independent routines

Routines not defined within any class or role.

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