These are just my slides someone posted on HackerNews. You can watch the actual presentation here: https://www.youtube.com/watch?v=paa3niF72Nw
I also wrote down the answers for questions that came up during the presentation here: http://blogs.perl.org/users/zoffix_znet/2016/03/wow-perl-6-talk-slides-recording-and-answers-to-questions.html
You can navigate these slides by pressing Left
/ Right
arrows or by swiping on your mobile devices.
by Pete 'Zoffix Znet' Evstratov
This presentation is available at TPM2016.zoffix.com
If you did not understand what I said or what I'm talking about, simply raise your hand.
This presentation is available at TPM2016.zoffix.com
Perl 6 lets you use fancy-pants Unicode terms and operators that you'll see in this presentation. All of them have "Texas variants" that use ASCII characters only. If you rather use those, see http://docs.perl6.org/language/unicode_texas to find them.
Let's do something crazy... like create an INFINITE LIST!!
1: my @to-infinity-and-beyond = 0, 2 … ∞;
2: say @to-infinity-and-beyond[1008];
# OUTPUT:
# 2016
Something more useful: working with a giant file:
1: for '/tmp/4GB-file.txt'.IO.words {
2: .say;
3: last if ++$ == 3;
4: }
5: say "Code took {now - INIT now} seconds to run";
# OUTPUT:
# foo
# bar
# ber
# Code took 0.01111143 seconds to run
Something more useful: working with a giant file
1: .say for '/tmp/4GB-file.txt'.IO.words[0..2];
2: say "Code took {now - INIT now} seconds to run";
# OUTPUT:
# foo
# bar
# ber
# Code took 0.01111143 seconds to run
A subset of a type lets you restrict the values it accepts:
1: subset BigPrime of Int where { $_ > 10_000 and .is-prime }
2:
3: sub MAIN ( BigPrime $num ) {
4: say "That's a nice-looking prime number you got there!";
5: }
$ perl6 test.p6 3 Usage: test.p6 <num> $ perl6 test.p6 31337 That's a nice-looking prime number you got there! $ perl6 test.p6 100000 Usage: test.p6 <num>
Multiple subs or methods of same name, but different parameters:
1: subset Prime of Int where *.is-prime;
2: subset BigPrime of Prime where * > 10_000;
3: subset SmallPrime of Prime where * <= 10_000;
4:
5: multi MAIN ( BigPrime $num ) { say "Prime number! Nice and big"; }
6: multi MAIN ( SmallPrime $num ) { say "Puny prime number"; }
7: multi MAIN ( $num ) { say "Gimme primes!"; }
$ perl6 test.p6 42 Gimme primes! $ perl6 test.p6 7 Puny prime number $ perl6 test.p6 31337 Prime number! Nice and big
Multiple subs or methods of same name, but different parameters:
1: class Numbers {
2: multi method id ( Numeric $num ) { say "$num is a number" }
3: multi method id ( $num ) { say "$num is something else" }
4: }
5: Numbers.new.id: π;
6: Numbers.new.id: 'blah';
# OUTPUT:
# 3.14159265358979 is a number
# blah is something else
Extend method functionality:
1: class Numbers {
2: multi method id ( Numeric $num ) { say "$num is a number" }
3: multi method id ( $num ) { say "$num is something else" }
4: }
5:
6: class SmarterNumbers is Numbers {
7: multi method id ( Numeric $num where * == π ) { say "Mmmm yummy pie!" }
8: }
9:
10: SmarterNumbers.new.id: 42;
11: SmarterNumbers.new.id: π;
12: SmarterNumbers.new.id: 'blah';
# OUTPUT:
# 42 is a number
# Mmmm yummy pie!
# blah is something else
1: sub infix:<¯\(°_o)/¯> {
2: ($^a, $^b).pick
3: }
4:
5: say 'Coke' ¯\(°_o)/¯ 'Pepsi';
# OUTPUT:
# Pepsi
Operator categories: infix
, prefix
, postfix
, circumfix
, postcircumfix
and you can also use term
for terms.
1: sub prefix:<∑> (*@els) { @els.sum }
2: say ∑ 1, 2, 3, 4;
# OUTPUT:
# 1234
Doesn't seem to work well...
1: sub prefix:<∑> (*@els) is looser(&infix:<,>) { @els.sum }
2: say ∑ 1, 2, 3, 4;
# OUTPUT:
# 10
Use is looser
/ is tighter
to change precedence.
Default precedence is same as +
/++
operator in that position.
(See docs on how to change associativity with is assoc
trait)
More examples:
1: sub term:<ξ> { (^10 + 1).pick; }
2: sub postcircumfix:<❨ ❩> ($before, $inside) is rw {
3: $before{$inside};
4: }
5:
6: my %hash = :foo<bar>, :meow<moo>;
7: %hash❨'foo'❩ = ξ;
8: %hash❨'meow'❩ = ξ;
9:
10: say %hash;
# OUTPUT:
# foo => 6, meow => 8
Overriding existing operators:
1: sub infix:<+> (Int $a, Int $b) { $a - $b };
2: say 2 + 2;
# OUTPUT:
# 0
Don't worry... this effect is lexically scoped!
Overriding existing operators:
1: class Thingamagig { has $.value };
2:
3: multi infix:<~> (Thingamagig $a, Str $b) {
4: $a.value ~ $b
5: }
6:
7: my $thing = Thingamagig.new: value => 'thingamagig';
8: say 'foo' ~ 'bar';
9: say $thing ~ 'bar';
10: say 'bar' ~ $thing;
# OUTPUT:
# foobar
# thingamagigbar
# barThingamagig<139870715547680>
See Color::Operators module from Color distribution for more examples.
Punch it, Chewie.
They look like «
and »
and you might see them explained with examples like these:
(1, 2) «+« (3)
(1, 2) »+» 1
(1, 2, 3, 4) «+» (1, 2)
Those won't be multi-threaded for a while, if at all. Instead, the variant I'll talk about is this one:
@foo».bar
Let's say you want to uppercase each string in your array and then break that array up into sublists of 3 elements each:
1: my @a = <one two three four five six seven eight nine>;
2: say @a.map({ .uc }).rotor: 3;
# Output:
# ((ONE TWO THREE) (FOUR FIVE SIX) (SEVEN EIGHT NINE))
It's nice and short, but what if you want to call a more time-consuming method on thousands of elements?
Just use a hyper operator before the method call:
1: my @a = <one two three four five six seven eight nine>;
2: say @a».uc.rotor: 3;
# OUTPUT:
# ((ONE TWO THREE) (FOUR FIVE SIX) (SEVEN EIGHT NINE))
Put » before the dot method call and the method you're calling will be called on individual elements instead. Further methods in the chain will be called on the array (list, etc.), unless they're hypered as well.
BONUS: it's a compiler hint to use multiple threads.
What if you want to "do stuff" on a bunch of things, but on multiple cores? Loop over a HyperSeq
.
You get one by calling either:
.hyper
—preserves element order
.race
—does not preserve element order
Iterate over a 4-element sequence, sleep for 1 second for each element:
1: for (1..4).race( batch => 1 ) {
2: say "Doing $_";
3: sleep 1;
4: }
5: say "Code took {now - INIT now} seconds to run";
# OUTPUT:
# Doing 1
# Doing 3
# Doing 2
# Doing 4
# Code took 1.0090415 seconds to run
Code runs for just over 1 second!
.hyper
is the same, but it preserves the order of elements in the resulting sequence.
Code with some logical checks:
1: my @valid = <foo bar baz>;
2: my $what = 'ber';
3: say "$what is not valid" if not @valid.grep: { $what eq $_ };
4: say "A ber or a bar" if $what eq 'ber' or $what eq 'bar';
# OUTPUT:
# ber is not valid
# A ber or a bar
1: my @valid = <foo bar baz>;
2: my $what = 'ber';
3: say "$what is not valid" if $what eq none @valid;
4: say "A ber or a bar" if $what eq 'ber' | 'bar';
# OUTPUT:
# ber is not valid
# A ber or a bar
type | constructor | operator | True if ... |
---|---|---|---|
all | all | & | no value evaluates to False |
any | any | | | at least one value evaluates to True |
one | one | ^ | exactly one value evaluates to True |
none | none | no value evaluates to True |
Best part? Junctions are autothreaded, meaning they are a hint to the compiler it can evaluate them on multiple threads!
I don't always write concurrent code,
but when I do, it's THIS simple:
1: start { sleep 3; say "two" };
2: say "one";
3: sleep 5;
4: say "three";
# OUTPUT:
# one
# two
# three
Concurrent / asynchronous code:
1: my @promises = ^3 .map: {
2: start {
3: .say; sleep 1;
4: $_ * 4;
5: }
6: };
7: say "Started! {now - INIT now}";
8: say await @promises;
9: say "All done! {now - INIT now}";
# OUTPUT:
# 0
# 1
# 2
# Started! 0.0196113
# (0 4 8)
# All done! 1.0188611
Start later:
1: Promise.in(5).then: -> $v { say "It's been {now - INIT now} seconds!" };
2: sleep 7;
3: say "Ran for {now - INIT now} seconds"
# OUTPUT:
# It's been 5.031918 seconds!
# Ran for 7.0160562 seconds
Asynchronous data streaming:
1: my $supplier = Supplier.new;
2:
3: $supplier.Supply .tap: -> $v { say "Original: $v" };
4: $supplier.Supply.map( * × 2 ).tap: -> $v { say " Double: $v" };
5: $supplier.Supply.grep( * % 2 ).tap: -> $v { say " Odd: $v" };
6:
7: $supplier.emit: $_ for ^3;
# OUTPUT:
# Original: 0
# Double: 0
# Original: 1
# Double: 2
# Odd: 1
# Original: 2
# Double: 4
Events at interval inside an event loop (react
):
1: react {
2: whenever Supply.interval: 2 -> $v {
3: say "$v: {now - INIT now}";
4: done if $v == 2;
5: }
6: whenever Supply.interval: 1 -> $v { say " 1 sec: {now - INIT now}"; }
7: }
# OUTPUT:
# 0: 0.026734
# 1 sec: 0.0333274
# 1 sec: 1.02325708
# 1: 2.027192
# 1 sec: 2.0276854
# 1 sec: 3.0234109
# 2: 4.0324349
A thread-safe queue:
1: my $c = Channel.new;
2: start {
3: loop { say "$c.receive() at {now - INIT now}" }
4: }
5: await ^10 .map: -> $r {
6: start {
7: sleep $r;
8: $c.send: $r;
9: }
10: }
11: $c.close;
# OUTPUT:
# 0 at 0.01036314
# 1 at 1.0152853
# 2 at 2.0174991
# 3 at 3.020067105
# 4 at 4.01953470
# 5 at 5.0195884
# 6 at 6.0205915
# 7 at 7.020651
# 8 at 8.02339744
1: grammar MyGrammar {
2: token TOP { <sign> <digits> <decimal>? }
3: token sign { <[+-]>? }
4: token digits { \d+ }
5: token decimal { \. <digits> }
6: }
7:
8: say MyGrammar.parse: '250.42';
# OUTPUT:
# 「250.42」
# sign => 「」
# digits => 「250」
# decimal => 「.42」
# digits => 「42」
1: grammar MyGrammar {
2: token TOP { <sign> <digits> <decimal>? }
3: token sign { <[+-]>? }
4: token digits { \d+ }
5: token decimal { \. <digits> }
6: }
7:
8: class MyActions {
9: method sign ($/) { $/.make: $/.chars ?? ~$/ !! '+' }
10: method TOP ($/) { $/.make: $<sign>.made ~ ($<digits> + 42 ) ~ $<decimal> }
11: }
12:
13: say MyGrammar.parse('250.42', actions => MyActions).made;
# OUTPUT:
# +292.42
Grammar::Debugger
and Grammar::Tracer
—just use
one of the modules in the distro to get debug output for your grammars
Grammar::BNF
—convert BNF to Perl 6 grammars automagically!!
Use WhateverStar as a quick way to get a closure with arguments:
1: say (* + 2)(2);
2: say <1 25 3 100>.grep: * > 5;
3: subset Primes of Int where *.is-prime;
4:
5: # same as
6:
7: say sub { $^a + 2 }(2);
8: say <1 25 3 100>.grep: { $_ > 5 };
9: subset Primes of Int where { $_.is-prime };
Each WhateverStar represents the next positional argument. You can't use WhateverStar to refer to the same argument more than once:
say ( * + * + * )(2, 3, 4);
# OUTPUT:
# 9
n-at-a-time .map
never looked simpler:
say ^12 .map: * + * + *;
# OUTPUT:
# (3 12 21 30)
Look, ma! The entire Fibonacci sequence in a lazy list!
1: my @fibonacci = 0, 1, * + * … *;
2: say @fibonacci[42];
# OUTPUT:
# 267914296
Just sayin'...
1: say ( ** + 2 )(^10);
2: say ( * + 2 )(^10);
# OUTPUT:
# (2 3 4 5 6 7 8 9 10 11)
# 2..^12
Same as if you placed the operator that is inside the brackets between each element in the list:
1: say [+] 1, 2, 3; # 6
2: say [*] 1..5; # 120
3:
4: my @numbers = (2, 4, 3);
5: say [<] @numbers; # False
Triangle Reduce: use operator between two successive elements, take the result and use the operator between it and the next element:
1: say [\+] 1, 2, 3;
2: say [\*] 1..5;
# OUTPUT:
# (1 3 6) ## Breaking it down: 1 (1), 1 + 2 (3), 3 + 3 (6)
# (1 2 6 24 120)
1: class Foo {
2: has $.attr;
3: has $.attr-rw is rw;
4: has $.attr-required is required;
5: has $!attr-private = 42;
6:
7: method public { say $!attr + $!attr-private; }
8: method !private { say 'secret' }
9: }
10:
11: my $foo = Foo.new: :attr<public>,
13: :attr-required<yup-here-it-is>;
14:
15: say $foo.attr;
16: say $foo.public;
17: $foo.attr-rw = 42;
You can specify types and subset restrictions too:
1: class Foo {
2: subset Primes of IntStr where *.is-prime;
3:
4: has Int $.attr;
5: has Str $.attr-rw is rw where *.chars < 5;
6: has Primes $.attr-required is required;
7: }
Roles—safely add behaviour to your classes:
1: role Drawable {
2: method draw { say "Line from $.x to $.y" };
3: }
4: class Line does Drawable {
5: has $.x;
6: has $.y;
7: }
8: my $line = Line.new: :42x, :75y;
9: $line.draw;
# OUTPUT:
# Line from 42 to 75
Roles—safely add behaviour to your classes:
1: role Drawable {
2: method draw { say "Line from $.x to $.y" };
3: }
4: role OtherDrawable {
5: method draw { say "It's a draw!" };
6: }
7: class Line does Drawable does OtherDrawable {
8: has $.x;
9: has $.y;
10: }
11: my $line = Line.new: :42x, :75y;
12: $line.draw;
# OUTPUT:
# ===SORRY!=== Error while compiling /home/zoffix/CPAN/TPM-2016/test.p6
# Method 'draw' must be resolved by class Line because it exists in multiple
# roles (OtherDrawable, Drawable) at /home/zoffix/CPAN/TPM-2016/test.p6:7
Introspection and the Perl 6 Object System
1: #| Just 'cause
2: class TestStuff {};
3: my $x = TestStuff.new;
4: say $x.WHAT; # (TestStuff) # The type object of the type
5: say $x.WHICH; # TestStuff|179689128 # The object's identity value
6: say $x.WHO; # TestStuff # The package supporting the object
7: say $x.WHERE; # -1225903068 # The memory address of the object (not stable)
8: say $x.HOW; # Perl6::Metamodel::ClassHOW.new # The metaclass object
9: say $x.WHY; # Just 'cause # The attached Pod value.
10: say $x.DEFINITE; # True # Returns True for instances and False for type objects
1: Int.^add_method('x', -> $class, $v { say $v });
2: constant A := Metamodel::ClassHOW.new_type( name => 'A' );
3: A.^add_method('x', -> $class, $v { say $v });
4: #A.^compose;
5:
6: A.x: 'A class';
7: Int.x: 'Int class';
Immutable:
Set
—collection of distinct objects
Bag
—collection of distinct objects with integer weights
Mix
—collection of distinct objects with real weights
Mutable:
SetHash
—collection of distinct objects
BagHash
—collection of distinct objects with integer weights
MixHash
—collection of distinct objects with real weights
Counting things? A Bag
will do:
.say for 'This is just a sentence'.comb.Bag;
# OUTPUT:
# n => 2
# a => 1
# => 4
# c => 1
# j => 1
# s => 4
# T => 1
# e => 3
# t => 2
# i => 2
# u => 1
# h => 1
Write concise code with set operators:
1: my $valid = set <foo bar ber boor>;
2: my @given = <foo meow bar ber>;
3:
4: say ‘Something's wrong’ unless @given ⊆ $valid;
Write concise code with set operators:
1: say <foo bar> ∪ <bar meow>; # Union operator
2: say <foo bar> ⊖ <bar meow>; # Symmetric set difference operator
# OUTPUT:
# set(foo, bar, meow)
# set(foo, meow)
See the docs for full list of operators.
Use other languages from within Perl 6!
Use C libraries without writing any C code!!
(more or less)
Standard C library:
1: use NativeCall;
2: sub fork is native {};
3: fork;
4: say $*PID;
# OUTPUT:
# 11274
# 11275
There's system
in standard C library:
Using a C library:
1: class STMT is repr('CPointer') { };
2: sub sqlite3_column_text(STMT, int32)
3: returns Str
4: is native('sqlite3', v0) { };
int8 (int8_t in C, also used for char)
int16 (int16_t in C, also used for short)
int32 (int32_t in C, also used for int)
int64 (int64_t in C)
uint8 (uint8_t in C, also used for unsigned char)
uint16 (uint16_t in C, also used for unsigned short)
uint32 (uint32_t in C, also used for unsigned int)
uint64 (uint64_t in C)
long (long in C)
longlong (long long in C, at least 64-bit)
num32 (float in C)
num64 (double in C)
Str (C string)
CArray[int32] (int* in C, an array of ints)
Pointer[void] (void* in C, can point to all other types)
bool (bool from C99)
size_t (size_t in C)
Use App::GPTrixie to convert C headers into Perl 6 sub definitions:
$ gptrixie --all /usr/local/include/gumbo.h
## Enumerations
enum GumboNamespaceEnum is export (
GUMBO_NAMESPACE_HTML => 0,
GUMBO_NAMESPACE_SVG => 1,
GUMBO_NAMESPACE_MATHML => 2
);
enum GumboParseFlags is export (
GUMBO_INSERTION_NORMAL => 0,
GUMBO_INSERTION_BY_PARSER => 1,
...
Use any Perl 5 module in Perl 6!
1: use Inline::Perl5;
2: use Mojo::DOM:from<Perl5>;
3:
4: my $dom = Mojo::DOM.new: '<p><b>This is awesome</b>, trust me</p>';
5:
6: say $dom.at('b').all_text;
# OUTPUT:
# This is awesome
Mileage may vary:
Perl 6 is [mostly] written in Perl 6
Regular Perl 6 users can make Perl 6 better!
The basics are written in NQP (Not Quite Perl): https://github.com/perl6/nqp
The rest is written in Perl 6: https://github.com/rakudo/rakudo
Yup, just a Perl 6 Grammar:
What line is the error at?
1: my $promise = start {
2: say 0/0;
3: # A whole
4: # bunch
5: # of other code
6: };
7:
8: say await $promise;
# OUTPUT:
# Attempt to divide by zero using div
# in block <unit> at test.p6 line 8
Include a backtrace:
1: my $promise = start {
2: say 0/0;
3: # A whole
4: # bunch
5: # of other code
6: CATCH { warn .backtrace };
7: };
8:
9: say await $promise;
# OUTPUT:
# in block at test.p6 line 2
# in block at test.p6 line 6
# Attempt to divide by zero using div
# in block <unit> at test.p6 line 9
Rat
is a Perl 6 type that represents a number with a numerators and denominator:
1: my $x = 0/0;
2: say 'Universe is still unimploded';
3: say sin $x;
# OUTPUT:
# Universe is still unimploded
# NaN
Actual number is calculated only when needed:
1: my $x = 0/0;
2: say 'Universe is still unimploded';
3: say $x;
# OUTPUT:
# Universe is still unimploded
# Attempt to divide by zero using div
# in block <unit> at test.p6 line 3
#
# Actually thrown at:
# in block <unit> at test.p6 line 3
Async communication with other programs:
1: my $proc = Proc::Async.new: :w, 'grep', 'foo';
2: $proc.stdout.tap: -> $v { print "Output: $v" };
3:
4: say 'Starting grep process...';
5: my $promise = $proc.start;
6: $proc.say: 'this line has foo';
7: $proc.say: "this one doesn't";
8: $proc.close-stdin;
9: await $promise;
10: say 'Done.';
# OUTPUT:
# Starting grep process...
# Output: this line has foo
# Done.
say
too much!say
function/method is for displaying brief info for human consumption and calls objects' gist
method.
If you want to output large chunks of stuff, use put
function/method:
1: ^101 .say;
2: (0..100).list.say;
3: say '----------';
4: ^101 .put;
5: (0..100).put;
# OUTPUT:
# ^101
# (0 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 ...)
# ----------
# 0 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
# 0 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Make your objects work right when say
-ed, put
-ed, or anything else:
1: class Thingamagig {
2: method gist { 'Brief info' }
3: method Str { 'Not so brief info' }
4: method Numeric { 42 }
5: }
6:
7: my $thing = Thingamagig.new;
8: say $thing;
9: put $thing;
10: say $thing + 2;
# OUTPUT:
# Brief info
# Not so brief info
# 44
Just specify --profile
flag on the command line, and the HTML file with results will be generated for you.
$ perl6 --profile test.p6
Questions?