Skip to content

Commit 73f6e6f

Browse files
committed
Test doc/Language/haskell-to-p6.pod6 examples
Closes #1199
1 parent 3a81880 commit 73f6e6f

File tree

2 files changed

+82
-11
lines changed

2 files changed

+82
-11
lines changed

doc/Language/haskell-to-p6.pod6

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,64 +20,81 @@ Haskell background.
2020
2121
In Haskell, you have type level programming and then value level programming.
2222
23+
=begin code :skip-test
2324
plusTwo :: Integer -> Integer -- Types
2425
plusTwo x = x + 2 -- Values
26+
=end code
2527
2628
You do not mix types and values in Haskell like the below
2729
30+
=begin code :skip-test
2831
plusTwo 2 -- This is valid
2932
plusTwo Integer -- This is not valid
33+
=end code
3034
3135
In Perl6, types (AKA type objects) live on the same level as values
3236
37+
=begin code
3338
sub plus-two(Int $x --> Int) { $x + 2 }
3439
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
3743
3844
I will illustrate this unique aspect of Perl6 with one more example:
3945
46+
=begin code
4047
multi sub is-string(Str $ --> True) {}
4148
multi sub is-string(Any $ --> False) {}
4249
43-
is-string('hello') #True
44-
is-string(4) #False
50+
is-string('hello'); #True
51+
is-string(4); #False
52+
=end code
4553
4654
=head2 Maybe
4755
4856
In Haskell, you have a Maybe type that allows you to forgo the worry of null types.
4957
Let's say you have a hypothetical function that parses a String to an Integer:
5058
59+
=begin code :skip-test
5160
parseInt :: String -> Maybe Integer
5261
5362
case parseInt myString of
5463
Just x -> x
5564
Nothing -> 0
65+
=end code
5666
5767
In Perl6, since type objects coexist with regular objects, we have the concept of Defined
5868
and Undefined objects. Plain type objects are undefined while instantiated objects are defined.
5969
70+
=begin code
6071
sub parse-int(Str $s --> Int) { ... }
6172
73+
my $string = {...};
6274
given parse-int($string) {
6375
when Int:D { $_ }
6476
when Int:U { 0 }
6577
}
78+
=end code
6679
6780
So in Perl6 we have type constraints that indicate the definedness of a type. These are
6881
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
7287
7388
If we wanted to be explicit in the above example (probably a good idea), we could add
7489
the C<:_> constraint on the return type. This would let the user know that they should account
7590
for both defined and undefined return values. We could also use other methods and constructs
7691
that specifically test for definedness.
7792
93+
=begin code
7894
sub parse-int(Str $s --> Int:_) { ... }
7995
8096
# One way to do it
97+
my $string = {...};
8198
given parse-int($string) {
8299
when Int:D { $_ }
83100
when Int:U { 0 }
@@ -93,17 +110,23 @@ that specifically test for definedness.
93110
94111
# With the defined-or operator
95112
parse-int($string) // 0
113+
=end code
96114
97115
The C<with> operator that you see above is like C<if>, except it explicitly tests for definedness and then
98116
passes the result to the following block. Similarly, C<without> tests that the object is undefined and also
99117
passes the result to the following block.
100118
101119
For more natural control flow with undefined and defined types, Perl6 introduces C<andthen> and C<orelse>.
102120
121+
=begin code
122+
sub parse-int(Str $s --> Int:_) { ... }
123+
124+
my $string = {...};
103125
my $result = parse-int($string) orelse 0;
104126
105127
sub hello() { say 'hi' }
106128
hello() andthen say 'bye';
129+
=end code
107130
108131
TODO: include a better example for andthen that makes sense. Maybe using promise objects?
109132
@@ -117,32 +140,39 @@ object and return a new object, you can certainly do so.
117140
118141
Here is a Haskell code example:
119142
143+
=begin code :skip-test
120144
data Point = Point x y
121145
122146
moveUp :: Point -> Point
123147
moveUp (Point x y) = Point x (y + 1)
148+
=end code
124149
125150
And an equivalent Perl6 example:
126151
152+
=begin code
127153
class Point { has $.x; has $.y; }
128154
129155
sub move-up(Point $p --> Point) {
130156
Point.new(x => $p.x, y => $p.y + 1)
131157
}
158+
=end code
132159
133160
The code I illustrated above is an example of a Product Type. If instead you'd like to
134161
write a Sum Type, there is not an exact equivalent in Perl6. The closest thing would be
135162
an Enum.
136163
164+
=begin code :skip-test
137165
data Animal = Dog | Cat | Bird | Horse
138166
139167
testAnimal :: Animal -> String
140168
testAnimal Dog = "Woof"
141169
testAnimal Horse = "Neigh"
170+
=end code
142171
143172
A Perl6 Enum does not fit the same exact use cases, but it can be used in putting
144173
constraints on types.
145174
175+
=begin code
146176
enum Animal < Dog Cat Bird Horse >;
147177
148178
proto sub test-animal( Animal ) {*}
@@ -151,25 +181,31 @@ constraints on types.
151181
152182
say test-animal Animal::Dog; # more explicit
153183
say test-animal Horse;
184+
=end code
154185
155186
=head2 Type Aliases and Subsets
156187
157188
In Haskell, you can alias an existing type to simply increase clarity of intent and re-use
158189
existing types.
159190
191+
=begin code :skip-test
160192
type Name = String
161193
162194
fullName :: Name -> Name -> Name
163195
fullName first last = first ++ last
196+
=end code
164197
165198
The equivalent in Perl6 is the following.
166199
200+
=begin code
167201
my constant Name = Str;
168202
169203
sub full-name ( Name \first, Name \last --> Name ) { first ~ last }
204+
=end code
170205
171206
It should be noted that in Perl6, one can also create a subset of an existing type.
172207
208+
=begin code :skip-test
173209
subset Name of Str where *.chars < 20;
174210
175211
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
178214
179215
full-name("12345678901234567890111", "Smith") # This does not compile, as the first parameter
180216
# doesn't fit the Name type
217+
=end code
181218
182219
=head2 Typeclasses
183220
@@ -193,26 +230,32 @@ explain how Perl6 roles compare to Haskell typeclasses
193230
194231
Haskell makes heavy use of pattern matching in function definitions.
195232
233+
=begin code :skip-test
196234
greeting :: String -> String
197235
greeting "" = "Hello, World!"
198236
greeting "bub" = "Hey bub."
199237
greeting name = "Hello, " ++ name ++ "!"
238+
=end code
200239
201240
Perl6 does this as well! You just use the C<multi> keyword to signify that it is a multiple dispatch
202241
function.
203242
243+
=begin code
204244
proto greeting ( Str --> Str ) {*}
205245
multi greeting ( "" --> "Hello, World!" ) {}
206246
multi greeting ( "bub" --> "Hey bub." ) {}
207247
multi greeting ( \name ) { "Hello, " ~ name ~ "!" }
248+
=end code
208249
209250
The C<proto> declarator is not necessary, but can sometimes aid in making sure that all multis
210251
follow your business rules. Using a variable name in the signature of the proto would provide
211252
more information in error messages, and for introspection.
212253
254+
=begin code
213255
proto greeting ( Str \name --> Str ) {*}
214256
215257
say &greeting.signature; # (Str \name --> Str)
258+
=end code
216259
217260
An interesting thing to note in the Perl6 code above is that passing values like C<'bub'> as a
218261
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.
222265
Using the example from the "Pattern Matching" section of this page, you can see the guards that are
223266
used behind the scenes to constrain our function arguments.
224267
268+
=begin code
225269
multi greeting ( "" --> "Hello, World!" ) {}
226270
multi greeting ( "bub" --> "Hey bub." ) {}
227271
@@ -234,6 +278,7 @@ used behind the scenes to constrain our function arguments.
234278
235279
multi greeting(Str \name where $_ ~~ '' ) {'Hello, World!'}
236280
multi greeting(Str \name where $_ ~~ 'bub') {'Hey bub.'}
281+
=end code
237282
238283
C<$_> is known as the topic variable. It assumes the form of whatever is appropriate. The smart match
239284
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
242287
The bottom examples above could be wrapped in curly braces, making it more obvious that it is a code
243288
block. Note that a where clause may also take an explicit Callable.
244289
290+
=begin code :skip-test
245291
multi greeting(Str \name where { $_ ~~ '' } ) {'Hello, World!'}
246292
247293
multi greeting(Str \name where -> $thing { $thing ~~ '' } ) {'Hello, World!'}
248294
249295
multi greeting ( Str \name where { Bool.pick } --> 'True' ){}
250296
251297
multi greeting ( Str \name where &some-subroutine ){…}
298+
=end code
252299
253300
If you read the section in this page on subsets, you'll notice that "where" is used in the making of
254301
subsets as well as here. The usage of "where" in both areas is exactly the same.
255302
256303
When using C<where>, note that the order of definition is important, just like in Haskell.
257304
305+
=begin code
258306
multi greeting ( Str \name where '' --> 'Hello, World!' ){}
259307
multi greeting ( Str \name where { Bool.pick } --> 'True' ){}
260308
multi greeting ( Str \name where 'bub' --> 'Hey, bub.' ){}
261309
262310
say greeting '' ; # will never say True
263311
say greeting 'bub'; # about 50% of the time it will say True
312+
=end code
264313
265314
=Argument Deconstruction
266315
@@ -284,20 +333,25 @@ show function composition operator. Maybe explain a more perl6ish way to do this
284333
285334
Haskell makes heavy use of case matching like the below:
286335
336+
=begin code :skip-test
287337
case number of
288338
2 -> "two"
289339
4 -> "four"
290340
8 -> "eight"
291341
_ -> "don't care"
342+
=end code
292343
293344
In Perl6 you can achieve this same thing with the given/when structure:
294345
346+
=begin code
347+
my $number = {...};
295348
given $number {
296349
when 2 { "two" }
297350
when 4 { "four" }
298351
when 8 { "eight" }
299352
default { "don't care" }
300353
}
354+
=end code
301355
302356
Note that the order of the C<when>'s is also significant, just like with the C<where>'s in the
303357
guard section of this page.
@@ -319,22 +373,31 @@ TODO compare haskell list comprehensions to Perl6 gather/take
319373
320374
Fold in Haskell is called Reduce in Perl6.
321375
376+
=begin code :skip-test
322377
mySum = foldl `+` 0 numList
378+
=end code
323379
380+
=begin code
381+
my @numbers = {...};
324382
reduce { $^a + $^b }, 0, |@numbers;
325383
@numbers.reduce({$^a + $^b}, with => 0)
384+
=end code
326385
327386
However, in Perl6, if you want to use an infix operator (+ - / % etc) there is a nice little
328387
helper called the Reduction Metaoperator.
329388
389+
=begin code
390+
my @numbers = {...};
330391
[+] @numbers # This is the same
331392
[+] 0, @numbers # as this
393+
=end code
332394
333395
It inserts the operator in between all values in the list and produces a result, just like Fold.
334396
335397
In Haskell you, you have foldl and foldr. In Perl6, this difference is determined by the
336398
associativity attached to the operator/subroutine.
337399
400+
=begin code
338401
sub two-elem-list ( \a, \b ) { ( a, b ) }
339402
340403
# you can use a subroutine as an infix operator
@@ -355,6 +418,7 @@ associativity attached to the operator/subroutine.
355418
# chaining
356419
say [<] 1..5; # True
357420
say (1..5).reduce: &[<]; # True
421+
=end code
358422
359423
=head2 Map
360424
@@ -364,28 +428,36 @@ TODO
364428
365429
Haskell and Perl6 both allow you to specify ranges of values.
366430
431+
=begin code :skip-test
367432
myRange1 = 10..100
368433
myRange2 = 1.. -- Infinite
369434
myRange3 = 'a'..'h' -- Letters work too
435+
=end code
370436
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
374442
375443
=head2 Laziness vs Eagerness
376444
377445
In the examples above, you have the concept of laziness displayed very plainly. Perl6 has laziness
378446
only where it makes the most sense. For example, in the range 10..100, this is eager because it has
379447
a definite end. If a list does not have a definite end, then the list should clearly be lazy.
380448
449+
=begin code
381450
(1 .. 100).is-lazy; # False
382451
(1 .. Inf).is-lazy; # True
452+
=end code
383453
384454
These are the "sane defaults" that Perl6 takes pride in. But they are still defaults and can be
385455
changed into one or the other.
386456
457+
=begin code
387458
(1 .. 100).lazy.is-lazy; # True
388459
(1 .. 100).lazy.eager.is-lazy; # False
460+
=end code
389461
390462
=head1 Contexts (let-in / where)
391463

xt/examples-compilation.t

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ if @*ARGS {
2323
| 'Language/5to6-perlop.p6'
2424
| 'Language/5to6-perlsyn.p6'
2525
| 'Language/5to6-perlvar.p6'
26-
| 'Language/haskell-to-p6.p6'
2726
| 'Language/modules.p6'
2827
| 'Language/nativecall.p6'
2928
| 'Language/packages.p6'

0 commit comments

Comments
 (0)