Skip to content

Commit

Permalink
Flesh out Array sections.
Browse files Browse the repository at this point in the history
  • Loading branch information
skids committed Jan 18, 2016
1 parent 3e3a649 commit 74e295f
Showing 1 changed file with 183 additions and 8 deletions.
191 changes: 183 additions & 8 deletions doc/Language/list.pod
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,6 @@ as named parameters:
my %a = "c" => 3;
Array.new(1, |%a, 4); # Array contains 1, 4
=head2 Array Constructor Context
[ 1, 2, :c(3) ]
=comment TODO actually not sure if there is anything special here, other then the internal itemization best left for later -- eager maybe?
=head2 Slice Indexing Context
From the perspective of the C<List> inside a L<slice subscript|/language/subscripts#Slices>,
Expand All @@ -243,16 +237,197 @@ structure in the result:
say ("a","b","c")[(1,2),(0,1)] eqv (("b", "c"), ("a", "b")) # says True
=head2 Array Constructor Context
Inside an Array Literal, the list of initialization values is not in capture
context and is just a normal list. It is, however, eagerly evaluated just as
in assignment.
[ 1, 2, :c(3) ] eqv Array.new((1, 2, :c(3))) # says True
[while $++ < 2 { 42.say; 43 }].map: *.say; # says 42 twice then 43 twice
(while $++ < 2 { 42.say; 43 }).map: *.say; # says "42" then "43"
# then "42" then "43"
Which brings us to Arrays...
=head1 Arrays
Arrays differ from lists in three major ways: Their elements may be typed,
they automatically itemize their elements, and they are mutable. Otherwise
they are Lists and are accepted wherever lists are.
say Array ~~ List # says True
A fourth, more subtle, way they differ is that when working with Arrays, it
can sometimes be harder to maintain laziness or work with infinite sequences.
=head2 Typing
Arrays may be typed such that their slots perform a typecheck whenever
they are assigned to. An Array that only allows C<Int> values to be assigned
is of type C<Array[Int]> and one can create one with C<Array[Int].new>. If
you intend to use an C<@>-sigiled variable only for this purpose, you may
change its type by specifying the type of the elements when declaring it:
my Int @a = 1, 2, 3; # An Array that contains only Ints
my @b := Array[Int].new(1,2,3); # Same thing, but the variable is not typed
say @b eqv @a; # says True.
my @c = 1, 2, 3; # An Array that can contain anything
say @b eqv @c; # says False because types do not match
say @c eqv (1,2,3); # says False because one is a List
say @b eq @c; # says True, because eq only checks values
say @b eq (1,2,3); # says True, because eq only checks values
@a[0] = 42; # fine
@a[0] = "foo"; # error: Type check failed in assignment
In the above example we bound a typed Array object to a C<@>-sigil variable for
which no type had been specified. The other way around does not work -- you may
not bind an Array that has the wrong type to a typed C<@>-sigiled variable:
my @a := Array[Int].new(1,2,3); # fine
@a := Array[Str].new("a","b"); # fine, can be re-bound
my Int @b := Array[Int].new(1,2,3); # fine
@b := Array.new(1,2,3); # error: Type check failed in binding
Note that in any given compiler, there may be fancy, under-the-hood, ways to
bypass the type check on arrays, so when handling untrusted input, it can be
good practice to perform additional type checks, where it matters:
for @a -> Int $i { $_++.say };
However, as long as you stick to normal assignment operations inside a trusted
area of code, this will not be a problem, and typecheck errors will happen
promptly during assignment to the array, if they cannot be caught at compile
time. None of the core functions provided in Perl 6 for operating on lists
should ever produce a wonky typed Array.
Nonexistant elements (when indexed), or elements to which C<Nil> has been assigned,
will assume a default value. This default may be adjusted on a variable-by-variable
basis with the C<is default> trait. Note that an untyped C<@>-sigiled variable has
an element type of C<Mu>, however its default value is an undefined C<Any>:
my @a;
@a.of.perl.say; # says "Mu"
@a.default.perl.say; # says "Any"
@a[0].say; # says "(Any)"
my Numeric @n is default(Real);
@n.of.perl.say; # says "Numeric"
@n.default.perl.say; # says "Real"
@n[0].say; # says "(Real)"
=head2 Itemization
=comment TODO explain nuances of automatic itemization done by arrays
For most uses, Arrays consist of a number of slots each containing a C<Scalar> of
the correct type. Each such C<Scalar>, in turn, contains a value of that type.
Perl 6 will automatically type-check values and create Scalars to contain them
when Arrays are initialized, assigned to, or constructed.
This is actually one of the trickiest parts of Perl 6 list handling to get a
firm understanding of.
First, be aware that because itemization in Arrays is assumed, it essentially
means that C<$(…)>s are being put around everything that you assign to an
array, if you do not put them there yourself. On the other side, Array.perl
does not put C<$> to explicitly show scalars, unlike List.perl:
((1,2),$(3,4)).perl.say; # says "((1, 2), $(3, 4))"
[(1,2),$(3,4)].perl.say; # says "[(1, 2), (3, 4)]"
# ...but actually means: "[$(1,2), $(3,4)]"
It was decided all those extra dollar signs and parenthesis were more of an
eye sore than a benefit to the user. Basically, when you see a square bracket,
remember the invisible dollar signs.
Second, remember that these invisible dollar signs also protect against
flattenning, so you cannot really flatten the elements inside of an Array
with a normal call to C<flat> or C<.flat>.
((1,2),$(3,4)).flat.perl.say; # (1, 2, $(3, 4)).Seq
[(1,2),$(3,4)].flat.perl.say; # ($(1, 2), $(3, 4)).Seq
Since the square brackets do not themselves protect against flattening,
you can still spill the elements out of an Array into a surrounding list
using C<flat>.
(0,[(1,2),$(3,4)],5).flat.perl.say; # (0, $(1, 2), $(3, 4), 5).Seq
...the elements themselves, however, stay in one peice.
This can irk users of data you provide if you have deeply nested Arrays
where they want flat data. Currently they have to deeply map the structure
by hand to undo the nesting:
say gather [0,[(1,2),[3,4]],$(5,6)].deepmap: *.take; # 1 2 3 4 5 6
...future versions of Perl 6 might find a way to make this easier. However,
not returning Arrays or itemized lists from functions, when non-itemized lists
are sufficient, is something that one should consider as a courtesy to their
users:
=item use Slips when you want to always merge with surrounding lists
=item use non-itemized lists when you want to make it easy for the user to flatten
=item use itemized lists to protect things the user probaby will not want flattened
=item use Arrays as non-itemized lists of itemized lists, if appropriate,
=item use Arrays if the user is going to want to mutate the result without copying it first.
The fact that all elements of an array are itemized (in C<Scalar> containers)
is more a gentleman's agreement than a universally enforced rule, and it is
less well enforced that typechecks in typed arrays. See the section below
on binding to Array slots.
=head2 Literal Arrays
Literal Arrays are constructed with a List inside square brackets. The List is
eagerly iterated (at compile time if possible) and values in the list are each
type-checked and itemized. The square brackets themselves will spill elements
into surrounding lists when flattened, but the elements themselves will not
spill due to the itemization.
=head2 Mutability
=head3 Binding
Unlike lists, Arrays are mutable. Elements may deleted, added, or changed.
my @a = "a", "b", "c";
@a.say; # [a b c]
@a.pop.say; # says "c"
@a.say; # says "[a b]"
@a.push("d");
@a.say; # says "[a b d]"
@a[1,3] = "c","c";
@a.say; # says "[a c d c]"
=head3 Assigning
Assignment of a list to an Array is eager. The list will be entirely evaluated,
and should not be infinite or the program may hang. Assignment to a slice of
an C<Array> is, likewise, eager, but only up to the requested number of elements,
which may be finite:
my @a;
@a[0,1,2] = (loop { 42 });
@a.say; # says "[42 42 42]"
During assignment, each value will be typechecked to ensure it is a permitted
type for the C<Array>. Any C<Scalar> will be stripped from each value and a
new C<Scalar> will be wrapped around it.
=head3 Binding
Individual Array slots may be bound the same way C<$>-sigiled variables are:
my $b = "foo";
my @a = 1, 2, 3;
@a[2] := $b;
@a.say; # says '[1 2 "foo"]'
$b = "bar";
@a.say; # says '[1 2 "bar"]'
...but binding Array slots directly to values is strongly discouraged. If you do,
expect surprises with built-in functions. The only time this would be done is if
a mutable container that knows the difference between values and Scalar-wrapped
values is needed, or for very large Arrays where a native-typed array cannot be used.
Such a creature should never be passed back to unsuspecting users.
=end pod

0 comments on commit 74e295f

Please sign in to comment.