-
Notifications
You must be signed in to change notification settings - Fork 292
/
traps.pod6
428 lines (292 loc) · 13.9 KB
/
traps.pod6
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
=begin pod
=TITLE Traps to avoid
=SUBTITLE Traps to avoid when getting started with Perl 6
When learning a programming language, possibly with the background of being
familiar with another programming language, there are always some things
that can surprise you and might cost valuable time in debugging and
discovery.
This document aims to show common misconceptions.
During the making of Perl 6 great pains were taken to get rid of warts in
the syntax. When you whack one wart, though, sometimes another pops up. So
a lot of time was spent finding the minimum number of warts or trying to put
them where they would rarely be seen. Because of this, Perl 6's warts are
in different places than you may expect them to be when coming from another
language.
=head1 Objects
=head2 Assigning to attributes
Newcomers often think that, because attributes with accessors are declared
as C<has $.x>, they can assign to C<$.x> inside the class. That's not the
case.
For example
use v6;
class Point {
has $.x;
has $.y;
method double {
$.x *= 2; # WRONG
$.y *= 2; # WRONG
self;
}
}
say Point.new(x => 1, y => -2).double.x
dies with
Cannot modify an immutable Int
in the first line marked with C<# WRONG>, because C<$.x>, short for C<$(
self.x )>, is a call to a read-only accessor.
The syntax C<has $.x> is short for something like C<has $!x; method x() {
$!x }>, so the actual attribute is called C<$!x>, and a read-only accessor
method is automatically generated.
Thus the correct way to write method C<double> is
method double {
$!x *= 2;
$!y *= 2;
self;
}
which operates on the attributes directly.
=head2 BUILD prevents automatic attribute initialization from constructor arguments
When you define your own C<BUILD> submethod, you must take care of
initializing all attributes yourself. For example
use v6;
class A {
has $.x;
has $.y;
submethod BUILD {
$!y = 18;
}
}
say A.new(x => 42).x; # Any
leaves C<$!x> uninitialized, because the custom C<BUILD> doesn't initialize
it.
One possible remedy is to explicitly initialize the attribute in C<BUILD>:
submethod BUILD(:$x) {
$!y = 18;
$!x := $x;
}
which can be shortened to:
submethod BUILD(:$!x) {
$!y = 18;
}
Another, more general approach is to leave C<BUILD> alone, and hook into the
C<BUILDALL> mechanism instead:
use v6;
class A {
has $.x;
has $.y;
method BUILDALL(|c) {
callsame;
$!y = 18;
self
}
}
say A.new(x => 42).x; # 42
(Note that C<BUILDALL> is a method, not a submethod. That's because by
default, there is only one such method per class hierarchy, whereas C<BUILD>
is explicitly called per class. Which also explains why you need the
C<nextsame> inside C<BUILDALL>, but not inside C<BUILD>).
=head1 Regexes
=head2 Whitespace in Regexes does not match literally
$ perl6 -e "say 'a b' ~~ /a b/"
False
Whitespace in regexes is, by default, considered an optional filler without
semantics, just like in the rest of the Perl 6 language.
Ways to match whitespace:
=item C<\s> to match any one whitespace, C<\s+> to match at least one
=item C<' '> (a blank in quotes) to match a single blank
=item C<\t>, C<\n> for specific whitespace (tab, newline)
=item C<\h>, C<\v> for horizontal, vertical whitespace
=item C<<.ws>>, a built-in rule for whitespace that oftentimes does what
you actually want it to do
=item with C<m:s/a b/> or C<m:sigspace/a b/>, the blank in the regexes
matches arbitrary whitespace
=head1 Captures
=head2 Containers versus values in a Capture
Beginners might expect a variable in a C<Capture> to supply its current
value when that C<Capture> is later used. For example:
$ perl6 -e 'my $a = 2; say join ",", ($a, ++$a)'
3,3
Here the C<Capture> contained the B<container> pointed to by C<$a> and the
B<value> of the result of the expression C<++$a>. Since the C<Capture> must
be reified before C<&say> can use it, the C<++$a> may happen before C<&say>
looks inside the container in C<$a> and so it may already be incremented.
Instead, use an expression that produces a value when you want a value.
$ perl6 -e 'my $a = 2; say join ",", (+$a, ++$a)'
2,3
=head1 Arrays
=head2 Referencing the last element of an array
In Perl 5 one could reference the last element of an array by asking for the
"-1th" element of the array, e.g.:
my @array = qw{victor alice bob charlie eve};
say @array[-1]; #-> eve
In Perl 6 it is not possible to use negative subscripts, however the same is
achieved by actually using a function, namely C<*-1>. Thus accessing the
last element of an array becomes:
my @array = qw{victor alice bob charlie eve};
say @array[*-1]; #-> eve
=head2 Typed Array parameters
Quite often new users will happen to write something like:
sub foo(Array @a) { ... }
...before they have gotten far enough in the documentation to realize that
this is asking for an Array of Arrays. To say that C<@a> should only accept
Arrays, use instead:
sub foo(@a where Array) { ... }
It is also common to expect this to work, when it does not:
sub bar(Int @a) { 42.say };
bar([1, 2, 3]); #-> expected Positional[Int] but got Array
The problem here is that [1, 2, 3] is not an C<Array[Int]>, it is a plain
old Array that just happens to have Ints in it. To get it to work,
the argument must also be an C<Array[Int]>.
my Int @b = 1, 2, 3;
bar(@b); #-> 42
bar(Array[Int].new(1, 2, 3));
This may seem inconvenient, but on the upside it moves the type-check
on what is assigned to C<@b> to where the assignment happens, rather
than requiring every element to be checked on every call.
=head1 Strings
=head2 Capitalizing a string
In Perl 5 one could capitalize a string by using the C<ucfirst> function
say ucfirst "alice"; #-> Alice
The C<ucfirst> function does not exist in Perl 6; one needs to use the
C<tc> method:
say "alice".tc; #-> Alice
which is equivalent to
say tc "alice"; #-> Alice
Here, C<tc> means "title case".
=head2 Quotes and interpolation
Interpolation in string literals can be too clever for your good.
"$foo<html></html>" # Perl 6 understands that as:
$foo<html> ~ '</html>'
"$foo(" ~ @args ~ ")" # Perl 6 understands that as:
$foo( ~ @args ~ ")" # and will produce very confusing error messages
You can avoid those problems by surrounding all variables with C<{}>, with or
without using C<Q:c> as your quoting construct.
my $a = 1;
say Q:c«{$a}()$b()»;
OUTPUT«1()$b()»
=head2 Strings are not iterable
There are methods that Str inherits from Any that work on iterables like lists. Iterators on Strings contain one element that is the whole string. To use C<sort>, C<reverse> and friends, you need to split the string into a list first.
say "cba".sort; # cba (what is wrong)
say "cba".comb.sort.join; # abc
=head1 Operators
Some operators commonly shared among other languages were repurposed in Perl 6 for other, more common, things:
=head2 Junctions
The C<^>, C<|>, and C<&> are I<not> bitwise operators, they create L<Junctions|/type/Junction>. The corresponding
bitwise operators in Perl 6 are: C<+^>, C<+|>, C<+&> for integers and C<?^>, C<?|>, C<?&> for booleans.
=head2 String Ranges/Sequences
In some languages, using strings as range end points, considers the entire string when figuring out what the next string
should be; loosely treating the strings as numbers in a large base. Here's Perl 5 version:
say join ", ", "az".."bc"
az, ba, bb, bc
Such a range in Perl 6 will produce a different result, where I<each letter> will be ranged to a corresponding letter in the
end point, producing more complex sequences:
say join ", ", "az".."bc"'
az, ay, ax, aw, av, au, at, as, ar, aq, ap, ao, an, am, al, ak, aj, ai, ah,
ag, af, ae, ad, ac, bz, by, bx, bw, bv, bu, bt, bs, br, bq, bp, bo, bn, bm,
bl, bk, bj, bi, bh, bg, bf, be, bd, bc
say join ", ", "r2".."t3"
r2, r3, s2, s3, t2, t3
To achieve simpler behaviour, similar to the Perl 5 example above, use a sequence operator that calls C<.succ> method on the
starting string:
say join ", ", ("az", *.succ ... "bc")
az, ba, bb, bc
=head1 Common Precedence Mistakes
=head2 Adverbs and Precedence
Adverbs do have a precedence that may not follow the order of operators that is displayed on your screen. If two operators of equal precedence are followed by an adverb it will pick the first operator it finds in the abstract syntax tree. Use parentheses to help Perl 6 understand what you mean or use operators with looser precedence.
my %x = a => 42;
say !%x<b>:exists; # dies with X::AdHoc
say %x<b>:!exists; # this works
say !(%x<b>:exists); # works too
say not %x<b>:exists; # works as well
say True unless %x<b>:exists; # avoid negation altogether
=head2 Ranges and Precedence
The loose precedence of C<..> can lead to some errors. It is usually best to parenthesize ranges when you want to operate on the entire range.
1..3.say # says "3" (and warns about useless "..")
(1..3).say # says "1..3"
=head2 Loose boolean operators
The precedence of C<and>, C<or>, etc. is looser then routine calls. This can
have surprising results for calls to routines that would be operators or
statements in other languages like C<return>, C<last> and many others.
sub f {
return True and False;
# this is actually
# (return True) and False;
}
say f; # OUTPUT«True»
=head1 Subroutine and method calls
Subroutine and method calls can be made using one of two forms:
foo(...); # function call form, where ... represent the required arguments
foo ...; # list op form, where ... represent the required arguments
The function call form can cause problems for the unwary when
whitespace is added after the function or method name and before the
opening parenthesis.
First we consider functions with zero or one parameter:
sub foo() { say 'no arg' }
sub bar($a) { say "one arg: $a" }
Then execute each with and without a space after the name:
foo(); # okay: no arg
foo (); # FAIL: Too many positionals passed; expected 0 arguments but got 1
bar($a); # okay: one arg: 1
bar ($a); # okay: one arg: 1
Now declare a function of two parameters:
sub foo($a, $b) { say "two args: $a, $b" }
Execute it with and without the space after the name:
foo($a, $b); # okay: two args: 1, 2
foo ($a, $b); # FAIL: Too few positionals passed; expected 2 arguments but got 1
The lesson is: "be careful with spaces following sub and method names
when using the function call format." As a general rule, good
practice might be to avoid the space after a function name when using
the function call format.
Note that there are clever ways to eliminate the error with the
function call format and the space, but that is bordering on hackery
and will not be mentioned here. For more information, consult
L<Functions|/language/functions#Functions>.
Finally, note that, currently, when declaring the functions whitespace
may be used between a function or method name and the parentheses
surrounding the parameter list without problems.
=head2 Named Parameters
Many built-in subroutines and method calls accept named parameters and your own
code may accept them as well, but be sure the arguments you pass when calling
your routines are actually named parameters:
sub foo($a, :$b) { ... }
foo(1, 'b' => 2); # FAIL: Too many positionals passed; expected 1 argument but got 2
What happened? That second argument is not a named parameter argument, but a
L<Pair|/type/Pair> passed as a positional argument. If you want a named
parameter it has to look like a name to Perl:
foo(1, b => 2); # okay
foo(1, :b(2)); # okay
foo(1, :b<it>); # okay
my $b = 2;
foo(1, :b($b)); # okay, but redundant
foo(1, :$b); # okay
# Or even...
my %arg = 'b' => 2;
foo(1, |%arg); # okay too
That last one may be confusing, but since it uses the C<|> prefix on a
L<Hash|/type/Hash>, it means "treat this hash as holding named arguments."
If you really do want to pass them as pairs you should use a L<List|/type/List>
or L<Capture|/type/Capture> instead:
my $list = ('b' => 2); # this is a List containing a single Pair
foo(|$arg, :$b); # okay: we passed the pair 'b' => 2 to the first argument
foo(1, |$arg); # FAIL: Too many positionals passed; expected 1 argument but got 2
my $cap = \('b' => 2); # a Capture with a single positional value
foo(|$cap, :$b); # okay: we passed the pair 'b' => 2 to the first argument
foo(1, |$cap); # FAIL: Too many positionals passed; expected 1 argument but got 2
A Capture is usually the best option for this as it works exactly like the usual
capturing of routine arguments during a regular call.
The nice thing about the distinction here is that it gives the developer the
option of passing pairs as either named or positional arguments, which can be
handy in various instances.
=head1 Exception Handling
=head2 Sunk C<Proc>
Some methods return a L<Proc> object. If it represents a failed process, C<Proc> itself
won't be exception-like, but B<sinking it> will cause an L<X::Proc::Unsuccessful>
exception to be thrown. That means this construct will throw, despite the C<try> in place:
try run("perl6", "-e", "exit 42");
say "still alive";
# OUTPUT: The spawned process exited unsuccessfully (exit code: 42)
This is because C<try> receives a C<Proc> and returns it, at which point it sinks and
throws. Explicitly sinking it inside the C<try> avoids the issue:
try sink run("perl6", "-e", "exit 42");
say "still alive";
# OUTPUT: still alive
=end pod
# vim: expandtab shiftwidth=4 ft=perl6