-
Notifications
You must be signed in to change notification settings - Fork 292
/
js-nutshell.pod6
749 lines (577 loc) · 20.9 KB
/
js-nutshell.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
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
=begin pod :tag<convert>
=TITLE Javascript (Node.js) to Perl 6 - nutshell
=SUBTITLE Learning Perl 6 from Node.js, in a nutshell
This page attempts to provide a way for users experienced in Node.js to learn
Perl 6. Features shared between the two languages will be explained here, as
well as major differences in syntax and features.
This is not a tutorial for learning Perl 6; this is a reference for users who
are already at an intermediate to advanced skill level with Node.js.
=head1 Basic syntax
=head2 "Hello, world!"
Let's start with the typical first program when learning new languages. In
Node.js, a hello world program would be written like this:
=begin code :lang<javascript>
console.log('Hello, world!');
=end code
Here are a couple ways to write this in the same way in Perl 6:
=begin code
say('Hello, world!');
say 'Hello, world!';
=end code
Parentheses are optional for function calls in Perl 6. While semicolons are,
for the most part, optional in Node.js, they are mandatory for expressions in
Perl 6.
Now that we've greeted the world, let's greet our good friend, Joe. We'll
start with Node.js again:
=begin code :lang<javascript>
let name = 'Joe';
console.log('What\'s up,' + name + '?');
console.log(`What's up, {name}?`);
console.log("What's up, ", name, "?");
=end code
Since he didn't hear us, let's greet him again, this time in Perl 6:
=begin code
my $name = 'Joe';
say 'What\'s up, ' ~ $name ~ '?';
say "What's up, $name?";
say "What's up, ", $name, "?";
=end code
Here, there are only a couple differences: most variables in Perl 6 have what
are called sigils, which are what the C<$> in front of its name is, and string
concatenation uses the C<~> operator instead of C<+>. What the two languages
share in common here is support for string interpolation.
Now that the basic examples are out of the way, let's explain the similarities
between the two languages in greater detail.
=head2 Variables
Variables in Node.js can be defined like this;
=begin code :lang<javascript>
var foo = 1; // Lexically scoped with functions and modules
let foo = 1; // Lexically scoped with blocks
const foo = 1; // Lexically scoped with blocks; constant
global.foo = 1; // Dynamically scoped; global
foo = 1; // Ditto, but implicit; forbidden in strict mode
=end code
In Perl 6 there is no equivalent to C<var>. An important note to make is that
there is no variable hoisting in Perl 6; variables are defined and assigned
at the line they're on, not defined at the top of its scope and later assigned
at that line.
This is how the equivalent types of variables are defined in Perl 6:
=begin code
my $foo = 1; # Lexically scoped with blocks
our $foo = 1; # Lexically scoped with blocks and modules
constant foo = 1; # Lexically scoped with blocks and modules; constant
my $*foo = 1; # Dynamically scoped with blocks
OUR::<$foo> = 1; # Dynamically scoped with blocks and modules
GLOBAL::<$foo> = 1; # Dynamically scoped; global
=end code
Use C<my> where you'd use C<let>, C<our> for variables you'd define in the
outermost scope needed, and C<constant> where you'd uses C<const>.
Dynamically scoped variables are not referred to in the same way as lexically
scoped ones like they are in Node.js. User-defined ones use either a C<$*>,
C<@*>, C<%*>, or C<&*> twigil. Refer to the documentation on
L<variables|/language/variables> for more information on sigils, twigils, and
variable containers.
Variables in Node.js can override others from outer scopes with the same name
(though linters will usually complain about it depending on how they're
configured):
=begin code :lang<javascript>
let foo = 1;
function logDupe() {
let foo = 2;
console.log(foo);
}
logDupe(2); // 2
console.log(foo); // 1
=end code
Perl 6 also allows this:
=begin code
my $foo = 1;
sub log-dupe {
my $foo = 2;
say $foo;
}
log-dupe; # 2
say $foo; # 1
=end code
=head2 Operators
=head3 Assignment
The C<=> operator works the same across both languages.
The C<:=> operator in Perl 6 binds a value to a variable. Binding a variable
to another variable gives them the same value and container, meaning mutating
attributes of one will mutate the other's as well. Bound variables cannot be
reassigned with C<=> or mutated with C<++>, C<-->, etc. but they can be bound
to another value again:
=begin code
my %map; # This is a hash, roughly equivalent to a JS object or map
my %unbound = %map;
my %bound := %map;
%map<foo> = 'bar';
say %unbound; # {}
say %bound; # {foo => bar}
%bound := %unbound;
say %bound; # {}
=end code
=head3 Equality
Node.js has two equality operators: C<==> and C<===>.
C<==> is the loose equality operator. When comparing operands with the same
type, it will return true if both operands are equal. However, if the
operands are different types, they are both cast to their primitives before
being compared, meaning these will return true:
=begin code :lang<javascript>
console.log(1 == 1); // true
console.log('1' == 1); // true
console.log([] == 0); // true
=end code
Similarly, in Perl 6, both operands are cast to Numeric before comparison if
they don't share the same type:
=begin code
say 1 == 1; # True
say '1' == 1; # True
say [1,2,3] == 3; # True, since the array has three elements
=end code
The inverse of C<==> is C<!=>.
Perl 6 has another operator similar to C<==>: C<eq>. Instead of casting operands
to Numeric if they're different types, C<eq> will cast them to strings:
=begin code
say '1' eq '1'; # True
say 1 eq '1'; # True
=end code
The inverse of C<eq> is C<ne> or C<!eq>.
C<===> is the strict equality operator. This returns true if both operands are
the same value. When comparing objects, this will I<only> return true if they
are the exact same object:
=begin code :lang<javascript>
console.log(1 === 1); // true
console.log('1' === 1); // false
console.log({} === {}); // false
let obj = {};
let obj2 = obj;
console.log(obj === obj2); // true;
=end code
In Perl 6, the operator behaves the same, with one exception: two objects that
have the same value, but different containers, will return false:
=begin code
say 1 === 1; # True
say '1' === 1; # True
say {} === {}; # False
my \hash = {};
my %hash := hash;
say hash === %hash; # False
=end code
The inverse of C<===> is C<!==>.
This is where Perl 6's other equality operators are useful. If the values have
different containers, the C<eqv> operator can be used. This operator can be also
be used to check for deep equality, which you would normally need to use a
library for in Node.js:
=begin code
say {a => 1} eqv {a => 1}; # True;
my \hash = {};
my %hash := hash;
say hash eqv %hash; # True
=end code
In the case you need to check if two variables have the same container and
value, use the C<=:=> operator.
=begin code
my @arr = [1,2,3];
my @arr2 := @arr; # Bound variables keep the container of the other variable
say @arr =:= @arr2; # True
=end code
=head3 Smartmatching
Perl 6 has one last operator for comparing values, but it is not exactly an
equality operator. This is C<~~>, the smartmatch operator. This has several
uses: it can be used like C<instanceof> in Node.js, to match a regex, and to
check if a value is a key in a hash, bag, set, or map:
=begin code
say 'foo' ~~ Str; # True
my %hash = a => 1;
say 'a' ~~ %hash; # True
my $str = 'abc';
$str ~~ s/abc/def/; # Mutates $str, like foo.replace('abc', 'def')
say $str; # def
=end code
While we are talking about C<instanceof>, the equivalent to the C<constructor>
property on Node.js objects in Perl 6 is the C<WHAT> attribute:
=begin code :lang<javascript>
console.log('foo'.constructor); // OUTPUT: String
=end code
=begin code
say 'foo'.WHAT; # OUTPUT: Str
=end code
=head3 Numeric
Node.js has C<+>, C<->, C</>, C<*>, C<%>, and (in ES6) C<**> as numeric
operators. When the operands are different types, similarly to the equality
operators, are cast to their primitives before following through with the
operation, making this possible:
=begin code :lang<javascript>
console.log(1 + 2); // 3
console.log([] + {}); // [object Object]
console.log({} + []); // 0
=end code
In Perl 6, again, they are converted to a Numeric type, as before:
=begin code
say 1 + 2; # 3
say [] + {}; # 0
say {} + [1,2,3]; # 3
=end code
In addition, Perl 6 has C<div> and C<%%>. C<div> behaves like C<int> division in
C, while C<%%> checks if one number is cleanly divisible by another or not:
=begin code
say 4 div 3; # 1
say 4 %% 3; # False
say 6 %% 3; # True
=end code
=head3 Bitwise
Node.js has C<&>, C<|>, C<^>, C<~>, C«<<», C«>>», C«>>>», and C<~> for bitwise
operators:
=begin code :lang<javascript>
console.log(1 << 1); // 2
console.log(1 >> 1); // 0
console.log(1 >>> 1); // 0
console.log(1 & 1); // 1
console.log(0 | 1); // 1
console.log(1 ^ 1); // 0
console.log(~1); // -2
=end code
In Perl 6, there is no equivalent to C«>>>». All bitwise operators are
prefixed with C<+>, however two's complement uses C<+^> instead of C<~>:
=begin code
say 1 +< 1; # 2
say 1 +> 1; # 0
# No equivalent for >>>
say 1 +& 1; # 1
say 0 +| 1; # 1
say 1 +^ 1; # 0
say +^1; # -2
=end code
=head3 Custom operators and operator overloading
Node.js does not allow operator overloading without having to use a Makefile or
build Node.js with a custom version of V8. Perl 6 allows custom operators and
operator overloading natively! Since all operators are subroutines, you can
define your own like so:
=begin code
multi sub infix:<||=>($a, $b) is equiv(&infix:<+=>) { $a || $b }
my $foo = 0;
$foo ||= 1;
say $foo; # OUTPUT: 1
=end code
Operators can be defined as C<prefix>, C<infix>, or C<postfix>. The
C<is tighter>, C<is equiv>, and C<is looser> traits optionally define the
operator's precedence. In this case, C<||=> has the same precedence as C<+=>.
Note how C<multi> is used when declaring the operator subroutines. This allows
multiple subroutines with the same name to be declared while also having
different signatures. This will be explained in greater detail in the
L<Functions|#Functions> section. For now, all we need to know is that it allows
us to override any native operator we want:
=begin code
# Using the `is default` trait here forces this subroutine to be chosen first,
# so long as the signature of the subroutine matches.
multi sub prefix:<++>($a) is default { $a - 1 }
my $foo = 1;
say ++$foo; # OUTPUT: 0
=end code
=head2 Control flow
=head3 if/else
You should be familiar with how C<if>/C<else> looks in JavaScript:
=begin code :lang<javascript>
let diceRoll = Math.ceil(Math.random() * 6) + Math.ceil(Math.random() * 6);
if (diceRoll === 2) {
console.log('Snake eyes!');
} else if (diceRoll === 16) {
console.log('Boxcars!');
} else {
console.log(`Rolled ${diceRoll}.`);
}
=end code
In Perl 6, C<if>/C<else> works largely the same, with a few key differences.
One, parentheses are not required. Two, C<else if> is written as C<elsif>.
Three, the if clause may be written I<after> a statement:
=begin code
my Int $dice-roll = ceiling rand * 12 + ceiling rand * 12;
if $dice-roll == 2 {
say 'Snake eyes!';
} elsif $dice-roll == 16 {
say 'Boxcars!';
} else {
say "Rolled $dice-roll.";
}
=end code
Alternatively, though less efficient, this could be written to use C<if> after
statements:
=begin code
my Int $dice-roll = ceiling rand * 12 + ceiling rand * 12;
say 'Snake eyes!' if $dice-roll == 2;
say 'Boxcars!' if $dice-roll == 16;
say "Rolled $dice-roll." if $dice-roll !~~ 2 | 16;
=end code
Perl 6 also has C<when>, which is like C<if>, but if the condition given is
true, no code past the C<when> block within the block it's in is executed:
=begin code
{
when True {
say 'In when block!'; # OUTPUT: In when block!
}
say 'This will never be output!';
}
=end code
Additionally, Perl 6 has C<with>, C<orwith>, and C<without>, which are like
C<if>, C<else if>, and C<else> respectively, but instead of checking whether
their condition is true, they check if it's defined.
=head3 switch
Switch statements are a way of checking for equality between a given value and
a list of values and run some code if one matches. C<case> statements define
each value to compare to. C<default>, if included, acts as a fallback for when
the given value matches no cases. After matching a case, C<break> is typically
used to prevent the code from the cases that follow the one matched from being
executed, though rarely this is intentionally omitted.
=begin code :lang<javascript>
const ranklist = [2, 3, 4, 5, 6, 7, 8, 9, 'Jack', 'Queen', 'King', 'Ace'];
const ranks = Array.from(Array(3), () => ranklist[Math.floor(Math.random() * ranks.length)]);
let score = 0;
for (let rank of ranks) {
switch (rank) {
case 'Jack':
case 'Queen':
case 'King':
score += 10;
break;
case 'Ace';
score += (score <= 11) ? 10 : 1;
break;
default:
score += rank;
break;
}
}
=end code
In Perl 6, C<given> can be used like switch statements. There is no equivalent
to C<break> since C<when> blocks are most commonly used like C<case>
statements. One major difference between C<switch> and C<given> is that a value
passed to a C<switch> statement will only match cases that are exactly equal to
the value; C<given> values are smart matched (C<~~>) against the C<when> values.
=begin code
my @ranklist = [2, 3, 4, 5, 6, 7, 8, 9, 'Jack', 'Queen', 'King', 'Ace'];
my @ranks = @ranklist.pick: 3;
my Int $score = 0;
for @ranks -> $rank {
# The when blocks implicitly return the last statement they contain.
$score += do given $rank {
when 'Jack' | 'Queen' | 'King' { 10 }
when 'Ace' { $score <= 11 ?? 10 !! 1 }
default { $_ }
};
}
=end code
If there are multiple C<when> blocks that match the value passed to C<given>
and you wish to run more than one of them, use C<proceed>. C<succeed> may be
used to exit both the C<when> block it's in and the given block, preventing any
following statements from being executed:
=begin code
given Int {
when Int { say 'Int is Int'; proceed }
when Numeric { say 'Int is Numeric'; proceed }
when Any { say 'Int is Any'; succeed }
when Mu { say 'Int is Mu' } # Won't output
}
# OUTPUT:
# Int is Int
# Int is Numeric
# Int is Any
=end code
=head3 for, while, and do/while
There are three different types of for loops in JavaScript:
=begin code :lang<javascript>
// C-style for loops
const letters = {};
for (let ord = 0x61; ord <= 0x7A; ord++) {
let letter = String.fromCharCode(ord);
letters[letter] = letter.toUpperCase();
}
// for..in loops (typically used on objects)
for (let letter in letters) {
console.log(letters[letter]);
# OUTPUT:
# A
# B
# C
# etc.
}
// for..of loops (typically used on arrays, maps, and sets)
for (let letter of Object.values(letters)) {
console.log(letter);
# OUTPUT:
# A
# B
# C
# etc.
}
=end code
Perl 6 C<for> loops most closely resemble C<for..of> loops, since they work on
anything as long as it's iterable. C-style loops are possible to write using
C<loop>, but this is discouraged since they're better written as C<for> loops
using ranges. Like C<if> statements, C<for> may follow a statement, with the
current iteration being accessible using the C<$_> variable (known as "it").
Methods on C<$_> may be called without specifying the variable:
=begin code
my Str %letters{Str};
%letters{$_} = .uc for 'a'..'z';
.say for %letters.values;
# OUTPUT:
# A
# B
# C
# etc.
=end code
C<while> loops work identically between JavaScript and Perl 6. Perl 6 also has
C<until> loops, where instead of iterating until the given condition is false,
they iterate until the condition is true.
C<do/while> loops are known as C<repeat/while> loops in Perl 6. Likewise with
C<while>, C<repeat/until> loops also exist and loop until the given condition
is false.
To write infinite loops in Perl 6, use C<loop> rather than C<for> or C<while>.
In JavaScript, C<continue> is used to skip to the next iteration in a loop, and
C<break> is used to exit a loop early:
=begin code :lang<javascript>
let primes = new Set();
let i = 2;
do {
let isPrime = true;
for (let prime of primes) {
if (i % prime == 0) {
isPrime = false;
break;
}
}
if (!isPrime) continue;
primes.add(i);
} while (++i < 20);
console.log(primes); # OUTPUT: Set { 2, 3, 5, 7, 11, 13, 17, 19 }
=end code
In Perl 6, these are known as C<next> and C<last> respectively. There is also
C<redo>, which repeats the current iteration without evaluating the loop's
condition again.
C<next>/C<redo>/C<last> statements may be followed by a label defined before an
outer loop to make the statement work on the loop the label refers to, rather
than the loop the statement is in:
=begin code
my %primes is SetHash;
my Int $i = 2;
OUTSIDE:
repeat {
next OUTSIDE if $i %% $_ for %primes.keys;
%primes{$i}++;
} while ++$i < 20;
say %primes; # OUTPUT: SetHash(11 13 17 19 2 3 5 7)
=end code
=head3 do
C<do> is not currently a feature in JavaScript, however a proposal has been made
to L<add it to ECMAScript|https://github.com/tc39/proposal-do-expressions>.
C<do> expressions evaluate a block and return the result:
=begin code
constant VERSION = v2.0.0;
constant VERSION_NUMBER = do {
my @digits = VERSION.Str.comb(/\d+/);
:16(sprintf "%02x%02x%04x", |@digits)
};
say VERSION_NUMBER; # OUTPUT: 33554432
=end code
=head2 Types
=comment TODO
# TBD
=head2 Functions
=comment TODO
# TBD
=head1 Object-oriented programming
=comment TODO
# TBD
=head1 The networking API
=head2 Net
In Perl 6, there are two APIs for dealing with networking: C<IO::Socket::INET>
(for synchronous networking), and C<IO::Socket::Async> (for asynchronous
networking).
C<IO::Socket::INET> currently only supports TCP connections. Its API resembles
that of C's socket API. If you're familiar with that, then it won't take long
to understand how to use it. For example, here's an echo server that closes the
connection after receiving its first message:
=begin code
my IO::Socket::INET $server .= new:
:localhost<localhost>,
:localport<8000>,
:listen;
my IO::Socket::INET $client .= new: :host<localhost>, :port<8000>;
$client.print: 'Hello, world!';
my IO::Socket::INET $conn = $server.accept;
my Str $msg = $conn.recv;
say $msg; # OUTPUT: Hello, world!
$conn.print($msg);
say $client.recv; # OUTPUT: Hello, world!
$conn.close;
$client.close;
$server.close;
=end code
By default, C<IO::Socket::INET> connections are IPv4 only. To use IPv6 instead,
pass C<:family(PF_INET6)> when constructing a server or a client.
In contrast, C<IO::Socket::Async> supports both IPv4 and IPv6 without the need
to specify which family you wish to use. It also supports UDP sockets. Here's
how you would write the same echo server as above asynchronously (note that
C<Supply.tap> is multithreaded; if this is undesirable, use C<Supply.act>
instead:
=begin code
my $supply = IO::Socket::Async.listen('localhost', 8000);
my $server = $supply.tap(-> $conn {
$conn.Supply.tap(-> $data {
say $data; # OUTPUT: Hello, world!
await $conn.print: $data;
$conn.close;
})
});
my $client = await IO::Socket::Async.connect('localhost', 8000);
$client.Supply.tap(-> $data {
say $data; # OUTPUT: Hello, world!
$client.close;
$server.close;
});
await $client.print: 'Hello, world!';
=end code
The equivalent code in Node.js looks like this:
=begin code :lang<javascript>
const net = require('net');
const server = net.createServer(conn => {
conn.setEncoding('utf8');
conn.on('data', data => {
console.log(data); # OUTPUT: Hello, world!
conn.write(data);
conn.end();
});
}).listen(8000, 'localhost');
const client = net.createConnection(8000, 'localhost', () => {
client.setEncoding('utf8');
client.on('data', data => {
console.log(data); # OUTPUT: Hello, world!
client.end();
server.close();
});
client.write("Hello, world!");
});
=end code
=head2 HTTP/HTTPS
Perl 6 doesn't natively support HTTP/HTTPS. However, CPAN packages such as
L<Cro|https://cro.services/> help fill the gap.
=head2 DNS
Perl 6 does not currently support the majority of the features that Node.js's DNS
module implements. C<IO::Socket::INET> and C<IO::Socket::Async> can resolve
hostnames, but features like resolving DNS records and reverse IP lookups are
not implemented yet. There are some modules that are a work in progress,
such as L<Net::DNS::BIND::Manage|https://github.com/tbrowder/Net-DNS-BIND-Manage-Perl6/>,
that aim to improve DNS support.
=head2 Punycode
Punycode support is available through the L<Net::LibIDN|https://github.com/Kaiepi/p6-Net-LibIDN>,
L<Net::LibIDN2|https://github.com/Kaiepi/p6-Net-LibIDN2>, and
L<IDNA::Punycode|https://github.com/FROGGS/p6-IDNA-Punycode> modules on CPAN.
=head1 The filesystem API
=comment TODO
# TBD
=head1 Modules and packages
=comment TODO
# TBD
=end pod
# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6