Created
December 1, 2015 10:43
-
-
Save gfldex/2701af637775b0e25bff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 supermutant | |
16 Semper invicta! in block at 2bind.p6:52 | |
57 Cait | |
16 Semper invicta! in block at 2bind.p6:65 | |
70 Dogmeat | |
79 Scalar | |
80 Proxy | |
81 Proxy | |
92 False | |
93 True | |
95 True | |
96 False | |
108 Proxy | |
125 how often will it be fetched? | |
126 8 | |
138 Scalar | |
139 Proxy | |
140 Proxy | |
152 Proxy | |
155 Piper | |
168 0 1 1 2 3 5 8 13 21 34 | |
169 Seq | |
180 Cannot modify an immutable Int | |
207 11 | |
209 76 | |
222 Merry 1.0! | |
Cannot modify an immutable none really | |
in block <unit> at 2bind.p6:219 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use v6; | |
=begin pod | |
=head1 2 bind or !2 bind | |
Is a question one may want to answer while programming in Perl 6. Let me explain. | |
=end pod | |
sub BOS() { | |
my Str $c-value; # closure variable | |
return Proxy.new( | |
FETCH => method () { $c-value }, | |
STORE => method ($new-value) { | |
die $?LINE ~ ' Semper invicta!' if $new-value.tc ~~ any <Supermutant Ghoul Synth>; | |
$c-value = $new-value; | |
} | |
); | |
} | |
# Let's prefix &say with the line number it's called from | |
my sub say(**@args is raw) { | |
print callframe(1).line; | |
print ' ' ~ .Str ~ $*OUT.nl-out for @args | |
} | |
my Str $who-container = BOS; | |
my Str \who-raw = BOS; | |
my Str $who-bound := BOS; | |
=begin pod | |
Now we have three variables, of which one is a buildin container. All of them | |
refer to a 'magic' variable. Via the methods given to Proxy.new, we can | |
control what values are allowed to be stored. As you likely have guessed | |
already, our magic variable doesn't like Supermutants and other unhumanly | |
creatures. There is a catch. The container that is still a container wont do | |
what we expect. | |
=end pod | |
$who-container = 'supermutant'; | |
say $who-container; | |
try { | |
who-raw = 'ghoul'; | |
say who-raw; | |
CATCH { | |
when X::AdHoc { warn $_.Str }; # line 52 | |
} | |
} | |
who-raw = 'Cait'; | |
say who-raw; | |
try { | |
$who-bound = 'synth'; | |
say $who-bound; | |
CATCH { | |
when X::AdHoc { warn $_.Str }; # line 65 | |
} | |
} | |
$who-bound = 'Dogmeat'; | |
say $who-bound; | |
=begin pod | |
By handling X::AdHoc we can turn a fatal C<die> into a harmless C<warn>. For | |
C<$who-container> we don't need to do that. Let's see how they differ. | |
=end pod | |
say $who-container.VAR.^name; # Scalar | |
say who-raw.VAR.^name; # Proxy | |
say $who-bound.VAR.^name; # Proxy | |
=begin pod | |
With C<VAR> we get access to the typeobject of the container we introduce with | |
C<my> or C<our>. Binding allowes us to set the type of the container, what is | |
not the same thing as the type of the value stored in the container. Let's have | |
a look at the type checks. | |
=end pod | |
say so BOS() ~~ Proxy; # False | |
say so BOS() ~~ Str; # True | |
say so BOS().VAR ~~ Proxy; # True | |
say so BOS().VAR ~~ Str; # False | |
=begin pod | |
The type of the returned value if the type of C<$c-value>. The container type | |
is C<Proxy>. We can use that to decide at runtime if we need to bind. | |
=end pod | |
my Str $foo; | |
$foo = BOS if BOS().VAR ~~ Scalar; | |
$foo := BOS if BOS().VAR ~~ Proxy; | |
say $foo.VAR.^name; # Proxy | |
=begin pod | |
There is no way for a function to force binding on it's returned value. So we | |
have to be careful with returned values. Luckily Perl 6 provides us with enough | |
introspection so we can handle it at runtime. | |
A bound C<Proxy> can help us to probe Rakudo a little. | |
=end pod | |
my $c-zeta; | |
my $counter; | |
my $zeta := Proxy.new(FETCH => { $counter++; $c-zeta }, STORE => -> $, $new { $c-zeta = $new } ); | |
$zeta = 'how often will it be fetched?'; | |
say $zeta; | |
say $counter; # 5 | |
=begin pod | |
On Rakudo Beta 1 C<FETCH> is called 5 times just to C<.say> the value. Let's | |
see how far our probe will reach. | |
=end pod | |
f($zeta, $zeta, $zeta); | |
sub f($c, $c-rw is rw, \r){ | |
say $c.VAR.^name; # Scalar | |
say $c-rw.VAR.^name; # Proxy | |
say r.VAR.^name; # Proxy | |
} | |
=begin pod | |
The default for sub and method parameters C<is copy>, what does what is sayed | |
on the tin. Both C<is rw> and a sigilless parameter are using binding. | |
=end pod | |
constant lover = BOS(); | |
say lover.VAR.^name; # Proxy | |
lover = 'Piper'; | |
say lover; # Piper | |
=begin pod | |
If we declare a constant Perl 6 will also force a binding. Allowing | |
sigillessness may give it away. C<Proxy> isn't all that constant though. So we | |
can turn a constant value into a variable. You just can't trust those dynamic | |
languages. | |
=end pod | |
constant fibonacci = 0, 1, *+* ... *; | |
say fibonacci[^10]; # (0 1 1 2 3 5 8 13 21 34) | |
say fibonacci.VAR.^name; # Seq | |
=begin pod | |
But then a sequence isn't really constant. It's values are calculated at | |
runtime, for as many values we ask for. | |
=end pod | |
try { | |
fibonacci[11] = 0; | |
CATCH { when X::Assignment::RO { say .Str } } | |
} | |
=begin pod | |
If we want to take advantage of type checks, we have to make sure a call to | |
FETCH does return a default value of the right type. That's easy to do with a | |
type capture. If we obmit the type in C<STORE> we can still cheat to our | |
heart's content. If we don't want to cheat, we could use C<::T> in C<STORE>'s | |
signature. | |
=end pod | |
sub typed(::T $type = Any){ # you may not want to default to any, here we do | |
my T $c-typed-value; | |
my $c-typed-value-type-string = $c-typed-value.WHAT.perl; | |
return Proxy.new( | |
FETCH => method () { $c-typed-value }, | |
STORE => method ($new-value) { | |
$c-typed-value = $new-value ~~ Str | |
?? ( $new-value.comb>>.ord.sum / $new-value.chars )."$c-typed-value-type-string"() | |
!! $new-value; | |
} | |
); | |
} | |
my Int $typed-container := typed(Int); | |
say $typed-container = 11; | |
$typed-container = 'FOO'; | |
say $typed-container; | |
=begin pod | |
If we do fancy container magic, we have to implement readonlyness by hand. | |
=end pod | |
my $constant-variable := Proxy.new( | |
FETCH => { q{Merry 1.0!} }, | |
STORE => -> $, $ { die X::Assignment::RO.new(typename => 'none really') } # line 219 | |
); | |
say $constant-variable; | |
$constant-variable = 'The Grudge stole Christmas!'; # this will die in line 219 | |
say $constant-variable; | |
=begin pod | |
Binding is a powerful tool to expose the dynamic nature of Perl 6 and allows us | |
to take advantage of that nature. No matter if you bind or not I wish you a | |
Merry 1.0 and a happy new year! | |
=end pod |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment