The Sigil Lineage: Perl, Raku, and guji
How the $, @, % sigils travelled from Perl's variant punctuation to Raku's invariant names to guji's shape-typed bindings.
The Sigil Lineage: Perl, Raku, and guji
A sigil is the little glyph in front of a variable name — $, @, % — that
tells you, at a glance, what kind of thing you are looking at. Three languages in
one family treat those glyphs very differently, and the differences turn out to be
a tidy parable about how language design learns from itself. This is the story of
the sigil from Perl to Raku to guji.
Perl: sigils that shimmer
Larry Wall released Perl 1.0 to the comp.sources.misc Usenet newsgroup on
December 18, 1987. He borrowed the sigil idea from the Unix shell — $var was
already how sh and awk named values — but Perl extended it into a small type
system you wear on your sleeve: $ for scalars, @ for arrays, % for hashes
(plus & for subs and * for typeglobs).
The catch, and the thing that made Perl famous and infamous in equal measure, is that in Perl 5 the sigil describes the access, not the container. The sigil shimmers depending on what you pull out:
my @array = (10, 20, 30);
my %hash = (ada => 30);
print "$array[0]\n"; # 10 — a SCALAR element, so the sigil is $
print "$hash{ada}\n"; # 30 — a SCALAR element, so the sigil is $
print "@array\n"; # 10 20 30 — the whole ARRAY, so the sigil is @
Read that twice: you index into @array, but you write $array[0], because the
single thing you get back is a scalar. The reasoning is internally consistent —
"any one value is a scalar, so it wears $" — but it means you cannot tell from
the sigil alone which variable a fragment refers to; you have to read the brackets
too. The sigil tells you the shape of the result, and the brackets tell you the
shape of the source, and you reconcile them in your head.
Perl leaned into this expressive density on purpose. As Wall wrote in the glossary of the first Programming Perl (1991):
"The three chief virtues of a programmer are: Laziness, Impatience and Hubris."
Variant sigils are a very lazy-in-the-good-sense idea: one symbol, $, marks
"a single value" no matter where it came from. But the cognitive bill comes due
when you read other people's code.
Raku: the sigil becomes part of the name
When the language that became Raku was designed (begun in 2000 as "Perl 6," and formally renamed to Raku in October 2019, with Larry Wall's blessing), one of the deliberate corrections was to make the sigil invariant. In Raku the sigil is part of the variable's name and never changes, however you access it:
my @array = 10, 20, 30;
my %hash = ada => 30;
say @array[0]; # 10 — the container is @array, so the sigil stays @
say %hash<ada>; # 30 — the container is %hash, so the sigil stays %
say @array; # [10 20 30]
The container keeps its sigil whether you take the whole thing or one element.
@array[0] instead of $array[0]; %hash<ada> instead of $hash{ada}. You can
now read a sigil-name as a single token and know exactly which binding it points
to. Raku kept the vocabulary of Perl's sigils but fixed their grammar: the
glyph stopped being a description of the result and became a stable label on the
declaration. Everything else about the family resemblance — the punctuation, the
data-type trinity — stayed put.
guji: sigils as a shape type
guji is a statically-typed, functional-first, compiled language whose signature strength is first-class text processing — regular expressions and PEG grammars are language primitives, not libraries. It inherits the three sigils directly, and it inherits Raku's invariance rule and sharpens it into a type-system idea.
In guji the sigil declares the broad shape of a binding and is, in the spec's word, "invariant: it never changes regardless of how the value is accessed." There are exactly three, and only three:
| Sigil | Shape | Example |
|---|---|---|
$ |
scalar | $count = 3 |
@ |
list | @items = [1, 2, 3] |
% |
map | %ages = {"ada": 30} |
Crucially, accessing an element keeps the container's sigil, exactly as in Raku — but guji adds a separate static type underneath the sigil:
@items = [10, 20, 30]
@items[0] # 10 — sigil stays @, and the type is Int
%ages = {"ada": 30}
%ages{"ada"} # 30 — sigil stays %, and the type is Int
The sigil and the type are two different layers doing two different jobs. The
sigil says "this is a list"; the inferred static type says "…of Int," i.e.
@items : List[Int], so @items[0] : Int. A Perl programmer used to the sigil
being the type information will find this liberating: the sigil now carries only
the coarse shape, and a real, checked type system carries the precision.
That separation pays off in string interpolation, which all three languages share
as a sigil-driven feature. In guji a sigil-name interpolates directly, and { … }
interpolates an arbitrary expression:
$name = "ada"
@scores = [10, 20]
"hi $name, scores: @scores, total: { @scores.sum() }"
# => "hi ada, scores: [10, 20], total: 30"
(Every line above is real; guji's v0 evaluator prints exactly that string.)
Because guji is functional-first and immutable-by-default, the sigil also quietly
reinforces the language's "one obvious way" principle. Lists and maps are the only
two compound bindings that get their own sigil; everything else — an Int, a
Str, a class instance, an Option, even a function value — is a scalar $. So
the data-first method chains that guji favours read with a single, stable list
sigil from start to finish:
@nums = [1, 2, 3, 4, 5, 6]
@nums.filter({ $_ % 2 == 0 }).map({ $_ * 2 }).sum()
# => 24
The @ never wavers as the pipeline transforms the list, and the topic variable
$_ inside each lambda is a scalar, so it wears $. The same discipline shows up
in match, guji's exhaustive pattern-matching expression, where bound captures are
scalars:
$desc = match $n {
0 { "zero" }
$x if $x < 0 { "negative" }
_ { "positive" }
}
What the lineage teaches
Trace the $ across thirty-odd years and you watch one idea get progressively
disambiguated:
- Perl (1987) made the sigil describe the result of an access. Expressive, compact, context-sensitive — and something you have to decode together with the brackets.
- Raku (Perl 6 → Raku, 2019) made the sigil describe the container and fixed it as part of the name. Same three glyphs, invariant grammar.
- guji keeps Raku's invariance and layers a real static type underneath, so the sigil carries shape and the type system carries precision — three sigils, no more, in service of "one obvious way."
The glyphs are nearly unchanged from 1987. What changed is the promise they make. Perl's sigil whispered "here is the shape of what you're about to get." Raku's and guji's sigils declare, plainly and permanently, "here is what this binding is." That is the whole arc of the lineage: the same three marks, asked to mean exactly one thing each.