Skip to content

Commit dcd83b9

Browse files
committed
[CaR Grant] Document all allomorphic types
1 parent ce0fa7c commit dcd83b9

File tree

1 file changed

+135
-5
lines changed

1 file changed

+135
-5
lines changed

doc/Language/numerics.pod6

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,141 @@ say <4/2>.nude; # OUTPUT: «(2 1)␤»
288288
289289
=head1 Allomorphs
290290
291-
=head2 C<IntStr>
292-
=head2 C<NumStr>
293-
=head2 C<ComplexStr>
294-
=head2 C<RatStr>
295-
=head2 C<MidRatStr>
291+
L<Allomorphs|/language/glossary#index-entry-Allomorph> are subclasses of two types that can
292+
behave as either of them. For example, the allomorph L<IntStr> is the subclass of L<Int> and
293+
L<Str> types and will be accepted by any type constraint that requires an L<Int> or L<Str> object.
294+
295+
Allomorphs can be created using L«angle brackets|/language/quoting#Word_quoting:_<_>», either used
296+
standalone or as part of a hash key lookup; directly
297+
using method C<.new>; and are also provided by some constructs such as
298+
parameters of L«C<sub MAIN>|/language/functions#sub_MAIN» or, in the case of the L<MidRat>
299+
allomorph, L<Rational> literals with large denominators.
300+
301+
=begin code
302+
say <42>.^name; # OUTPUT: «IntStr␤»
303+
say <42e0>.^name; # OUTPUT: «NumStr␤»
304+
say < 42+42i>.^name; # OUTPUT: «ComplexStr␤»
305+
say < 1/2>.^name; # OUTPUT: «RatStr␤»
306+
say <0.5>.^name; # OUTPUT: «RatStr␤»
307+
say <1/99999999999999999999>.^name; # OUTPUT: «MidRat␤»
308+
say < 1/99999999999999999999>.^name; # OUTPUT: «MidRatStr␤»
309+
310+
@*ARGS = "42";
311+
sub MAIN($x) { say $x.^name } # OUTPUT: «IntStr␤»
312+
313+
say IntStr.new(42, "42").^name; # OUTPUT: «IntStr␤»
314+
=end code
315+
316+
A couple of constructs above have a space after the opening angle bracket. That space isn't
317+
accidental. Numerics that are often written using an operator, such as C<1/2> (L<Rat>,
318+
division operator) and C<1+2i> (L<Complex>, addition) can be written as a literal that doesn't
319+
involve the use of an operator: angle brackets I<without> any spaces between the brackets and the
320+
characters inside. By adding spaces within the brackets, we tell the compiler that not only we
321+
want a L<Rat> or L<Complex> literal, but we also want it to be an allmorph: the L<RatStr> or
322+
L<ComplexStr>, in this case.
323+
324+
If the numeric literal doesn't use any operators, then writing it inside the angle brackets, even
325+
without including any spaces within, would produce the allomorph. (Logic: if you didn't want
326+
the allomorph, you wouldn't use the angle brackets. The same isn't true for operator-using
327+
numbers as some constructs, such as signature literals, do not let you use operators, so you
328+
can't just omit angle brackets for such numeric literals).
329+
330+
=head2 Available Allomorphs
331+
332+
The core language offers the following allomorphs:
333+
334+
=begin table
335+
336+
Type | Allomorph of | Example
337+
===========+======================+=================
338+
IntStr | Int and Str | <42>
339+
NumStr | Num and Str | <42e0>
340+
ComplexStr | Complex and Str | < 1+2i>
341+
RatStr | Rat and Str | <1.5>
342+
MidRat | Rat and FatRat | <1/99999999999999999999>
343+
MidRatStr | Rat, FatRat, and Str | < 1/99999999999999999999>
344+
345+
=end table
346+
347+
Note: there is no C<FatRatStr> type.
348+
349+
=head2 Coercion of Allomorphs
350+
351+
Keep in mind that allomorphs are simply subclasses of the two (or three) types they represent. Just
352+
as a variable or parameter type-constrained to C<Foo> can accept any subclass of C<Foo>, so will
353+
a variable or parameter type-constrained to L<Int> will accept an L<IntStr> allomorph:
354+
355+
=begin code
356+
sub foo(Int $x) { say $x.^name }
357+
foo <42>; # OUTPUT: «IntStr␤»
358+
my Num $y = <42e0>;
359+
say $y.^name; # OUTPUT: «NumStr␤»
360+
=end code
361+
362+
This, of course, also applies to parameter L<coercers|/type/Signature#Coercion_Type>:
363+
364+
=begin code
365+
sub foo(Int(Cool) $x) { say $x.^name }
366+
foo <42>; # OUTPUT: «IntStr␤»
367+
=end code
368+
369+
The given allomorph is I<already> an object of type L<Int>, so it does not get converted to
370+
a "plain" L<Int> in this case.
371+
372+
Of course, the power of allomorphs would be severely diminished if there were no way to
373+
"collapse" them to one of their components. Thus, if you explicitly call a method with the name
374+
of the type to coerce to, you'll get just that component. The same applies to any proxy methods,
375+
such as calling method L«C<.Numeric>|/routine/Numeric» instead of L«C<.Int>|/routine/Int»
376+
or using the L«C<< prefix:<~> >> operator|/routine/~» instead of
377+
L«C<.Str>|/routine/Str» method call.
378+
379+
=begin code
380+
my $al := IntStr.new: 42, "forty two";
381+
say $al.Str; # OUTPUT: «forty two␤»
382+
say +$al; # OUTPUT: «42␤»
383+
384+
say <1/99999999999999999999>.^name; # OUTPUT: «MidRat␤»
385+
say <1/99999999999999999999>.Rat.^name; # OUTPUT: «Rat␤»
386+
say <1/99999999999999999999>.FatRat.^name; # OUTPUT: «FatRat␤»
387+
=end code
388+
389+
A handy way to coerce a whole list of allomorphs is by
390+
L<hypering|/language/operators#Hyper_Operators> the appropraite prefix operator:
391+
392+
=begin code
393+
say map *.^name, <42 50e0 100>; # OUTPUT: «(IntStr NumStr IntStr)␤»
394+
say map *.^name, +«<42 50e0 100>; # OUTPUT: «(Int Num Int)␤»
395+
say map *.^name, ~«<42 50e0 100>; # OUTPUT: «(Str Str Str)␤»
396+
=end code
397+
398+
=head2 Object Identity
399+
400+
The above discussion on coercing allomorphs becomes more important when we consider object
401+
identity. Some constructs untilize it to ascertain whether two objects are "the same". And while
402+
to humans an allomorphic C<42> and regular C<42> might appear "the same", to those constructs,
403+
they're entirely different objects:
404+
405+
=begin code
406+
# "42" shows up twice in the result: 42 and <42> are different objects:
407+
say unique 1, 1, 1, 42, <42>; # OUTPUT: «(1 42 42)␤»
408+
# Use a different operator to `unique` with:
409+
say unique :with(&[==]), 1, 1, 1, 42, <42>; # OUTPUT: «(1 42)␤»
410+
# Or coerce the input instead (faster than using a different `unique` operator):
411+
say unique :as(*.Int), 1, 1, 1, 42, <42>; # OUTPUT: «(1 42)␤»
412+
say unique +«(1, 1, 1, 42, <42>); # OUTPUT: «(1 42)␤»
413+
414+
# Parametarized Hash with `Any` keys does not stringify them; our key is of type `Int`:
415+
my %h{Any} = 42 => "foo";
416+
# But we use the allomorphic key of type `IntStr`, which is not in the Hash:
417+
say %h<42>:exists; # OUTPUT: «False␤»
418+
# Must use curly braces to avoid the allomorph:
419+
say %h{42}:exists; # OUTPUT: «False␤»
420+
421+
# We are using a set operator to look up an `Int` object in a list of `IntStr` objects:
422+
say 42 ∈ <42 100 200>; # OUTPUT: «False␤»
423+
=end code
424+
425+
Be mindful of these object identity differences and coerce your allomorphs as needed.
296426
297427
=head1 Native
298428

0 commit comments

Comments
 (0)