Perl in 2025
Paul “LeoNerd” Evans (PEVANS)
Who Am I?
CPAN "PEVANS"� https://metacpan.org/author/PEVANS
TPF Grantee
Who Am I?
IRC - <LeoNerd> on
Freenode
irc.perl.org
Meet people in community
Perl in 2020
2020 Perl Advent Calendar
Writing Perl in 2020
Perl in 2020�Signatures from Perl 5.20+
use Carp;
sub get_page_title {
my ($url) = @_;
...
}
use feature qw(signatures);
sub get_page_title($url) {
...
}
use feature qw(signatures);
my $compare = sub($x) { $x > 0 };
use feature qw(signatures);
my $compare = sub($x) { $x > 0 };
my $compare = sub { my ($x) = @_; $x > 0 };
my $compare = sub { $_[0] > 0 };
Perl in 2020�isa from Perl 5.32+
use Scalar::Util qw(blessed);
if(blessed $obj and $obj->isa("Butterfly")) {
...
}
use Safe::Isa;
if($obj->$_isa("Butterfly")) {
...
}
use feature qw(isa);
if($obj isa Butterfly) {
...
}
Perl in 2020�Exception Handling
my $ok = eval {
my $response = GET("http://my-site-here.com");
say "Got a response";
1;
};
if(!$ok) {
my $e = $@;
print STDERR "It failed: $e\n";
}
my $ok = eval {
my $response = GET("http://my-site-here.com");
say "Got a response";
1;
};
if(!$ok) {
my $e = $@;
print STDERR "It failed: $e\n";
}
sub get_page_title($url) {
my $ok = eval {
my $response = GET($url);
return $response->page_title;
};
...
Perl in 2020�Exception Handling
my $ok = eval {
my $response = GET("http://my-site-here.com");
say "Got a response";
1;
};
if(!$ok) {
my $e = $@;
print STDERR "It failed: $e\n";
}
use Syntax::Keyword::Try;
try {
my $response = GET("http://my-site-here.com");
say "Got a response";
}
catch($e) {
print STDERR "It failed: $e\n";
}
use Syntax::Keyword::Try;
sub get_page_title($url) {
try {
my $response = GET($url);
return $response->page_title;
}
catch($e) {
return undef;
}
}
Perl in 2020�Asynchronous Programming
sub _cached_read
{
my $self = shift;
return $self->{_bytes} if $self->{_bytes};
return $self->_read->then(sub {
($self->{_bytes}) = @_;
return Future->done($self->{_bytes});
});
}
use Future::AsyncAwait;
async sub _cached_read
{
my $self = shift;
return $self->{_bytes} //= await $self->_read;
}
Perl in 2020�Object classes
package Point {
sub new {
my $class = shift;
bless {x => $_[0], y => $_[1]}, $class;
}
sub describe {
my $self = shift;
say "A point at ($self->{x}, $self->{y})";
}
}
use Object::Pad;
class Point {
has $x; has $y;
BUILD { ($x, $y) = @_; }
method describe { say "A point at ($x, $y)"; }
}
Perl in 2020�Object classes and Cor
Inspired by Cor by Curtis Poe (aka "Ovid")�https://github.com/Ovid/Cor/wiki
Cor's idea to Object::Pad's implementation
Cor - The Future of OO in Perl�https://fosdem.org/2021/schedule/event/future_of_oo_perl_cor/
Perl in 2020�Combining Syntax Modules
use Future::AsyncAwait;
use Syntax::Keyword::Try;
async sub get_page_images($url)
{
try {
return (await GET($url))->images;
}
catch {
return ();
}
}
use Future::AsyncAwait;
use Object::Pad;
...
has $_bytes;
async method _cached_read
{
return $_bytes //= await $self->_read;
}
sub _cached_read
{
my $self = shift;
return $self->{_bytes} if $self->{_bytes};
return $self->_read->then(sub {
($self->{_bytes}) = @_;
return Future->done($self->{_bytes});
});
}
Perl in 2025
Some complete
Some more vague
Some can be CPAN modules
Some must be core
Perl in 2025
A lot of them combine together
Feedback welcome
Let's BEGIN
Perl in 2025�No more smartmatch
Imported from Perl 6 (as was)
Perl in 2025�No more smartmatch
Imported from Raku
Raku types
#!/usr/bin/raku
my $str = "5";
my $num = 5;
say "str is $str and num is $num";
# types remain unchanged
Perl in 2025�No more smartmatch
Imported from Raku
Perl types
#!/usr/bin/perl
my $str = "5";
my $num = 5;
say "str is $str and num is $num";
# $num is now both number and string
Perl in 2025�No more smartmatch
Imported from Raku
Unpredictable rules
5 ~~ 5.0
5 ~~ "5.0"
"5" ~~ "5.0"
true
true
false
Perl in 2025�No more smartmatch
Imported from Raku
Unpredictable rules
5 ~~ [1..10]
"x" ~~ {x => 12, y => 34}
"a" ~~ {x => 12, y => 34}
true
true
false
Perl in 2025�No more smartmatch
Imported from Raku
Unpredictable rules
5 ~~ sub { $_ % 2 == 0 }
6 ~~ sub { $_ % 2 == 0 }
%ENV ~~ sub { $_ % 2 == 0 }
false
true
???
Perl in 2025�No more smartmatch
given/when is useful but relies on ~~
use feature qw(switch);
given($var) {
when("abc") { ... }
when("def") { ... }
when("1.0") { ... }
otherwise { ... }
}
if ($var ~~ "abc") { ... }
elsif($var ~~ "def") { ... }
elsif($var ~~ "1.0") { ... }
else { ... }
Perl in 2025�match/case
if ($var eq "abc") { ... }
elsif($var eq "def") { ... }
elsif($var eq "1.0") { ... }
else { ... }
use Hypothetical::MatchCase;
match($var : eq) {
case("abc") { ... }
case("def") { ... }
case("1.0") { ... }
otherwise { ... }
}
use Hypothetical::MatchCase;
match($var : eq) {
case("abc") { ... }
case("def") { ... }
case("1.0") { ... }
otherwise { ... }
}
use Hypothetical::MatchCase;
match($var : eq) {
case("abc") { ... }
case("def") { ... }
case("1.0") { ... }
otherwise { ... }
}
Perl in 2025�match/case
if ($var == 1) { ... }
elsif($var == 2) { ... }
elsif($var == 3) { ... }
else { ... }
use Hypothetical::MatchCase;
match($var : ==) {
case(1) { ... }
case(2) { ... }
case(3) { ... }
otherwise { ... }
}
Perl in 2025�match/case
Compare to Switch::Plain
use Switch::Plain;
sswitch($var) {
case "abc": { ... }
case "def": { ... }
case "1.0": { ... }
default: { ... }
}
use Switch::Plain;
nswitch($var) {
case 1: { ... }
case 2: { ... }
case 3: { ... }
default: { ... }
}
Perl in 2025�match/case
if ($str =~ m/^foo/) { ... }
elsif($str =~ m/^bar/) { ... }
elsif($str =~ m/^qux/) { ... }
else { ... }
use Hypothetical::MatchCase;
match($str : =~) {
case(m/^foo/) { ... }
case(m/^bar/) { ... }
case(m/^qux/) { ... }
otherwise { ... }
}
Perl in 2025�match/case
if ($fruit isa Apple) { ... }
elsif($fruit isa Orange) { ... }
elsif($fruit isa Tomato) { ... }
else { ... }
use Hypothetical::MatchCase;
match($fruit : isa) {
case(Apple) { ... }
case(Orange) { ... }
case(Tomato) { ... }
otherwise { ... }
}
Perl in 2025�match/case
use Hypothetical::MatchCase;
match($n : ==) {
case(0) { say "No items" }
case(1) { say "An item" }
case(2,3,4) { say "Few items" }
otherwise { say "Many items" }
}
if ($n == 0) { say "No items" }
elsif($n == 1) { say "An item" }
elsif($n == 2 or $n == 3 or $n == 4)
{ say "Few items" }
else { say "Many items" }
Perl in 2025�match/case - some comments
Only constants for case(), no variables
Uniqueness can be checked at compile-time
More efficient operation
Fixed set of operators
eq, ==, =~, isa
Perl in 2025�match/case - open questions
Do we handle undef?
given($str) {
when(undef) { say "Undefined" }
when("") { say "Empty" }
when("abc") { say "A string" }
}
match($str : eq) {
case(undef) { say "Undefined" }
case("") { say "Empty" }
case("abc") { say "A string" }
}
Does it special-case?
if (!defined $str) { ... }
elsif($str eq "") { ... }
elsif($str eq "abc") { ... }
?
Perl in 2025�match/case - open questions
Do we handle undef?
match($str : eq) {
case(undef) { say "Undefined" }
case("") { say "Empty" }
case("abc") { say "A string" }
}
Do we need a new operator?
match($str : equ) {
case(undef) { say "Undefined" }
case("") { say "Empty" }
case("abc") { say "A string" }
}
$x equ $y
?
Perl in 2025�equ operator
undef equ undef # true
undef equ "anything" # false
undef equ undef # true
undef equ "anything" # false
undef equ "" # false
undef equ undef # true
undef equ "anything" # false
undef equ "" # false
"str" equ "str" # true
"abc" equ "def" # false
use warnings;
undef equ "anything" # silent
Perl in 2025�equ and === operators
undef equ undef # true
undef equ "anything" # false
undef equ "" # false
"str" equ "str" # true
"abc" equ "def" # false
undef === undef # true
undef === 100 # false
undef === 0 # false
200 === 200 # true
123 === 456 # false
Perl in 2025�equ and === operators
if(!defined $x and !defined $y or
defined $x and defined $y and $x eq $y) {
say "Same";
}
else {
say "Different";
}
if($x equ $y) {
say "Same";
}
else {
say "Different";
}
Perl in 2025�match/case - open questions
match($num : ==) {
case(1) { say "One" }
case(2,3,4,5,6,7,8,9)
{ say "Some" }
}
Do we handle ranges?
match($num : ==) {
case(1) { say "One" }
case(2..9) { say "Some" }
}
if ( $num == 1) { say "One" }
elsif( 2 <= $num <= 9) { say "Some" }
Perl in 2025�match/case - open questions
Do we handle ranges?
match($num : ==) {
case(1) { say "One" }
case(2..9) { say "Some" }
case(10..) { say "Lots" }
}
if ( $num == 1) { say "One" }
elsif( 2 <= $num <= 9) { say "Some" }
elsif(10 <= $num ) { say "Lots" }
Perl in 2025�match/case - open questions
Do we handle ranges?
match($num : ==) {
case(1) { say "One" }
case(2..9) { say "Some" }
case(10..) { say "Lots" }
}
Not really any-like now
match(2.5 : ==) {
case(0..9) { say "Lone digit" }
}
Perl in 2025�any as basic syntax
if ($n == 0) { say "No items" }
elsif($n == 1) { say "An item" }
elsif($n == 2 or $n == 3 or $n == 4)
{ say "Few items" }
else { say "Many items" }
use List::Util qw(any);
...
elsif(any {$n == $_} 2, 3, 4) { say "Few items" }
use List::Util qw(any);
...
elsif(any {$n == $_} 2, 3, 4) { say "Few items" }
Perl in 2025�any as basic syntax
if ($n == 0) { say "No items" }
elsif($n == 1) { say "An item" }
elsif($n == 2 or $n == 3 or $n == 4)
{ say "Few items" }
else { say "Many items" }
use Hypothetical::List::Ops qw(any);
...
elsif(any {$n == $_} 2, 3, 4) { say "Few items" }
Perl in 2025�all as basic syntax
use Hypothetical::List::Ops qw(all);
...
if(all {$_ < 100} @sizes) { say "All small" }
use Hypothetical::List::Ops qw(none);
...
if(none {$_ > 100} @sizes) { say "Nothing too big" }
Perl in 2025�in operator
use Hypothetical::List::Ops qw(any);
if(any {$n == $_} 2, 3, 4) { say "Few items" }
if($n in (2, 3, 4)) { say "Few items" }
Perl in 2025�in metaoperator
use Hypothetical::List::Ops qw(any);
if(any {$n == $_} 2, 3, 4) { say "Few items" }
use Hypothetical::List::Ops qw(in);
if($n in(==) (2, 3, 4)) { say "Few items" }
use Hypothetical::List::Ops qw(in);
if($n in(==) (2, 3, 4)) { say "Few items" }
if($n in<==> (2, 3, 4)) { say "Few items" }
use Hypothetical::List::Ops qw(in);
if($n in(==) (2, 3, 4)) { say "Few items" }
if($n in<==> (2, 3, 4)) { say "Few items" }
if($n in:== (2, 3, 4)) { say "Few items" }
use Hypothetical::List::Ops qw(any);
if(any {$n == $_} 2, 3, 4) { say "Few items" }
Perl in 2025�in metaoperator
use Hypothetical::List::Ops qw(in);
if($num in:== (2, 3, 4)) { say "Numerical" }
if($str in:eq ("ab", "cd")) { say "Stringy" }
my @patterns = (qr/^foo/, ...);�if($str in:=~ @patterns) { say "Regexp" }
my @classes = (Tomato, ...);�if($obj in:isa @classes) { say "Classy" }
use Hypothetical::List::Ops qw(in);
if($num in:=== (2, 3, 4)) ...
if($num in:equ ("ab", "cd")) ...
Perl in 2025�Checked list assignment
Signatures are sort-of like list unpack
use feature qw(signatures);
sub get_page_title($url) {
...
}
sub get_page_title {
my ($url) = @_;
...
}
sub get_page_title {
@_ >= 1 or croak "Too few arguments for subroutine";
@_ <= 1 or croak "Too many arguments for subroutine";
my ($url) = @_;
...
}
Perl in 2025�Checked list assignment
Signatures are sort-of like list unpack
use feature qw(signatures);
sub func($one, $two, $three = 3) {
...
}
sub func {
@_ >= 2 or croak "Too few arguments for subroutine";
@_ <= 3 or croak "Too many arguments for subroutine";
my $one = $_[0];
my $two = $_[1];
my $three = @_ > 2 ? $_[2] : 3;
...
Perl in 2025�Checked list assignment
use feature qw(bind);
my ($one, $two, $three = 3) := (1, 2);
use feature qw(bind);
my ($one, $two, $three = 3) := (1, 2);
my ($one, $two, $three = 3) := @values;
use feature qw(bind);
my ($one, $two, $three = 3) := (1, 2);
my ($one, $two, $three = 3) := @values;
my ($one, $two, $three = 3) := some_func();
use feature qw(let);
let ($one, $two, $three = 3) := some_func();
use Hypothetical::Let;
let ($one, $two, $three = 3) = some_func();
Perl in 2025�Checked list assignment
Signatures might gain named parameters
use feature qw(signatures);
sub set_rectangle(:$width, :$height) { ... }
set_rectangle(width => 10, height => 20);
use Hypothetical::Let;
let (:$width, :$height) = %rect;
my ($width, $height) = @rect{qw(width height)};
Perl in 2025�Checked list assignment
Signatures might gain isa assertions
use feature qw(signatures);
sub set_user($user isa User) { ... }
use Hypothetical::Let;
let ($user isa User) = @args;
Perl in 2025�Non-fatal list bind
use Hypothetical::Let;
let (:$width, :$height) = %rect;
use Hypothetical::Let;
if(maybelet (:$width, :$height) = %rect) {
Rectangle->new($width, $height);
}
use Hypothetical::Let;
while(maybelet ($item) = $coll->next) {
say "An item: $item";
}
Perl in 2025�Non-fatal list bind
use Hypothetical::Let;
while(maybelet ($item) = $coll->next) {
say "An item: $item";
}
maybelet ($item) = () # false
maybelet ($item) = ("thing") # true
maybelet ($item) = ("") # true
maybelet ($item) = (undef) # true
Perl in 2025�Twigils in Object::Pad
use Object::Pad;
class Point {
has $x; has $y;
BUILD { ($x, $y) = @_; }
method describe {
say "A point at ($x, $y)";
}
}
use Object::Pad;
class Point {
has $x; has $y;
BUILD { ($x, $y) = @_; }
method describe($z) {
say "A point at ($x, $y, $z)";
}
}
use Object::Pad;
class Point {
has $:x; has $:y;
BUILD { ($:x, $:y) = @_; }
method describe($z) {
say "A point at ($:x, $:y, $z)";
}
}
say "A point at ($self->{x}, $self->{y}, $z)";
Perl in 2025�Typing semantics
Types as static assertions
sub display_a_flag(Flag $flag) {
...
}
my string $str;
display_a_flag($str);
$ perl -c example.pl
Type mismatch for '$flag' argument to 'display_a_flag' (string cannot be a Flag) at example.pl line 123.
Perl in 2025�Typing semantics
Types as dynamic assertions
sub display_a_flag($flag isa Flag) {
...
}
my $str = "not a flag";
display_a_flag($str);
$ perl -c example.pl
example.pl syntax OK
$ perl example.pl
Type mismatch for '$flag' argument to display_a_flag (value "not a flag" cannot be a Flag) at example.pl line 123.
Perl in 2025�Typing semantics
Types as performance hints
sub display_the_flags(Flag @flags) {
my Palette $palette = Palette->new;
foreach my $flag (@flags) {
my @colours = $flag->get_colours;
$palette->add_colours(@colours);
}
...
}
Perl in 2025�Typing semantics
Types as static (compiletime) checks
Types as dynamic (runtime) checks
Types as performance hints
Perl in 2025�Typing semantics
Types as static (compiletime) checks
Types as dynamic (runtime) checks ✔
Types as performance hints
Perl in 2025�Type assertions
use feature qw(signatures);
sub set_user($user isa User) { ... }
use feature qw(signatures types);
sub set_height($user is NonNegative) { ... }
use feature qw(typing);
subtype NonNegative is Numerical where { $_ >= 0 };
Perl in 2025�Type assertions
use feature qw(signatures);
sub set_user($user isa User) { ... }
use feature qw(signatures types);
sub set_flag($flag is MulticolouredFlag) { ... }
use feature qw(types);
subtype MulticolouredFlag isa Flag where
{ $_->colours > 1 };
Perl in 2025�Type assertions
use feature qw(let types);
let (:$height is Positive, :$width is Positive) := %rect;
use feature qw(types);
subtype Positive is Numerical where { $_ > 0 };
Perl in 2025�Type assertions
use feature qw(types);
subtype Negative is Numerical where { $_ < 0 };
subtype Positive is Numerical where { $_ > 0 };
use feature qw(match types);
match($num : is) {
case(Negative) { say "Less than zero" }
case(Positive) { say "Greater than zero" }
}
Perl in 2025�Type assertions
use feature qw(types);
subtype Positive is Numerical where { $_ > 0 };
use feature qw(types);
my ($x, $x) is Numerical;
my ($width, $height) is Positive;
Perl in 2025�Type assertions
use feature qw(types);
subtype Positive is Numerical where { $_ > 0 };
use feature qw(class types);
class Rectangle {
has $x is Numerical; has $y is Numerical;
has $width is Positive; has $height is Positive;
}
Perl in 2025�multi sub
use feature qw(isa signatures);
sub speak($animal) {
if ($animal isa Cow) { say "Moo" }
elsif($animal isa Sheep) { say "Baa" }
else { die "Cannot" }
}
use feature qw(isa match signatures);
sub speak($animal) {
match($animal : isa) {
case(Cow) { say "Moo" }
case(Sheep) { say "Baa" }
otherwise { die "Cannot" }
}
}
use Hypothetical::Multi::Dispatch;
multi sub speak($animal isa Cow) {
say "Moo";
}
multi sub speak($animal isa Sheep) {
say "Baa";
}
Perl in 2025�multi sub
use feature qw(class types);
use Hypothetical::Multi::Dispatch;
multi method speak_to($target isa Goose)
{
$self->say("Boo", target => $target);
}
use feature qw(class types);
use Future::AsyncAwait;
use Hypothetical::Multi::Dispatch;
multi async method speak_to($target isa Goose)
{
await $self->say("Boo", target => $target);
}
Additional Syntax
match/case
equ and === operators
any, all as operators
in metaoperator
let as generic list bind
Type assertions with is
multi sub
Additional Syntax
A few standalone ideas
Many of them work best in combination
isa, is, equ, ===
try/catch, match/case, signatures, let, multi sub
Additional Syntax
multi sub speak($animal isa Cow) {
say "Moo";
}
multi sub speak($animal isa Sheep) {
say "Baa";
}
class ByteReader;
has $_bytes;
async method _cached_read
{
return $_bytes //= await $self->_read;
}
if($num in:=== (2, 3, 4)) ...
if($num in:equ ("ab", "cd")) ...
match($num : ==) {
case(1) { say "One" }
case(2..9) { say "Some" }
case(10..) { say "Lots" }
}
while(maybelet ($item) = $coll->next) {
say "An item: $item";
}
Migration to core
Migrate "stablised" experiments into core perl
use feature qw(try);
try {
...
}
catch($e) {
...
}
use feature qw(class);
class Point {
has $x;
has $y;
method zero {
...
}
}
Migration to core�async/await in core?
Requires a Future representation
https://metacpan.org/pod/Future has about 40 methods
Mostly predates async/await
Core would get a smaller cut-down class
Migration to core�use VERSION
use v5.10;
turns on features like say, state
use v5.16;
turns on features like fc, __SUB__
Migration to core�use VERSION
use v5.36;
try {
...
}
catch($e) {
...
}
use v5.36;
match($str : eq) {
case("a") { ... }
case("b") { ... }
case("c") { ... }
}
use v5.40;
class Point {
has $x;
has $y;
method zero {
...
}
}
use v5.36;
if($n in:== @nums) { ... }
use v5.36;
let ($x, $y) := get_size();
use v5.40;
subtype Bestagon isa Shape where
{ $_->sides == 6 };
my $hexagon is Bestagon;
Migration to core�use VERSION
use v7;
try {
...
}
catch($e) {
...
}
use v7;
match($str : eq) {
case("a") { ... }
case("b") { ... }
case("c") { ... }
}
use v7;
class Point {
has $x;
has $y;
method zero {
...
}
}
use v7;
if($n in:== @nums) { ... }
use v7;
let ($x, $y) := get_size();
use v7;
subtype Bestagon isa Shape where
{ $_->sides == 6 };
my $hexagon is Bestagon;
Thank You
Live Q&A