@@ -20,64 +20,81 @@ Haskell background.
20
20
21
21
In Haskell, you have type level programming and then value level programming.
22
22
23
+ = begin code :skip-test
23
24
plusTwo :: Integer -> Integer -- Types
24
25
plusTwo x = x + 2 -- Values
26
+ = end code
25
27
26
28
You do not mix types and values in Haskell like the below
27
29
30
+ = begin code :skip-test
28
31
plusTwo 2 -- This is valid
29
32
plusTwo Integer -- This is not valid
33
+ = end code
30
34
31
35
In Perl6, types (AKA type objects) live on the same level as values
32
36
37
+ = begin code
33
38
sub plus-two(Int $x --> Int) { $x + 2 }
34
39
35
- plus-two(2) # This is valid
36
- plus-two(Int) # This is valid
40
+ plus-two(2); # This is valid
41
+ plus-two(Int); # This is valid
42
+ = end code
37
43
38
44
I will illustrate this unique aspect of Perl6 with one more example:
39
45
46
+ = begin code
40
47
multi sub is-string(Str $ --> True) {}
41
48
multi sub is-string(Any $ --> False) {}
42
49
43
- is-string('hello') #True
44
- is-string(4) #False
50
+ is-string('hello'); #True
51
+ is-string(4); #False
52
+ = end code
45
53
46
54
= head2 Maybe
47
55
48
56
In Haskell, you have a Maybe type that allows you to forgo the worry of null types.
49
57
Let's say you have a hypothetical function that parses a String to an Integer:
50
58
59
+ = begin code :skip-test
51
60
parseInt :: String -> Maybe Integer
52
61
53
62
case parseInt myString of
54
63
Just x -> x
55
64
Nothing -> 0
65
+ = end code
56
66
57
67
In Perl6, since type objects coexist with regular objects, we have the concept of Defined
58
68
and Undefined objects. Plain type objects are undefined while instantiated objects are defined.
59
69
70
+ = begin code
60
71
sub parse-int(Str $s --> Int) { ... }
61
72
73
+ my $string = {...};
62
74
given parse-int($string) {
63
75
when Int:D { $_ }
64
76
when Int:U { 0 }
65
77
}
78
+ = end code
66
79
67
80
So in Perl6 we have type constraints that indicate the definedness of a type. These are
68
81
69
- Int:D # This is a defined Int.
70
- Int:U # This is an undefined Int, AKA a type object
71
- Int:_ # This is either defined or undefined.
82
+ = begin code
83
+ Int:D; # This is a defined Int.
84
+ Int:U; # This is an undefined Int, AKA a type object
85
+ Int:_; # This is either defined or undefined.
86
+ = end code
72
87
73
88
If we wanted to be explicit in the above example (probably a good idea), we could add
74
89
the C < :_ > constraint on the return type. This would let the user know that they should account
75
90
for both defined and undefined return values. We could also use other methods and constructs
76
91
that specifically test for definedness.
77
92
93
+ = begin code
78
94
sub parse-int(Str $s --> Int:_) { ... }
79
95
80
96
# One way to do it
97
+ my $string = {...};
81
98
given parse-int($string) {
82
99
when Int:D { $_ }
83
100
when Int:U { 0 }
@@ -93,17 +110,23 @@ that specifically test for definedness.
93
110
94
111
# With the defined-or operator
95
112
parse-int($string) // 0
113
+ = end code
96
114
97
115
The C < with > operator that you see above is like C < if > , except it explicitly tests for definedness and then
98
116
passes the result to the following block. Similarly, C < without > tests that the object is undefined and also
99
117
passes the result to the following block.
100
118
101
119
For more natural control flow with undefined and defined types, Perl6 introduces C < andthen > and C < orelse > .
102
120
121
+ = begin code
122
+ sub parse-int(Str $s --> Int:_) { ... }
123
+
124
+ my $string = {...};
103
125
my $result = parse-int($string) orelse 0;
104
126
105
127
sub hello() { say 'hi' }
106
128
hello() andthen say 'bye';
129
+ = end code
107
130
108
131
TODO: include a better example for andthen that makes sense. Maybe using promise objects?
109
132
@@ -117,32 +140,39 @@ object and return a new object, you can certainly do so.
117
140
118
141
Here is a Haskell code example:
119
142
143
+ = begin code :skip-test
120
144
data Point = Point x y
121
145
122
146
moveUp :: Point -> Point
123
147
moveUp (Point x y) = Point x (y + 1)
148
+ = end code
124
149
125
150
And an equivalent Perl6 example:
126
151
152
+ = begin code
127
153
class Point { has $.x; has $.y; }
128
154
129
155
sub move-up(Point $p --> Point) {
130
156
Point.new(x => $p.x, y => $p.y + 1)
131
157
}
158
+ = end code
132
159
133
160
The code I illustrated above is an example of a Product Type. If instead you'd like to
134
161
write a Sum Type, there is not an exact equivalent in Perl6. The closest thing would be
135
162
an Enum.
136
163
164
+ = begin code :skip-test
137
165
data Animal = Dog | Cat | Bird | Horse
138
166
139
167
testAnimal :: Animal -> String
140
168
testAnimal Dog = "Woof"
141
169
testAnimal Horse = "Neigh"
170
+ = end code
142
171
143
172
A Perl6 Enum does not fit the same exact use cases, but it can be used in putting
144
173
constraints on types.
145
174
175
+ = begin code
146
176
enum Animal < Dog Cat Bird Horse >;
147
177
148
178
proto sub test-animal( Animal ) {*}
@@ -151,25 +181,31 @@ constraints on types.
151
181
152
182
say test-animal Animal::Dog; # more explicit
153
183
say test-animal Horse;
184
+ = end code
154
185
155
186
= head2 Type Aliases and Subsets
156
187
157
188
In Haskell, you can alias an existing type to simply increase clarity of intent and re-use
158
189
existing types.
159
190
191
+ = begin code :skip-test
160
192
type Name = String
161
193
162
194
fullName :: Name -> Name -> Name
163
195
fullName first last = first ++ last
196
+ = end code
164
197
165
198
The equivalent in Perl6 is the following.
166
199
200
+ = begin code
167
201
my constant Name = Str;
168
202
169
203
sub full-name ( Name \first, Name \last --> Name ) { first ~ last }
204
+ = end code
170
205
171
206
It should be noted that in Perl6, one can also create a subset of an existing type.
172
207
208
+ = begin code :skip-test
173
209
subset Name of Str where *.chars < 20;
174
210
175
211
sub full-name(Name $first, Name $last) {
@@ -178,6 +214,7 @@ It should be noted that in Perl6, one can also create a subset of an existing ty
178
214
179
215
full-name("12345678901234567890111", "Smith") # This does not compile, as the first parameter
180
216
# doesn't fit the Name type
217
+ = end code
181
218
182
219
= head2 Typeclasses
183
220
@@ -193,26 +230,32 @@ explain how Perl6 roles compare to Haskell typeclasses
193
230
194
231
Haskell makes heavy use of pattern matching in function definitions.
195
232
233
+ = begin code :skip-test
196
234
greeting :: String -> String
197
235
greeting "" = "Hello, World!"
198
236
greeting "bub" = "Hey bub."
199
237
greeting name = "Hello, " ++ name ++ "!"
238
+ = end code
200
239
201
240
Perl6 does this as well! You just use the C < multi > keyword to signify that it is a multiple dispatch
202
241
function.
203
242
243
+ = begin code
204
244
proto greeting ( Str --> Str ) {*}
205
245
multi greeting ( "" --> "Hello, World!" ) {}
206
246
multi greeting ( "bub" --> "Hey bub." ) {}
207
247
multi greeting ( \name ) { "Hello, " ~ name ~ "!" }
248
+ = end code
208
249
209
250
The C < proto > declarator is not necessary, but can sometimes aid in making sure that all multis
210
251
follow your business rules. Using a variable name in the signature of the proto would provide
211
252
more information in error messages, and for introspection.
212
253
254
+ = begin code
213
255
proto greeting ( Str \name --> Str ) {*}
214
256
215
257
say &greeting.signature; # (Str \name --> Str)
258
+ = end code
216
259
217
260
An interesting thing to note in the Perl6 code above is that passing values like C < 'bub' > as a
218
261
function parameter is just syntax sugar for a C < where > guard.
@@ -222,6 +265,7 @@ function parameter is just syntax sugar for a C<where> guard.
222
265
Using the example from the "Pattern Matching" section of this page, you can see the guards that are
223
266
used behind the scenes to constrain our function arguments.
224
267
268
+ = begin code
225
269
multi greeting ( "" --> "Hello, World!" ) {}
226
270
multi greeting ( "bub" --> "Hey bub." ) {}
227
271
@@ -234,6 +278,7 @@ used behind the scenes to constrain our function arguments.
234
278
235
279
multi greeting(Str \name where $_ ~~ '' ) {'Hello, World!'}
236
280
multi greeting(Str \name where $_ ~~ 'bub') {'Hey bub.'}
281
+ = end code
237
282
238
283
C < $_ > is known as the topic variable. It assumes the form of whatever is appropriate. The smart match
239
284
operator C < ~~ > figures out the best way to determine if the left matches the right, be it number ranges,
@@ -242,25 +287,29 @@ strings, etc. Our three examples above go from most sugared (top), to least suga
242
287
The bottom examples above could be wrapped in curly braces, making it more obvious that it is a code
243
288
block. Note that a where clause may also take an explicit Callable.
244
289
290
+ = begin code :skip-test
245
291
multi greeting(Str \name where { $_ ~~ '' } ) {'Hello, World!'}
246
292
247
293
multi greeting(Str \name where -> $thing { $thing ~~ '' } ) {'Hello, World!'}
248
294
249
295
multi greeting ( Str \name where { Bool.pick } --> 'True' ){}
250
296
251
297
multi greeting ( Str \name where &some-subroutine ){…}
298
+ = end code
252
299
253
300
If you read the section in this page on subsets, you'll notice that "where" is used in the making of
254
301
subsets as well as here. The usage of "where" in both areas is exactly the same.
255
302
256
303
When using C < where > , note that the order of definition is important, just like in Haskell.
257
304
305
+ = begin code
258
306
multi greeting ( Str \name where '' --> 'Hello, World!' ){}
259
307
multi greeting ( Str \name where { Bool.pick } --> 'True' ){}
260
308
multi greeting ( Str \name where 'bub' --> 'Hey, bub.' ){}
261
309
262
310
say greeting '' ; # will never say True
263
311
say greeting 'bub'; # about 50% of the time it will say True
312
+ = end code
264
313
265
314
= Argument Deconstruction
266
315
@@ -284,20 +333,25 @@ show function composition operator. Maybe explain a more perl6ish way to do this
284
333
285
334
Haskell makes heavy use of case matching like the below:
286
335
336
+ = begin code :skip-test
287
337
case number of
288
338
2 -> "two"
289
339
4 -> "four"
290
340
8 -> "eight"
291
341
_ -> "don't care"
342
+ = end code
292
343
293
344
In Perl6 you can achieve this same thing with the given/when structure:
294
345
346
+ = begin code
347
+ my $number = {...};
295
348
given $number {
296
349
when 2 { "two" }
297
350
when 4 { "four" }
298
351
when 8 { "eight" }
299
352
default { "don't care" }
300
353
}
354
+ = end code
301
355
302
356
Note that the order of the C < when > 's is also significant, just like with the C < where > 's in the
303
357
guard section of this page.
@@ -319,22 +373,31 @@ TODO compare haskell list comprehensions to Perl6 gather/take
319
373
320
374
Fold in Haskell is called Reduce in Perl6.
321
375
376
+ = begin code :skip-test
322
377
mySum = foldl `+` 0 numList
378
+ = end code
323
379
380
+ = begin code
381
+ my @numbers = {...};
324
382
reduce { $^a + $^b }, 0, |@numbers;
325
383
@numbers.reduce({$^a + $^b}, with => 0)
384
+ = end code
326
385
327
386
However, in Perl6, if you want to use an infix operator (+ - / % etc) there is a nice little
328
387
helper called the Reduction Metaoperator.
329
388
389
+ = begin code
390
+ my @numbers = {...};
330
391
[+] @numbers # This is the same
331
392
[+] 0, @numbers # as this
393
+ = end code
332
394
333
395
It inserts the operator in between all values in the list and produces a result, just like Fold.
334
396
335
397
In Haskell you, you have foldl and foldr. In Perl6, this difference is determined by the
336
398
associativity attached to the operator/subroutine.
337
399
400
+ = begin code
338
401
sub two-elem-list ( \a, \b ) { ( a, b ) }
339
402
340
403
# you can use a subroutine as an infix operator
@@ -355,6 +418,7 @@ associativity attached to the operator/subroutine.
355
418
# chaining
356
419
say [<] 1..5; # True
357
420
say (1..5).reduce: &[<]; # True
421
+ = end code
358
422
359
423
= head2 Map
360
424
@@ -364,28 +428,36 @@ TODO
364
428
365
429
Haskell and Perl6 both allow you to specify ranges of values.
366
430
431
+ = begin code :skip-test
367
432
myRange1 = 10..100
368
433
myRange2 = 1.. -- Infinite
369
434
myRange3 = 'a'..'h' -- Letters work too
435
+ = end code
370
436
371
- my $range1 = 10..100
372
- my $range2 = 1..* # Infinite
373
- my $range3 = 'a'..'h' # Letters work too
437
+ = begin code
438
+ my $range1 = 10..100;
439
+ my $range2 = 1..*; # Infinite
440
+ my $range3 = 'a'..'h'; # Letters work too
441
+ = end code
374
442
375
443
= head2 Laziness vs Eagerness
376
444
377
445
In the examples above, you have the concept of laziness displayed very plainly. Perl6 has laziness
378
446
only where it makes the most sense. For example, in the range 10..100, this is eager because it has
379
447
a definite end. If a list does not have a definite end, then the list should clearly be lazy.
380
448
449
+ = begin code
381
450
(1 .. 100).is-lazy; # False
382
451
(1 .. Inf).is-lazy; # True
452
+ = end code
383
453
384
454
These are the "sane defaults" that Perl6 takes pride in. But they are still defaults and can be
385
455
changed into one or the other.
386
456
457
+ = begin code
387
458
(1 .. 100).lazy.is-lazy; # True
388
459
(1 .. 100).lazy.eager.is-lazy; # False
460
+ = end code
389
461
390
462
= head1 Contexts (let-in / where)
391
463
0 commit comments