Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
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
You can’t perform that action at this time.