Day 2 – 2 bind or !2 bind

Is a question one may want to answer while programming in Perl 6. Let me explain with the help of a Proxy and a script.

11 sub BOS() {
12 	my Str $c-value; # closure variable
13 	return Proxy.new(
14 		FETCH => method () { $c-value },
15 		STORE => method ($new-value) { 
16 			die $?LINE ~ ' Semper invicta!' if $new-value.tc ~~ any <Supermutant Ghoul Synth>; 
17 			$c-value = $new-value;
18 		}
19 	);
20 }
21 
22 # Let's prefix &say with the line number it's called from
23 my sub say(**@args is raw) { 
24 	print callframe(1).line; 
25 	print ' ' ~ .Str ~ $*OUT.nl-out for @args
26 }
27 
28 my Str $who-container = BOS;
29 my Str \who-raw = BOS;
30 my Str $who-bound := BOS;

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.

43 $who-container = 'supermutant';
44 say $who-container;
45 
46 try {
47 
48 	who-raw = 'ghoul';
49 	say who-raw;
50 
51 	CATCH {
52 		when X::AdHoc { warn $_.Str }; # line 52
53 	}
54 }
55 
56 who-raw = 'Cait';
57 say who-raw;
58 
59 try {
60 
61 	$who-bound = 'synth';
62 	say $who-bound;
63 
64 	CATCH {
65 		when X::AdHoc { warn $_.Str }; # line 65
66 	}
67 }
68 
69 $who-bound = 'Dogmeat';
70 say $who-bound;

By handling X::AdHoc we can turn a fatal die into a harmless warn. For $who-container we don’t need to do that. Let’s see how they differ.

79 say $who-container.VAR.^name; # Scalar
80 say who-raw.VAR.^name; # Proxy
81 say $who-bound.VAR.^name; # Proxy

With VAR we get access to the typeobject of the container we introduce with my or our. Binding allows 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.

92 say so BOS() ~~ Proxy; # False
93 say so BOS() ~~ Str; # True
94 
95 say so BOS().VAR ~~ Proxy; # True
96 say so BOS().VAR ~~ Str; # False

The type of the returned value is the type of $c-value. The container type is Proxy. We can use that to decide at runtime if we need to bind.

105 my Str $foo;
106 $foo = BOS if BOS().VAR ~~ Scalar;
107 $foo := BOS if BOS().VAR ~~ Proxy;
108 say $foo.VAR.^name; # Proxy

There is no way for a function to force binding on its 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 Proxy can help us to probe Rakudo a little.

120 my $c-zeta;
121 my $counter;
122 my $zeta := Proxy.new(FETCH => { $counter++; $c-zeta }, STORE => -> $, $new { $c-zeta = $new } );
123 
124 $zeta = 'how often will it be fetched?';
125 say $zeta;
126 say $counter; # 5

On Rakudo Beta 1 FETCH is called 5 times just to .say the value. Let’s see how far our probe will reach.

135 f($zeta, $zeta, $zeta);
136 
137 sub f($c, $c-rw is rw, \r){
138 	say $c.VAR.^name; # Scalar
139 	say $c-rw.VAR.^name; # Proxy
140 	say r.VAR.^name; # Proxy
141 }

The default for sub and method parameters is ro, what does what is said on the tin. Both is rw and a sigilless parameter are using binding.

150 constant lover = BOS();
151 
152 say lover.VAR.^name; # Proxy
153 
154 lover = 'Piper';
155 say lover; # Piper

If we declare a constant Perl 6 will also force a binding. Allowing sigillessness may give it away. 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.

166 constant fibonacci = 0, 1, *+* ... *;
167 
168 say fibonacci[^10]; # (0 1 1 2 3 5 8 13 21 34)
169 say fibonacci.VAR.^name; # Seq

But then a sequence isn’t really constant. It’s values are calculated at runtime, for as many values we ask for.

178 try {
179 	fibonacci[11] = 0;
180 	CATCH { when X::Assignment::RO { say .Str } }
181 }

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 omit the type in STORE we can still cheat to our heart’s content. If we don’t want to cheat, we could use ::T in STORE‘s signature.

193 sub typed(::T $type = Any){ # you may not want to default to Any, here we do
194 	my T $c-typed-value;
195 	my $c-typed-value-type-string = $c-typed-value.WHAT.perl;
196 	return Proxy.new(
197 		FETCH => method () { $c-typed-value },
198 		STORE => method ($new-value) { 
199 			$c-typed-value = $new-value ~~ Str 
200 				?? ( $new-value.comb>>.ord.sum / $new-value.chars )."$c-typed-value-type-string"()
201 				!! $new-value;
202 		}
203 	);
204 }
205 
206 my Int $typed-container := typed(Int);
207 say $typed-container = 11;
208 $typed-container = 'FOO';
209 say $typed-container;

If we do fancy container magic, we have to implement readonlyness by hand.

217 my $constant-variable := Proxy.new( 
218 	FETCH => { q{Merry 1.0!} }, 
219 	STORE => -> $, $ { die X::Assignment::RO.new(typename => 'none really') } # line 219 
220 );
221 
222 say $constant-variable;
223 
224 $constant-variable = 'The Grudge stole Christmas!'; # this will die in line 219 
225 say $constant-variable;

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!

3 thoughts on “Day 2 – 2 bind or !2 bind

  1. Interesting post. I assume.

    Unfortunately, without knowing about “binding” or what, e.g., “my Str \who-raw = BOS;” is supposed to do, I failed to understand large parts of the text.
    Some introductory words explaining these concepts, perhaps a sentence on “Proxy” being a “built in” class, etc. would have made this post much more comprehensible to me…

    Sorry for sounding grumpy; I know it´s all done in the authors’ spare time and I´m tremendously grateful for all the effort put in this advent calender…

  2. Seconding Mike. I’ve done a fair bit of reading of and about Perl 6, but this post is opaque to me. Perhaps a later post giving background on Proxy, binding, sigil-lessness, what we presumably expect from a container that is still a container (??), why we don’t have to try/CATCH around that container, what ‘we have to be careful with returned values’ specifically means in its context, what any of the statements in this post have to do with each other …

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s