-
Notifications
You must be signed in to change notification settings - Fork 292
/
js-nutshell.rakudoc
938 lines (724 loc) · 27.6 KB
/
js-nutshell.rakudoc
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
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
=begin pod :kind("Language") :subkind("Language") :category("migration")
=TITLE JavaScript (Node.js) to Raku - nutshell
=SUBTITLE Learning Raku from Node.js, in a nutshell
This page attempts to provide a way for users experienced in Node.js to learn
Raku. 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 Raku; 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 Raku:
=begin code
say('Hello, world!');
say 'Hello, world!';
=end code
Parentheses are optional for function calls in Raku. While semicolons are,
for the most part, optional in Node.js, they are mandatory for expressions in
Raku.
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 Raku:
=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 Raku 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
// No equivalent to Raku dynamic variables exists.
global.foo = 1; // Globally scoped
foo = 1; // Ditto, but implicit; forbidden in strict mode
=end code
In Raku there is no equivalent to C<var>. An important note to make is that
there is no variable hoisting in Raku; 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.
In addition to regular variables, in Raku there are what is known as dynamic
variables. Dynamic variables are looked up using the caller's scope, rather
than the outer scope. This is what the equivalent variable declarations look
like in Raku:
=for code
my $foo = 1; # Lexically scoped
our $foo = 1; # Package scoped
=for code
my constant foo = 1; # Lexically scoped; constant
=for code
constant foo = 1; # Package scoped; constant
=for code
my $*foo = 1; # Dynamic variable; lexically scoped
our $*foo = 1; # Dynamic variable; package scoped
=for code
GLOBAL::<$foo> := 1; # Globally scoped
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 use C<const>.
You may have noticed the C<$> and C<$*> symbols placed before variable names.
These are known as sigils and twigils respectively, and define what container
the variable has. Refer to the documentation on
L<variables|/language/variables> for more information on sigils, twigils, and
containers.
Variables in Node.js can have the same name as others from outer scopes without
conflicting (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); // OUTPUT: 2
console.log(foo); // OUTPUT: 1
=end code
Raku also allows this:
=begin code
my $foo = 1;
sub log-dupe {
my $foo = 2;
say $foo;
}
log-dupe; # OUTPUT: 2
say $foo; # OUTPUT: 1
=end code
=head2 Operators
=head3 Assignment
The C<=> operator works the same across both languages.
The C<:=> operator in Raku 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; # OUTPUT: {}
say %bound; # OUTPUT: {foo => bar}
%bound := %unbound;
say %bound; # OUTPUT: {}
=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); // OUTPUT: true
console.log('1' == 1); // OUTPUT: true
console.log([] == 0); // OUTPUT: true
=end code
Similarly, in Raku, both operands are cast to Numeric before comparison if
they don't share the same type:
=begin code
say 1 == 1; # OUTPUT: True
say '1' == 1; # OUTPUT: True
say [1,2,3] == 3; # OUTPUT: True, since the array has three elements
=end code
The inverse of C<==> is C<!=>.
Raku 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'; # OUTPUT: True
say 1 eq '1'; # OUTPUT: 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); // OUTPUT: true
console.log('1' === 1); // OUTPUT: false
console.log({} === {}); // OUTPUT: false
let obj = {};
let obj2 = obj;
console.log(obj === obj2); // OUTPUT: true;
=end code
In Raku, 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; # OUTPUT: «True»
say '1' === 1; # OUTPUT: «False»
say 'ayy lmao' === 'ayy lmao'; # OUTPUT: «True»
say {} === {}; # OUTPUT: «False»
my \hash = {};
my %hash = hash;
say hash === %hash; # OUTPUT: False
=end code
In the last case it's the same object, but containers are different, which is
why it returns False.
The inverse of C<===> is C<!==>.
This is where Raku'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}; # OUTPUT: True
my \hash = {};
my %hash := hash;
say hash eqv %hash; # OUTPUT: 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; # OUTPUT: True
=end code
=head3 Smartmatching
Raku 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 'ayy lmao' ~~ Str; # OUTPUT: True
my %hash = a => 1;
say 'a' ~~ %hash; # OUTPUT: True
my $str = 'abc';
$str ~~ s/abc/def/; # Mutates $str, like foo.replace('abc', 'def')
say $str; # OUTPUT: def
=end code
While we are talking about C<instanceof>, the equivalent to the C<constructor>
property on Node.js objects in Raku is the C<WHAT> attribute:
=begin code :lang<javascript>
console.log('foo'.constructor); // OUTPUT: String
=end code
=begin code :ok-test<WHAT>
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); // OUTPUT: 3
console.log([] + {}); // OUTPUT: [object Object]
console.log({} + []); // OUTPUT: 0
=end code
In Raku, again, they are converted to a Numeric type, as before:
=begin code
say 1 + 2; # OUTPUT: 3
say [] + {}; # OUTPUT: 0
say {} + [1,2,3]; # OUTPUT: 3
=end code
In addition, Raku 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; # OUTPUT: 1
say 4 %% 3; # OUTPUT: False
say 6 %% 3; # OUTPUT: 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); // OUTPUT: 2
console.log(1 >> 1); // OUTPUT: 0
console.log(1 >>> 1); // OUTPUT: 0
console.log(1 & 1); // OUTPUT: 1
console.log(0 | 1); // OUTPUT: 1
console.log(1 ^ 1); // OUTPUT: 0
console.log(~1); // OUTPUT: -2
=end code
In Raku, there is no equivalent to C«>>>». All bitwise operators are
prefixed with C<+>, however bitwise negation uses C<+^> instead of C<~>:
=begin code
say 1 +< 1; # OUTPUT: 2
say 1 +> 1; # OUTPUT: 0
# No equivalent for >>>
say 1 +& 1; # OUTPUT: 1
say 0 +| 1; # OUTPUT: 1
say 1 +^ 1; # OUTPUT: 0
say +^1; # OUTPUT: -2
=end code
=head3 Checking for definedness
Javascript includes a nullish coalescing operator, C<??>, which progresses
only if null or undefined:
=for code :lang<javascript>
undefined || null || 0 || 1 ; // => 1
undefined ?? null ?? 0 ?? 1 ; // => 0
This is very similar to Raku L<C<//>|/routine/$SOLIDUS$SOLIDUS> operator:
=for code
Any || Nil || 0 || 1 ; # => 1
Any // Nil // 0 // 1 ; # => 0
=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. Raku allows custom operators and
operator overloading natively! Since all operators are subroutines, you can
define your own like so:
=begin code
# "distance operator": the distance of two numbers is the absolute value
# of their difference
multi sub infix:<|-|>($a, $b) is equiv(&infix:<->) { abs $a - $b }
say -1 |-| 3; # OUTPUT: 4
=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 Raku, 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 && $dice-roll != 16;
=end code
Raku 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, Raku 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 Raku, 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 smartmatched (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
Raku 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 Raku. Raku 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 Raku. Likewise with
C<while>, C<repeat/until> loops also exist and loop until the given condition
is false.
To write infinite loops in Raku, 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 Raku, 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
=head3 Creating types
In JavaScript, types are created by making a class (or a constructor in ES5
and earlier). If you've used TypeScript, you can define a type as a subset of
other types like so:
=begin code :lang<typescript>
type ID = string | number;
=end code
In Raku, classes, roles, subsets, and enums are considered types. Creating
classes and roles will be discussed in
L<the OOP section of this article|#Object-oriented_programming>. Creating an ID
subset can be done like so:
=begin code
subset ID where Str | Int;
=end code
See the documentation on L<subset|/language/typesystem#subset> and
L<Junction|/type/Junction> for more information.
TypeScript enums may have numbers or strings as their values. Defining the
values is optional; by default, the value of the first key is 0, the next key,
1, the next, 2, etc. For example, here is an enum that defines directions for
extended ASCII arrow symbols (perhaps for a TUI game):
=begin code :lang<typescript>
enum Direction (
UP = '↑',
DOWN = '↓',
LEFT = '←',
RIGHT = '→'
);
=end code
Enums in Raku may have any type as their keys' values. Enum keys (and
optionally, values) can be defined by writing C<enum>, followed by the name
of the enum, then the list of keys (and optionally, values), which can be done
using L«< >|/language/quoting#Word_quoting:_<_>»,
L<« »|/language/quoting#Word_quoting_with_interpolation_and_quote_protection:_«_»>,
or L<( )|/language/operators#term_(_)>. C<( )> must be used if you want to
define values for the enum's keys. Here is the Direction enum as written in
Raku:
=begin code
enum Direction (
UP => '↑',
DOWN => '↓',
LEFT => '←',
RIGHT => '→'
);
=end code
See the documentation on L<enum|/language/typesystem#enum> for more information.
=head3 Using types
In TypeScript, you can define the type of variables. Attempting to assign a
value that doesn't match the type of the variable will make the transpiler
error out. This is done like so:
=begin code :lang<typescript>
enum Name (Phoebe, Daniel, Joe);
let name: string = 'Phoebe';
name = Phoebe; # Causes tsc to error out
let hobbies: [string] = ['origami', 'playing instruments', 'programming'];
let todo: Map<string, boolean> = new Map([
['clean the bathroom', false],
['walk the dog', true],
['wash the dishes', true]
]);
let doJob: (job: string) => boolean = function (job: string): boolean {
todo.set(job, true);
return true;
};
=end code
In Raku, variables can be typed by placing the type between the declarator
(C<my>, C<our>, etc.) and the variable name. Assigning a value that doesn't
match the variable's type will throw either a compile-time or runtime error,
depending on how the value is evaluated:
=begin code :skip-test<compile-time error>
enum Name <Phoebe Daniel Joe>;
my Str $name = 'Phoebe';
$name = Phoebe; # Throws a compile-time error
# The type here defines the type of the elements of the array.
my Str @hobbies = ['origami', 'playing instruments', 'programming'];
# The type between the declarator and variable defines the type of the values
# of the hash.
# The type in the curly braces defines the type of the keys of the hash.
my Bool %todo{Str} = (
'clean the bathroom' => False,
'walk the dog' => True,
'wash the dishes' => True
);
# The type here defines the return value of the routine.
my Bool &do-job = sub (Str $job --> Bool) {
%todo{$job} = True;
};
=end code
=head3 Comparing JavaScript and Raku types
Here is a table of some JavaScript types and their equivalents in Raku:
=begin table
JavaScript | Raku
=============================
Object | Mu, Any, Hash
Array | List, Array, Seq
String | Str
Number | Int, Num, Rat
Boolean | Bool
Map | Map, Hash
Set | Set, SetHash
=end table
C<Object> is both a superclass of all types in JavaScript and a way to create
a hash. In Raku, L<Mu|/type/Mu> is a superclass of all types, though usually
you want to use L<Any|/type/Any> instead, which is a subclass of C<Mu> but also
a superclass of nearly every type, with L<Junction|/type/Junction> being an
exception. When using C<Object> as a hash, L<Hash|/type/Hash> is what you want
to use. One key difference between C<Object> and C<Hash> is that C<Object>
preserves the order of its keys; C<Hash> does not by default.
There are three types equivalent to C<Array>. L<Array|/type/Array> is most
similar to C<Array>, since it acts as a mutable array. L<List|/type/List> is
similar to C<Array>, but is immutable. L<Seq|/type/Seq> is used to create lazy
arrays.
C<String> and L<Str|/type/Str> are for the most part used identically.
There are several different types in Raku equivalent to C<Number>, but the
three you'll most commonly see are L<Int|/type/Int>, L<Num|/type/Num>, and
L<Rat|/type/Rat>. C<Int> represents an integer. C<Num> represents a
floating-point number, making it the most similar to C<Number>. C<Rat>
represents a fraction of two numbers, and is used when C<Num> cannot provide
precise enough values.
C<Boolean> and L<Bool|/type/Bool> are for the most part used identically.
C<Map> has both a mutable and an immutable equivalent in Raku.
L<Map|/type/Map> is the immutable one, and L<Hash|/type/Hash> is the mutable
one. Don't get them mixed up! Like C<Map> in JavaScript, C<Map> and C<Hash> can
have any type of key or value, not just strings for keys.
Like C<Map>, C<Set> also has both a mutable and an immutable equivalent in Raku.
L<Set|/type/Set> is the immutable one, and L<SetHash|/type/SetHash> is the
mutable one.
=head2 Functions
=comment TODO
# TBD
=head1 Object-oriented programming
=comment TODO
# TBD
=head1 Asynchronous programming
=comment TODO
# TBD
=head1 The networking API
=head2 Net
In Raku, 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
Raku doesn't natively support HTTP/HTTPS. However, CPAN packages such as
L<Cro|https://cro.services/> help fill the gap.
=head2 DNS
Raku 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