NAME
Confold - the <: compile time constant operator
VERSION
Version 0.03
SYNOPSIS
use Confold;
my $x = <: 42; # folded at compile time
my $y = <: "hello"; # folded at compile time
my $z = <: $variable; # runtime: creates immutable copy
DESCRIPTION
Introduces the <: prefix operator, which marks an expression as a compile-time constant. When the operand is a literal value, the operator is folded away entirely at compile time, leaving a plain constant with no runtime overhead at all. For non-constant operands, a runtime path creates an immutable copy.
The operator exists to say something to the compiler that a function call cannot. By the time an ordinary subroutine call has been built, the expression is wrapped in an entersub and every call checker and optimisation pass downstream has lost sight of what it contains. <: is visible earlier, so a value marked with it can still be recognised as simple.
my $a = <: 42; # compiles to: my $a = 42;
The immutable copy is the same promise enforced at run time. Perl aliases @_ to the caller's variables, so an ordinary argument can be modified by the subroutine it is passed to; an argument marked with <: cannot.
sub clobber { $_[0] = "changed" }
my $open = "original";
clobber($open); # $open is now "changed"
my $shut = "original";
clobber(<: $shut); # dies: Modification of a read-only value
Compile-time variables
A variable declared with <: has a value the compiler can read:
my $slot = <: 'age:Int';
Perl cannot normally do this. A my variable is not populated until run time, so anything running while the program is still being compiled sees nothing there. That is why a class definition has to be repeated as a literal inside BEGIN, rather than named once and reused. With <: it does not:
use Object::Proto;
my $class = <: 'Person';
my $slot = <: 'age:Int';
BEGIN { Object::Proto::define($class, $slot) } # both are readable here
The value is passed on rather than the variable, both to subroutines and to anything inspecting the code as it compiles. So a module that examines its arguments during compilation - a call checker - sees an actual value and can act on it. Object::Proto uses this to settle a typed slot's check once instead of on every assignment:
my $age = <: 42;
Person::age($p, $age); # the type check is resolved while compiling
Assigning to such a variable retires it: later reads go back to reading the variable, so they never return a value that has stopped being true. Because the value rather than the variable is passed, @_ aliasing cannot write through it, and a subroutine that assigns to $_[0] gets a read-only error.
What it does not do
<: folds an operand that is already constant. It does not evaluate arbitrary expressions at compile time, so it will not turn a function call into a constant, and it does not promote anything Perl had not already folded on its own. Its value is the marker and the immutable copy, not new folding.
Precedence
<: binds as tightly as \ and unary minus, so it takes the smallest term to its right rather than the whole expression:
<: $a + $b # means: (<: $a) + $b
<: $a ** 2 # means: <: ($a ** 2) - ** binds tighter
<: $h->{k} * 2 # means: (<: $h->{k}) * 2
Parenthesise when a whole expression is meant:
<: ($a + $b)
Scope
The operator is lexically scoped. It is active from use Confold to the end of the enclosing block or file, and no Confold switches it off again. Outside an active scope <: means whatever it meant before.
Limitations
Within an active scope, <: is claimed as the operator wherever a term is expected. A glob whose pattern begins with a colon, <:foo>, is therefore a syntax error rather than a glob. Write glob(":foo") instead. Everywhere an operator is expected instead of a term, < is untouched, so comparisons, <=>, left shift and readline all behave normally.
Quoted text is never affected. The operator is recognised during tokenisation of code only, so "a <: b", '<:encoding(UTF-8)', here-documents and regular expressions all keep their contents.
Loading Confold enables Perl's pluggable-operator path for the rest of the process. That is a compile-time cost only, and applies to any module using that hook; execution speed of code that does not use <: is unaffected.
Compared with use constant
constant has covered part of the case this operator is since 5.004, and covers that part well. use constant PI => 3.14 writes an inlinable constant sub, so the use site compiles to a const op rather than a call, the value is readable from a BEGIN block compiled later, and passing it to a subroutine that assigns to $_[0] already dies read-only. For naming a literal, use constant. <: adds nothing there.
Three things constant cannot do.
Mark an expression rather than a name. <: applies at a use site to something that already exists, and declares nothing:
f(<: $h->{k});
Make a run-time value immutable. A constant's value has to exist at compile time. <: copies whatever the variable holds when the call is reached, so a value that is not known until run time can still be protected from @_ aliasing:
my $shut = read_from_the_database();
clobber(<: $shut); # dies: Modification of a read-only value
Scope to a block. use constant writes a sub into the package. It is visible outside the block it was written in, it answers to ->can, it is inherited like any other sub, and it cannot be reassigned. my $slot = <: 'age:Int' is an ordinary lexical that happens to carry a value the compiler can read, and assigning to it retires that value rather than being an error.
SEE ALSO
constant, Perl's built-in for naming a literal, and the right tool when that is what you want. The DESCRIPTION says where the two differ.
Infix::Custom, for user-defined infix operators.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION.
This is free software, licensed under the Artistic License 2.0.