This page was generated at 2009-07-03 23:01:19 GMT.
(syn r27396, pugs-smoke 19912)
[ Index of Synopses ]
Synopsis 2: Bits and Pieces
Larry Wall <larry@wall.org>
Created: 10 Aug 2004
Last Modified: 17 Jun 2009
Version: 171
This document summarizes Apocalypse 2, which covers small-scale lexical items and typological issues. (These Synopses also contain updates to reflect the evolving design of Perl 6 over time, unlike the Apocalypses, which are frozen in time as "historical documents". These updates are not marked--if a Synopsis disagrees with its Apocalypse, assume the Synopsis is correct.)
From t/spec/S02-one-pass-parsing/less-than.t lines 7–30 (no results): (skip)
| # L<S02/One-pass parsing/>
|
|
|
| # test parsing of < and >, especially distinction between operators
|
| # and terms (when used as a quote as in <a b c>)
|
| #
|
| # nearly all of these tests had been regressions at one point,
|
| # so don't discard them for being too simple ;-)
|
|
|
| ok(rand >= 0, 'random numbers are greater than or equal to 0');
|
| ok(rand < 1, 'random numbers are less than 1');
|
|
|
| ok 3 > 0, "3 is greater than 0";
|
|
|
|
|
| # used to be a pugs regression
|
| # ~< foo bar >
|
| # doesn't parse (as does +< foo bar >).
|
| is eval('~< foo bar >'), "foo bar", "~<...> is parsed correctly";
|
| is eval('+< foo bar >'), 2, "+<...> is parsed correctly";
|
| ok eval('?< foo bar >'), "?<...> is parsed correctly";
|
|
|
| is eval('~(< foo bar >)'), "foo bar", "~(<...>) is parsed correctly";
|
| is eval('+(< foo bar >)'), 2, "+(<...>) is parsed correctly";
|
| ok eval('?(< foo bar >)'), "?(<...>) is parsed correctly";
|
Highlighted:
small|full
From t/spec/S02-whitespace_and_comments/one-pass-parsing.t lines 6–9 (no results): (skip)
| # L<S02/"One-pass parsing">
|
|
|
| ok(eval('regex foo { <[ } > ]> }; 1'),
|
| "can parse non-backslashed curly and right bracket in cclass");
|
Highlighted:
small|full
To the extent allowed by sublanguages' parsers, Perl is parsed using a one-pass, predictive parser. That is, lookahead of more than one "longest token" is discouraged. The currently known exceptions to this are where the parser must:
- Locate the end of interpolated expressions that begin with a sigil and might or might not end with brackets.
- Recognize that a reduce operator is not really beginning a
[...] composer.
- In the abstract, Perl is written in Unicode, and has consistent Unicode semantics regardless of the underlying text representations. By default Perl presents Unicode in "NFG" formation, where each grapheme counts as one character. A grapheme is what the novice user would think of as a character in their normal everyday life, including any diacritics.
From t/spec/S02-lexical-conventions/unicode.t lines 7–105 (no results): (skip)
| # L<S02/"Lexical Conventions"/"Perl is written in Unicode">
|
|
|
| # Unicode variables
|
| # english ;-)
|
| lives_ok {my $foo; sub foo {}; 1}, "ascii declaration";
|
| is (do {my $bar = 2; sub id ($x) { $x }; id($bar)}), 2, "evaluation";
|
|
|
| # umlauts
|
| lives_ok {my $übervar; sub fü {}; 1}, "umlauts declaration";
|
| is (do {my $schloß = 2; sub öok ($x) { $x }; öok($schloß)}), 2, "evaluation";
|
|
|
| # monty python
|
| lives_ok {my $møøse; sub bïte {}; 1}, "a møøse once bit my sister";
|
| is (do {my $møøse = 2; sub såck ($x) { $x }; såck($møøse)}), 2,
|
| "møøse bites kan be preti nasti";
|
|
|
| # french
|
| lives_ok {my $un_variable_français; sub blâ {}; 1}, "french declaration";
|
| is (do {my $frénch = 2; sub bléch ($x) { $x }; bléch($frénch)}), 2, "evaluation";
|
|
|
| # Some Chinese Characters
|
| lives_ok {my $一; 1}, "chinese declaration";
|
| is (do {my $二 = 2; sub 恆等($x) {$x}; 恆等($二)}), 2, "evaluation";
|
|
|
| # Tibetan Characters
|
| lives_ok {my $ཀ; 1}, "tibetan declaration";
|
| is (do {my $ཁ = 2; $ཁ}), 2, "evaluation";
|
|
|
| # Japanese
|
| lives_ok {my $い; 1}, "japanese declaration";
|
| is (do {my $に = 2; $に}), 2, "evaluation";
|
|
|
| # arabic
|
| lives_ok {my $الصفحة ; 1}, "arabic declaration";
|
| is (do {my $الصفحة = 2; $الصفحة}), 2, "evaluation";
|
|
|
| # hebrew
|
| lives_ok {my $פוו; sub לה {}; 1}, "hebrew declaration";
|
| is (do {my $באר = 2; sub זהות ($x) { $x }; זהות($באר)}), 2, "evaluation";
|
|
|
| # magyar
|
| lives_ok {my $aáeéiíoóöőuúüű ; 1}, "magyar declaration";
|
| is (do {my $áéóőöúűüí = 42; sub űáéóőöúüí ($óőöú) { $óőöú }; űáéóőöúüí($áéóőöúűüí)}),
|
| 42, "evaluation";
|
|
|
| # russian
|
| lives_ok {my $один; sub раз {}; 1}, "russian declaration";
|
| is
|
| (do {my $два = 2; sub идентичный ($x) { $x }; идентичный($два)}),
|
| 2,
|
| "evaluation";
|
|
|
| #?rakudo 2 skip 'VOWEL SIGNs in identifiers'
|
| lives_ok { my $पहला = 1; }, "hindi declaration";
|
| is((try { my $दूसरा = 2; sub टोटल ($x) { $x + 2 }; टोटल($दूसरा) }), 4, "evaluation");
|
|
|
| # Unicode subs
|
| {
|
| my sub äöü () { 42 }
|
| is (äöü), 42, "Unicode subs with no parameters";
|
| }
|
| {
|
| my sub äöü ($x) { 1000 + $x }
|
| is (äöü 17), 1017, "Unicode subs with one parameter (parsed as prefix ops)";
|
| }
|
|
|
| # Unicode parameters
|
| {
|
| my sub abc (:$äöü) { 1000 + $äöü }
|
|
|
| is abc(äöü => 42), 1042, "Unicode named params (1)";
|
| is abc(:äöü(42)), 1042, "Unicode named params (2)";
|
| }
|
|
|
| # Unicode placeholder variables
|
| {
|
| is
|
| ~(< foostraße barstraße fakestraße >.map: { ucfirst $^straßenname }),
|
| "Foostraße Barstraße Fakestraße",
|
| "Unicode placeholder variables";
|
| }
|
|
|
| # Unicode methods and attributes
|
| {
|
| class A {
|
| has $!möp = 'pugs';
|
| method äöü {
|
| $!möp.ucfirst();
|
| }
|
| }
|
| is A.new().äöü(), "Pugs", "Unicode methods and attributes";
|
| }
|
|
|
| {
|
| sub f(*%x) { %x<ä> };
|
| is f(ä => 3), 3, 'non-ASCII named arguments';
|
| }
|
|
|
| # vim: ft=perl6 fileencoding=utf-8
|
Highlighted:
small|full
- Perl can count Unicode line and paragraph separators as line markers, but that behavior had better be configurable so that Perl's idea of line numbers matches what your editor thinks about Unicode lines.
- Unicode horizontal whitespace is counted as whitespace, but it's better not to use thin spaces where they will make adjoining tokens look like a single token. On the other hand, Perl doesn't use indentation as syntax, so you are free to use any amount of whitespace anywhere that whitespace makes sense. Comments always count as whitespace.
From t/spec/S02-whitespace_and_comments/unicode-whitespace.t lines 7–133 (4 √, 22 ×): (skip)
| # L<S02/"Lexical Conventions"/"Unicode horizontal whitespace"> |
| |
√ | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "CHARACTER TABULATION"); |
| |
√ | is(eval(' |
| my |
| @x |
| = |
| <a |
| b |
| c>; |
| sub |
| y |
| (@z) |
| { |
| @z[1] |
| }; |
| y(@x) |
| '), "b", "LINE FEED (LF)"); |
| |
× | is(eval(' |
| my@x=<abc>;suby(@z){@z[1]};y(@x) |
| '), "b", "LINE TABULATION"); |
| |
× | is(eval(' |
| my@x =<abc>;suby(@z){@z[1]};y(@x) |
| '), "b", "FORM FEED (FF)"); |
| |
√ | is(eval(' |
| my
@x
=
<a
b
c>;
sub
y
(@z)
{
@z[1]
};
y(@x) |
| '), "b", "CARRIAGE RETURN (CR)"); |
| |
√ | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "SPACE"); |
| |
× | is(eval(' |
| my
@x
=
<a
b
c>;
sub
y
(@z)
{
@z[1]
};
y(@x) |
| '), "b", "NEXT LINE (NEL)"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "NO-BREAK SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "OGHAM SPACE MARK"); |
| |
× | is(eval(' |
| my@x=<abc>;suby(@z){@z[1]};y(@x) |
| '), "b", "MONGOLIAN VOWEL SEPARATOR"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "EN QUAD"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "EM QUAD"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "EN SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "EM SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "THREE-PER-EM SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "FOUR-PER-EM SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "SIX-PER-EM SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "FIGURE SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "PUNCTUATION SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "THIN SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "HAIR SPACE"); |
| |
× | is(eval(' |
| my
@x
=
<a
b
c>;
sub
y
(@z)
{
@z[1]
};
y(@x) |
| '), "b", "LINE SEPARATOR"); |
| |
× | is(eval(' |
| my
@x
=
<a
b
c>;
sub
y
(@z)
{
@z[1]
};
y(@x) |
| '), "b", "PARAGRAPH SEPARATOR"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "NARROW NO-BREAK SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "MEDIUM MATHEMATICAL SPACE"); |
| |
× | is(eval(' |
| my @x = <a b c>; sub y (@z) { @z[1] }; y(@x) |
| '), "b", "IDEOGRAPHIC SPACE"); |
| |
| #Long dot whitespace tests |
| #These currently get different results than the above |
| |
| #This makes 'foo.lc' and 'foo .lc' mean different things |
| multi foo() { 'a' } |
| multi foo($x) { $x } |
| |
| $_ = 'b'; |
| |
Highlighted:
small|full
From t/spec/S02-whitespace_and_comments/unicode-whitespace.t lines 134–163 (21 √, 3 ×): (skip)
| # L<S02/"Lexical Conventions"/"Unicode horizontal whitespace"> |
| is(eval('foo\ .lc'), 'a', 'long dot with CHARACTER TABULATION'); |
| is(eval('foo\ |
| .lc'), 'a', 'long dot with LINE FEED (LF)'); |
| is(eval('foo\.lc'), 'a', 'long dot with LINE TABULATION'); |
√ | is(eval('foo\.lc'), 'a', 'long dot with FORM FEED (FF)'); |
√ | is(eval('foo\
.lc'), 'a', 'long dot with CARRIAGE RETURN (CR)'); |
| is(eval('foo\ .lc'), 'a', 'long dot with SPACE'); |
√ | is(eval('foo\
.lc'), 'a', 'long dot with NEXT LINE (NEL)'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with NO-BREAK SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with OGHAM SPACE MARK'); |
√ | is(eval('foo\.lc'), 'a', 'long dot with MONGOLIAN VOWEL SEPARATOR'); |
× | is(eval('foo\ .lc'), 'a', 'long dot with EN QUAD'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with EM QUAD'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with EN SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with EM SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with THREE-PER-EM SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with FOUR-PER-EM SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with SIX-PER-EM SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with FIGURE SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with PUNCTUATION SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with THIN SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with HAIR SPACE'); |
√ | is(eval('foo\
.lc'), 'a', 'long dot with LINE SEPARATOR'); |
√ | is(eval('foo\
.lc'), 'a', 'long dot with PARAGRAPH SEPARATOR'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with NARROW NO-BREAK SPACE'); |
√ | is(eval('foo\ .lc'), 'a', 'long dot with MEDIUM MATHEMATICAL SPACE'); |
× | is(eval('foo\ .lc'), 'a', 'long dot with IDEOGRAPHIC SPACE'); |
× | |
√ | # vim: ft=perl6 |
Highlighted:
small|full
- For some syntactic purposes, Perl distinguishes bracketing characters from non-bracketing. Bracketing characters are defined as any Unicode characters with either bidirectional mirrorings or Ps/Pe/Pi/Pf properties.
From t/spec/S02-literals/quoting.t lines 25–58 (no results): (skip)
| # L<S02/Lexical Conventions/bidirectional mirrorings>
|
| {
|
| my $s = q{ foo bar };
|
| is $s, ' foo bar ', 'string using q{}';
|
| }
|
|
|
| {
|
| #?rakudo skip 'nested curlies in q{...}'
|
| is q{ { foo } }, ' { foo } ', 'Can nest curlies in q{ .. }';
|
| is q{{ab}}, 'ab', 'Unnested single curlies in q{{...}}';
|
| is q{{ fo} }}, ' fo} ', 'Unnested single curlies in q{{...}}';
|
| #?rakudo skip 'nested double curlies in q{{...}}'
|
| is q{{ {{ } }} }}, ' {{ } }} ', 'Can nest double curlies in q{{...}}';
|
| }
|
|
|
| {
|
| is q{\n}, '\n', 'q{..} do not interpolate \n';
|
| ok q{\n}.chars == 2, 'q{..} do not interpolate \n';
|
| is q{$x}, '$x', 'q{..} do not interpolate scalars';
|
| ok q{$x}.chars == 2, 'q{..} do not interpolate scalars';
|
| }
|
|
|
| {
|
| is Q{\n}, '\n', 'Q{..} do not interpolate \n';
|
| ok Q{\n}.chars == 2, 'Q{..} do not interpolate \n';
|
| is Q{$x}, '$x', 'Q{..} do not interpolate scalars';
|
| ok Q{$x}.chars == 2, 'Q{..} do not interpolate scalars';
|
| is Q {\\}, '\\\\', 'Q {..} quoting';
|
| }
|
|
|
| {
|
| ok Q{\\}.chars == 2, 'Q{..} do not interpolate backslashes';
|
| }
|
|
|
Highlighted:
small|full
In practice, though, you're safest using matching characters with Ps/Pe/Pi/Pf properties, though ASCII angle brackets are a notable exception, since they're bidirectional but not in the Ps/Pe/Pi/Pf sets.
Characters with no corresponding closing character do not qualify as opening brackets. This includes the second section of the Unicode BidiMirroring data table.
If a character is already used in Ps/Pe/Pi/Pf mappings, then any entry in BidiMirroring is ignored (both forward and backward mappings). For any given Ps character, the next Pe codepoint (in numerical order) is assumed to be its matching character even if that is not what you might guess using left-right symmetry. Therefore U+298D maps to U+298E, not U+2990, and U+298F maps to U+2990, not U+298E. Neither U+298E nor U+2990 are valid bracket openers, despite having reverse mappings in the BidiMirroring table.
The U+301D codepoint has two closing alternatives, U+301E and U+301F; Perl 6 only recognizes the one with lower code point number, U+301E, as the closing brace. This policy also applies to new one-to-many mappings introduced in the future.
From t/spec/S02-whitespace_and_comments/unspace.t lines 268–271 (no results): (skip)
| # L<S02/"Lexical Conventions"/"U+301D codepoint has two closing alternatives">
|
| is((foo\#〝 comment 〞.id), 'a', 'unspace with U+301D/U+301E comment');
|
| eval_dies_ok('foo\#〝 comment 〟.id', 'unspace with U+301D/U+301F is invalid');
|
|
|
Highlighted:
small|full
However, many-to-one mappings are fine; multiple opening characters may map to the same closing character. For instance, U+2018, U+201A, and U+201B may all be used as the opener for the U+2019 closer. Constructs that count openers and closers assume that only the given opener is special. That is, if you open with one of the alternatives, all other alternatives are treated as non-bracketing characters within that construct.
From t/spec/S02-literals/listquote-whitespace.t lines 5–66 (no results): (skip)
| # L<S02/Whitespace and Comments>
|
|
|
| =begin kwid
|
|
|
| = DESCRIPTION
|
|
|
| Tests that the List quoting parser properly
|
| ignores whitespace in lists. This becomes important
|
| if your line endings are \x0d\x0a.
|
|
|
| Characters that should be ignored are:
|
|
|
| \t
|
| \r
|
| \n
|
| \x20
|
|
|
| Most likely there are more. James tells me that
|
| the maximum Unicode char is \x10FFFF , so maybe
|
| we should simply (re)construct the whitespace
|
| list via IsSpace or \s on the fly.
|
|
|
| Of course, in the parsed result, no item should
|
| contain whitespace.
|
|
|
| C<\xA0> is specifically an I<nonbreaking> whitespace
|
| character and thus should B<not> break the list.
|
|
|
| =end kwid
|
|
|
| #?pugs emit if $?PUGS_BACKEND ne "BACKEND_PUGS" {
|
| #?pugs emit skip_rest "PIL2JS and PIL-Run do not support eval() yet.";
|
| #?pugs emit exit;
|
| #?pugs emit }
|
|
|
| my @list = <a b c d>;
|
| my @separators = ("\t","\r","\n"," ");
|
| my @nonseparators = (",","/","\\",";","\xa0");
|
|
|
| plan +@separators + @nonseparators;
|
|
|
| for @separators -> $sep {
|
| my $str = "<$sep" ~ @list.join("$sep$sep") ~ "$sep>";
|
| my @res = eval $str;
|
|
|
| my $vis = sprintf "%02x", ord $sep;
|
| is( @res, @list, "'\\x$vis\\x$vis' is properly parsed as list whitespace")
|
| };
|
|
|
| for @nonseparators -> $sep {
|
| my $ex = @list.join($sep);
|
| my $str = "<" ~$ex~ ">";
|
| my @res = eval $str;
|
|
|
| my $vis = sprintf "%02x", ord $sep;
|
| #?rakudo emit if $sep eq "\xa0" {
|
| #?rakudo emit todo('\xa0 should not be a separator for list quotes');
|
| #?rakudo emit };
|
| is( @res, [@list.join($sep)], "'\\x$vis' does not split in a whitespace quoted list")
|
| };
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
- POD sections may be used reliably as multiline comments in Perl 6. Unlike in Perl 5, POD syntax now lets you use
=begin comment and =end comment delimit a POD block correctly without the need for =cut. (In fact, =cut is now gone.) The format name does not have to be comment -- any unrecognized format name will do to make it a comment. (However, bare =begin and =end probably aren't good enough, because all comments in them will show up in the formatted output.)
From t/spec/S02-whitespace_and_comments/comments.t lines 169–187 (no results): (skip)
| # L<S02/"Whitespace and Comments"/POD sections may be>
|
| =begin oppsFIXME
|
| {
|
| # needs to be wrapped in eval so it can be properly isolated
|
| my $a = eval q{
|
| my $var = 1;
|
|
|
| =begin comment
|
|
|
| This is a comment without
|
| a "=cut".
|
|
|
| =end comment
|
|
|
| "bar";
|
| };
|
| is $a, 'bar', '=begin comment without =cut works';
|
| }
|
|
|
Highlighted:
small|full
We have single paragraph comments with =for comment as well. That lets =for keep its meaning as the equivalent of a =begin and =end combined. As with =begin and =end, a comment started in code reverts to code afterwards.
From t/spec/S02-whitespace_and_comments/comments.t lines 188–212 (no results): (skip)
| # L<S02/Whitespace and Comments/"single paragraph comments"
|
| # =for comment>
|
|
|
| {
|
| is eval(q{
|
| my $var = 1;
|
|
|
| =for comment TimToady is here!
|
|
|
| 32;
|
| }), 32, '=for comment works';
|
| }
|
|
|
| {
|
| is eval(q{
|
| my $var = 1;
|
|
|
| =for comment TimToady and audreyt
|
| are both here, yay!
|
|
|
| 17;
|
| }), 17, '=for comment works';
|
| }
|
|
|
| =end oppsFIXME
|
Highlighted:
small|full
Since there is a newline before the first =, the POD form of comment counts as whitespace equivalent to a newline. See S26 for more on embedded documentation.
- Except within a string literal, a
# character always introduces a comment in Perl 6. There are two forms of comment based on #. Embedded comments require the # to be followed by one or more opening bracketing characters.
All other uses of # are interpreted as single-line comments that work just as in Perl 5, starting with a # character and ending at the subsequent newline. They count as whitespace equivalent to newline for purposes of separation. Unlike in Perl 5, # may not be used as the delimiter in quoting constructs.
From t/spec/S02-whitespace_and_comments/comments.t lines 157–168 (no results): (skip)
| # L<S02/Whitespace and Comments/"# may not be used as"
|
| # delimiter quoting>
|
| {
|
| my $a;
|
| ok eval('$a = q{ 32 }'), 'sanity check';
|
| is $a, ' 32 ', 'sanity check';
|
|
|
| $a = undef;
|
| eval_dies_ok '$a = q# 32 #;', 'misuse of # as quote delimiters';
|
| ok !$a.defined, "``#'' can't be used as quote delimiters";
|
| }
|
|
|
Highlighted:
small|full
- Embedded comments are supported as a variant on quoting syntax, introduced by
# plus any user-selected bracket characters (as defined in "Lexical Conventions" above):
From t/spec/S02-whitespace_and_comments/comments.t lines 9–60 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"Embedded comments"
|
| # "#" plus any bracket>
|
| {
|
|
|
| ok #[
|
| Multiline
|
| comments
|
| is fine
|
| ] 1, 'multiline embedded comment with #[]';
|
|
|
| ok #(
|
| Parens works also
|
| ) 1, 'multiline embedded comment with #()';
|
|
|
| ok eval("2 * 3\n #<<<\n comment>>>"), "multiline comment with <<<";
|
|
|
| my $var = #{ foo bar } 32;
|
| is $var, 32, 'embedded comment with #{}';
|
|
|
| $var = 3 + #「 this is a comment 」 56;
|
| is $var, 59, 'embedded comment with LEFT/RIGHT CORNER BRACKET';
|
|
|
| is 2 #『 blah blah blah 』 * 3, 6, 'embedded comment with LEFT/RIGHT WHITE CORNER BRACKET';
|
|
|
| my @list = 'a'..'c';
|
|
|
| is @list[ #(注释)2 ], 'c', 'embedded comment with FULLWIDTH LEFT/RIGHT PARENTHESIS';
|
|
|
| is @list[ 0 #《注释》], 'a', 'embedded comment with LEFT/RIGHT DOUBLE ANGLE BRACKET';
|
|
|
| is @list[#〈注释〉1], 'b', 'embedded comment with LEFT/RIGHT ANGLE BRACKET';
|
|
|
| # Note that 'LEFT/RIGHT SINGLE QUOTATION MARK' (i.e. ‘’) and
|
| # LEFT/RIGHT DOUBLE QUOTATION MARK (i.e. “”) are not valid delimiter
|
| # characters.
|
|
|
| #test some 13 more lucky unicode bracketing pairs
|
| is(1 #᚛ pa ᚜ +1, 2, 'embedded comment with #᚛᚜');
|
| is(1 #⁅ re ⁆ +2, 3, 'embedded comment with #⁅⁆');
|
| is(2 #⁽ ci ⁾ +3, 5, 'embedded comment with #⁽⁾');
|
| is(3 #❨ vo ❩ +5, 8, 'embedded comment with #❨ vo ❩');
|
| is(5 #❮ mu ❯ +8, 13, 'embedded comment with #❮❯');
|
| is(8 #❰ xa ❱ +13, 21, 'embedded comment with #❰❱');
|
| is(13 #❲ ze ❳ +21, 34, 'embedded comment with #❲❳');
|
| is(21 #⟦ bi ⟧ +34, 55, 'embedded comment with #⟦⟧');
|
| is(34 #⦅ so ⦆ +55, 89, 'embedded comment with #⦅⦆');
|
| is(55 #⦓ pano ⦔ +89, 144, 'embedded comment with #⦓⦔');
|
| is(144 #⦕ papa ⦖ +233, 377, 'embedded comment with #⦕⦖');
|
| is(377 #『 pare 』 +610, 987, 'embedded comment with #『』');
|
| is(610 #﴾ paci ﴿ +987, 1597, 'embedded comment with #﴾﴿');
|
| }
|
|
|
Highlighted:
small|full
say #( embedded comment ) "hello, world!";
$object\#{ embedded comments }.say;
$object\ #「
embedded comments
」.say;
Brackets may be nested, following the same policy as ordinary quote brackets.
From t/spec/S02-whitespace_and_comments/comments.t lines 87–126 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"Brackets may be nested">
|
| #?rakudo skip 'nested brackets'
|
| {
|
| is 3, #(
|
| (Nested parens) works also
|
| ) 3, 'nested parens #(...(...)...)';
|
|
|
| is 3, #{
|
| {Nested braces} works also {}
|
| } 3, 'nested braces #{...{...}...}';
|
| }
|
|
|
| # I am not sure if this is speced somewhere:
|
| # comments can be nested
|
| #?rakudo skip 'nested brackets'
|
| {
|
| is 3, #(
|
| comment
|
| #{
|
| internal comment
|
| }
|
| more comment
|
| ) 3, 'comments can be nested with different brackets';
|
| is 3, #(
|
| comment
|
| #(
|
| internal comment
|
| )
|
| more
|
| ) 3, 'comments can be nested with same brackets';
|
|
|
| # TODO:
|
| # ok eval(" #{ comment }") failes with an error as it tries to execute
|
| # comment() before seeing that I meant #{ comment within this string.
|
|
|
| #?pugs todo 'bug'
|
| ok eval(" #<<\n comment\n # >>\n >> 3"),
|
| 'single line comment cannot correctly nested within multiline';
|
| }
|
|
|
Highlighted:
small|full
There must be no space between the # and the opening bracket character. (There may be the visual appearance of space for some double-wide characters, however, such as the corner quotes above.)
From t/spec/S02-whitespace_and_comments/comments.t lines 61–70 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"no space" between "#" and bracket>
|
| {
|
|
|
| ok !eval("3 * # (invalid comment) 2"), "no space allowed between '#' and '('";
|
| ok !eval("3 * #\t[invalid comment] 2"), "no tab allowed between '#' and '['";
|
| ok !eval("3 * # \{invalid comment\} 2"), "no spaces allowed between '#' and '\{'";
|
| ok !eval("3 * #\n<invalid comment> 2"), "no spaces allowed between '#' and '<'";
|
|
|
| }
|
|
|
Highlighted:
small|full
An embedded comment is not allowed as the first thing on the line.
#sub foo # line-end comment
#{ # ILLEGAL, syntax error
# ...
#}
If you wish to have a comment there, you must disambiguate it to either an embedded comment or a line-end comment. You can put a space in front of it to make it an embedded comment:
#sub foo # line end comment
#{ # okay, comment
... # extends
} # to here
Or you can put something other than a single # to make it a line-end comment. Therefore, if you are commenting out a block of code using the line-comment form, we recommend that you use ##, or # followed by some whitespace, preferably a tab to keep any tab formatting consistent:
##sub foo
##{ # okay
## ...
##}
# sub foo
# { # okay
# ...
# }
# sub foo
# { # okay
# ...
# }
However, it's often better to use pod comments because they are implicitly line-oriented. And if you have an intelligent syntax highlighter that will mark pod comments in a different color, there's less visual need for a # on every line.
- For all quoting constructs that use user-selected brackets, you can open with multiple identical bracket characters, which must be closed by the same number of closing brackets. Counting of nested brackets applies only to pairs of brackets of the same length as the opening brackets:
From t/spec/S02-whitespace_and_comments/comments.t lines 71–86 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"closed by" "same number of"
|
| # "closing brackets">
|
| {
|
|
|
| ok #<<<
|
| Or this <also> works...
|
| >>> 1, '#<<<...>>>';
|
|
|
| my $var = \#((( comment ))) 12;
|
| is $var, 12, '#(((...)))';
|
|
|
| is(5 * #<< < >> 5, 25, '#<< < >>');
|
|
|
| is(6 * #<< > >> 6, 36, '#<< > >>');
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-whitespace_and_comments/comments.t lines 127–140 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"Counting of nested brackets"
|
| # "applies only to" "pairs of brackets of the same length">
|
| #?rakudo skip 'nested parens and braces'
|
| {
|
| is -1 #<<<
|
| Even <this> <<< also >>> works...
|
| >>>, -1, 'nested brackets in embedded comment';
|
|
|
| is 'cat', #{{
|
| This comment contains unmatched } and { { { { (ignored)
|
| Plus a nested {{ ... }} pair (counted)
|
| }} 'cat', 'embedded comments with nested/unmatched bracket chars';
|
| }
|
|
|
Highlighted:
small|full
say #{{
This comment contains unmatched } and { { { { (ignored)
Plus a nested {{ ... }} pair (counted)
}} q<< <<woot>> >> # says " <<woot>> "
Note however that bare circumfix or postcircumfix <<...>> is not a user-selected bracket, but the ASCII variant of the «...» interpolating word list. Only # and the q-style quoters (including m, s, tr, and rx) enable subsequent user-selected brackets.
- Some languages such as C allow you to escape newline characters to combine lines. Other languages (such as regexes) allow you to backslash a space character for various reasons. Perl 6 generalizes this notion to any kind of whitespace. Any contiguous whitespace (including comments) may be hidden from the parser by prefixing it with
\. This is known as the "unspace". An unspace can suppress any of several whitespace dependencies in Perl. For example, since Perl requires an absence of whitespace between a noun and a postfix operator, using unspace lets you line up postfix operators:
From t/spec/S02-whitespace_and_comments/unspace.t lines 7–180 (no results): (skip)
| # L<S02/"Whitespace and Comments"/This is known as the "unspace">
|
|
|
|
|
| ok(4\ .sqrt == 2, 'unspace with numbers');
|
| is(4\#(quux).sqrt, 2, 'unspace with comments');
|
| is("x"\ .chars, 1, 'unspace with strings');
|
| is("x"\ .chars(), 1, 'unspace with strings + parens');
|
|
|
| #?rakudo skip 'unspace with postfix operators'
|
| {
|
| my $foo = 4;
|
| is($foo.++, 4, '(short) unspace with postfix inc');
|
| is($foo, 5, '(short) unspace with postfix inc really postfix');
|
| is($foo\ .++, 5, 'unspace with postfix inc');
|
| is($foo, 6, 'unspace with postfix inc really postfix');
|
| is($foo\ .--, 6, 'unspace with postfix dec');
|
| is($foo, 5, 'unspace with postfix dec really postfix');
|
| }
|
|
|
| is("xxxxxx"\.chars, 6, 'unspace without spaces');
|
| is("xxxxxx"\
|
| .chars, 6, 'unspace with newline');
|
|
|
| is((:foo\ ("bar")), ('foo' => "bar"), 'unspace with adverb');
|
|
|
| is( ~([1,2,3]\ .[2,1,0]), "3 2 1", 'unspace on postfix subscript');
|
|
|
| my @array = 1,2,3;
|
|
|
| eval "
|
| @array\ .>>++;
|
| @array>>\ .++;
|
| @array\ .>>\ .++;
|
| @array\ .»++;
|
| @array»\ .++;
|
| @array\ .»\ .++;
|
| ";
|
| #?pugs todo 'unimpl'
|
| #?rakudo todo 'unimpl'
|
| is( ~@array, "7 8 9", 'unspace with postfix hyperops');
|
|
|
|
|
| #Test the "unspace" and unspace syntax
|
|
|
|
|
| #This makes 'foo.lc' and 'foo .lc' mean different things
|
| multi foo() { 'a' }
|
| multi foo($x) { $x }
|
|
|
| #This should do the same, but currently doesn't
|
| sub bar($x? = 'a') { $x }
|
|
|
| $_ = 'b';
|
|
|
| is((foo.lc ), 'a', 'sanity - foo.lc');
|
| is((foo .lc ), 'b', 'sanity - foo .lc');
|
| is((bar.lc ), 'a', 'sanity - bar.lc');
|
| is((bar .lc ), 'b', 'sanity - bar .lc');
|
| is((foo\.lc ), 'a', 'short unspace');
|
| is((foo\ .lc ), 'a', 'unspace');
|
| is((foo \ .lc), 'b', 'not a unspace');
|
| eval_dies_ok('fo\ o.lc', 'unspace not allowed in identifier');
|
| is((foo\ .lc), 'a', 'longer dot');
|
| is((foo\#( comment ).lc), 'a', 'unspace with embedded comment');
|
| #?rakudo skip 'unimplemented'
|
| eval_dies_ok('foo\#\ ( comment ).lc', 'unspace can\'t hide space between # and opening bracket');
|
| is((foo\ # comment
|
| .lc), 'a', 'unspace with end-of-line comment');
|
| is((:foo\ <bar>), (:foo<bar>), 'unspace in colonpair');
|
| #?rakudo skip 'unimplemented'
|
| is((foo\ .\ ("x")), 'x', 'unspace is allowed both before and after method .');
|
| is((foo\
|
| =begin comment
|
| blah blah blah
|
| =end comment
|
| .lc), 'a', 'unspace with pod =begin/=end comment');
|
| #?rakudo skip '=for pod not implemented (in STD.pm)'
|
| {
|
| is((foo\
|
| =for comment
|
| blah
|
| blah
|
| blah
|
|
|
| .lc), 'a', 'unspace with pod =for comment');
|
| }
|
| is(eval('foo\
|
| =comment blah blah blah
|
| .lc'), 'a', 'unspace with pod =comment');
|
| #This is pretty strange: according to Perl-6.0.0-STD.pm,
|
| #unspace is allowed after a pod = ... which means pod is
|
| #syntactically recursive, i.e. you can put pod comments
|
| #inside pod directives recursively!
|
| is(eval('foo\
|
| =\ begin comment
|
| blah blah blah
|
| =\ end comment
|
| .lc'), 'a', 'unspace with pod =begin/=end comment w/ pod unspace');
|
| #?rakudo skip '=for pod not implemented (in STD.pm)'
|
| {
|
| is(eval('foo\
|
| =\ for comment
|
| blah
|
| blah
|
| blah
|
|
|
| .lc'), 'a', 'unspace with pod =for comment w/ pod unspace');
|
| }
|
| is(eval('foo\
|
| =\ comment blah blah blah
|
| .lc'), 'a', 'unspace with pod =comment w/ pod unspace');
|
| is(eval('foo\
|
| =\
|
| =begin nested pod
|
| blah blah blah
|
| =end nested pod
|
| begin comment
|
| blah blah blah
|
| =\
|
| =begin nested pod
|
| blah blah blah
|
| =end nested pod
|
| end comment
|
| .lc'), 'a', 'unspace with pod =begin/=end comment w/ pod-in-pod');
|
| #?rakudo skip '=for pod not implemented (in STD.pm)'
|
| {
|
| is(eval('foo\
|
| =\
|
| =for nested pod
|
| blah
|
| blah
|
| blah
|
|
|
| for comment
|
| blah
|
| blah
|
| blah
|
|
|
| .lc'), 'a', 'unspace with pod =for commenti w/ pod-in-pod');
|
| is(eval('foo\
|
| =\
|
| =nested pod blah blah blah
|
| comment blah blah blah
|
| .lc'), 'a', 'unspace with pod =comment w/ pod-in-pod');
|
| is(eval('foo\
|
| =\ #1
|
| =\ #2
|
| =\ #3
|
| =comment blah blah blah
|
| for comment #3
|
| blah
|
| blah
|
| blah
|
|
|
| begin comment #2
|
| blah blah blah
|
| =\ #4
|
| =comment blah blah blah
|
| end comment #4
|
| begin comment #1
|
| blah blah blah
|
| =\ #5
|
| =\ #6
|
| =for comment
|
| blah
|
| blah
|
| blah
|
|
|
| comment blah blah blah #6
|
| end comment #5
|
| .lc'), 'a', 'hideous nested pod torture test');
|
|
|
| }
|
|
|
Highlighted:
small|full
%hash\ {$key}
@array\ [$ix]
$subref\($arg)
As a special case to support the use above, a backslash where a postfix is expected is considered a degenerate form of unspace. Note that whitespace is not allowed before that, hence
$subref \($arg)
is a syntax error (two terms in a row). And
foo \($arg)
will be parsed as a list operator with a Capture argument:
foo(\($arg))
However, other forms of unspace may usefully be preceded by whitespace. (Unary uses of backslash may therefore never be followed by whitespace or they would be taken as an unspace.)
Other postfix operators may also make use of unspace:
$number\ ++;
$number\ --;
1+3\ i;
$object\ .say();
$object\#{ your ad here }.say
Another normal use of a you-don't-see-this-space is typically to put a dotted postfix on the next line:
$object\ # comment
.say
$object\#[ comment
].say
$object\
.say
But unspace is mainly about language extensibility: it lets you continue the line in any situation where a newline might confuse the parser, regardless of your currently installed parser. (Unless, of course, you override the unspace rule itself...)
Although we say that the unspace hides the whitespace from the parser, it does not hide whitespace from the lexer. As a result, unspace is not allowed within a token. Additionally, line numbers are still counted if the unspace contains one or more newlines. A # following such a newline is always an end-of-line comment, as described above. Since Pod chunks count as whitespace to the language, they are also swallowed up by unspace. Heredoc boundaries are suppressed, however, so you can split excessively long heredoc intro lines like this:
ok(q:to'CODE', q:to'OUTPUT', \
"Here is a long description", \ # --more--
todo(:parrøt<0.42>, :dötnet<1.2>));
...
CODE
...
OUTPUT
To the heredoc parser that just looks like:
ok(q:to'CODE', q:to'OUTPUT', "Here is a long description", todo(:parrøt<0.42>, :dötnet<1.2>));
...
CODE
...
OUTPUT
Note that this is one of those cases in which it is fine to have whitespace before the unspace, since we're only trying to suppress the newline transition, not all whitespace as in the case of postfix parsing. (Note also that the example above is not meant to spec how the test suite works. :)
- An unspace may contain a comment, but a comment may not contain an unspace. In particular, end-of-line comments do not treat backslash as significant. If you say:
From t/spec/S02-whitespace_and_comments/comments.t lines 150–156 (no results): (skip)
| # L<S02/Whitespace and Comments/"comment may not contain an unspace">
|
| {
|
| my $a;
|
| ok !eval '$a = #\ (comment) 32', "comments can't contain unspace";
|
| ok !$a.defined, '$a remains undef';
|
| }
|
|
|
Highlighted:
small|full
#\ (...
it is an end-of-line comment, not an embedded comment. Write:
\ #(
...
)
to mean the other thing.
- In general, whitespace is optional in Perl 6 except where it is needed to separate constructs that would be misconstrued as a single token or other syntactic unit. (In other words, Perl 6 follows the standard longest-token principle, or in the cases of large constructs, a prefer shifting to reducing principle. See "Grammatical Categories" below for more on how a Perl program is analyzed into tokens.)
This is an unchanging deep rule, but the surface ramifications of it change as various operators and macros are added to or removed from the language, which we expect to happen because Perl 6 is designed to be a mutable language. In particular, there is a natural conflict between postfix operators and infix operators, either of which may occur after a term. If a given token may be interpreted as either a postfix operator or an infix operator, the infix operator requires space before it. Postfix operators may never have intervening space, though they may have an intervening dot. If further separation is desired, an unspace or embedded comment may be used as described above, as long as no whitespace occurs outside the unspace or embedded comment.
From t/spec/S02-whitespace_and_comments/unspace.t lines 207–267 (no results): (skip)
| # L<S02/"Whitespace and Comments"/"natural conflict between postfix operators and infix operators">
|
| #This creates syntactic ambiguity between
|
| # ($n) ++ ($m)
|
| # ($n++) $m
|
| # ($n) (++$m)
|
| # ($n) + (+$m)
|
|
|
| #?rakudo skip 'defining new operators'
|
| {
|
| my $n = 1;
|
| my $m = 2;
|
| sub infix:<++>($x, $y) { 42 }
|
|
|
| #'$n++$m' should be a syntax error
|
| eval_dies_ok('$n++$m', 'infix requires space when ambiguous with postfix');
|
| is($n, 1, 'check $n');
|
| is($m, 2, 'check $m');
|
|
|
| #'$n ++$m' should be infix:<++>
|
| #no, really: http://irclog.perlgeek.de/perl6/2007-05-09#id_l328
|
| $n = 1; $m = 2;
|
| is(eval('$n ++$m'), 42, '$n ++$m with infix:<++> is $n ++ $m');
|
| is($n, 1, 'check $n');
|
| is($m, 2, 'check $m');
|
|
|
| #'$n ++ $m' should be infix:<++>
|
| $n = 1; $m = 2;
|
| is(eval('$n ++ $m'), 42, 'postfix requires no space w/ infix ambiguity');
|
| is($n, 1, 'check $n');
|
| is($m, 2, 'check $m');
|
|
|
| #These should all be postfix syntax errors
|
| $n = 1; $m = 2;
|
| eval_dies_ok('$n.++ $m', 'postfix dot w/ infix ambiguity');
|
| eval_dies_ok('$n\ ++ $m', 'postfix unspace w/ infix ambiguity');
|
| eval_dies_ok('$n\ .++ $m', 'postfix unspace w/ infix ambiguity');
|
| is($n, 1, 'check $n');
|
| is($m, 2, 'check $m');
|
|
|
| #Unspace inside operator splits it
|
| $n = 1; $m = 2;
|
| is(($n+\ +$m), 3, 'unspace inside operator splits it');
|
| is($n, 1, 'check $n');
|
| is($m, 2, 'check $m');
|
|
|
| $n = 1;
|
| eval_dies_ok('$n ++', 'postfix requires no space');
|
| is($n, 1, 'check $n');
|
|
|
| $n = 1;
|
| is($n.++, 1, 'postfix dot');
|
| is($n, 2, 'check $n');
|
|
|
| $n = 1;
|
| is($n\ ++, 1, 'postfix unspace');
|
| is($n, 2, 'check $n');
|
|
|
| $n = 1;
|
| is($n\ .++, 1, 'postfix unspace');
|
| is($n, 2, 'check $n');
|
|
|
Highlighted:
small|full
For instance, if you were to add your own infix:<++> operator, then it must have space before it. The normal autoincrementing postfix:<++> operator may never have space before it, but may be written in any of these forms:
From t/01-sanity/02-counter.t lines 1–19 (no results): (skip)
| # L<S02/"Whitespace and Comments"/space before it, but may be written in any> |
| use v6; |
| |
| # Checking that testing is sane: counted tests |
| |
| |
| say '1..4'; |
| |
| my $counter = 1; |
| say "ok $counter"; |
| |
| $counter++; |
| say "ok $counter"; |
| |
| ++$counter; |
| say 'ok ', $counter; |
| |
| ++$counter; |
| say 'ok ' ~ $counter; |
Highlighted:
small|full
$x++
$x\++
$x.++
$x\ ++
$x\ .++
$x\#( comment ).++
$x\#((( comment ))).++
$x\
.++
$x\ # comment
# inside unspace
.++
$x\ # comment
# inside unspace
++ # (but without the optional postfix dot)
$x\#『 comment
more comment
』.++
$x\#[ comment 1
comment 2
=begin podstuff
whatever (pod comments ignore current parser state)
=end podstuff
comment 3
].++
A consequence of the postfix rule is that (except when delimiting a quote or terminating an unspace) a dot with whitespace in front of it is always considered a method call on $_ where a term is expected. If a term is not expected at this point, it is a syntax error. (Unless, of course, there is an infix operator of that name beginning with dot. You could, for instance, define a Fortranly infix:<.EQ.> if the fit took you. But you'll have to be sure to always put whitespace in front of it, or it would be interpreted as a postfix method call instead.)
For example,
foo .method
and
foo
.method
will always be interpreted as
foo $_.method
but never as
foo.method
Use some variant of
foo\
.method
if you mean the postfix method call.
One consequence of all this is that you may no longer write a Num as 42. with just a trailing dot. You must instead say either 42 or 42.0. In other words, a dot following a number can only be a decimal point if the following character is a digit. Otherwise the postfix dot will be taken to be the start of some kind of method call syntax. (The .123 form with a leading dot is still allowed however when a term is expected, and is equivalent to 0.123 rather than $_.123.)
From t/spec/S02-whitespace_and_comments/unspace.t lines 272–279 (no results): (skip)
| # L<S02/"Whitespace and Comments"/".123">
|
| # .123 is equal to 0.123
|
|
|
| is ( .123), 0.123, ' .123 is equal to 0.123';
|
| is (.123), 0.123, '.123 is equal to 0.123';
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
From t/spec/S06-signature/sub-ref.t lines 5–106 (no results): (skip)
| # L<S02/"Built-In Data Types">
|
|
|
| plan 33;
|
|
|
| =begin description
|
|
|
| These tests test subroutine references and their invocation.
|
|
|
| See L<S02/"Built-in Data Types"> for more information about Code, Routine, Sub, Block, etc.
|
|
|
| =end description
|
|
|
| # Quoting A06:
|
| # Code
|
| # ____________|________________
|
| # | |
|
| # Routine Block
|
| # ________________|_______________
|
| # | | | | | |
|
| # Sub Method Submethod Multi Rule Macro
|
|
|
| {
|
| my $foo = sub () { 42 };
|
| isa_ok($foo, Code);
|
| isa_ok($foo, Routine);
|
| isa_ok($foo, Sub);
|
| is $foo.(), 42, "basic invocation of an anonymous sub";
|
| #?rakudo todo 'signature error checking'
|
| dies_ok { $foo.(23) }, "invocation of an parameterless anonymous sub with a parameter dies";
|
| }
|
|
|
| {
|
| my $foo = -> { 42 };
|
| isa_ok($foo, Code);
|
| isa_ok($foo, Block);
|
| is $foo.(), 42, "basic invocation of a pointy block";
|
| #?rakudo todo 'signature error checking'
|
| dies_ok { $foo.(23) }, "invocation of an parameterless pointy block with a parameter dies";
|
| }
|
|
|
| {
|
| my $foo = { 100 + $^x };
|
| isa_ok($foo, Code);
|
| isa_ok($foo, Block);
|
| is $foo.(42), 142, "basic invocation of a pointy block with a param";
|
| dies_ok { $foo.() }, "invocation of an parameterized block expecting a param without a param dies";
|
| }
|
|
|
| {
|
| my $foo = sub { 100 + (@_[0] // -1) };
|
| isa_ok($foo, Code);
|
| isa_ok($foo, Routine);
|
| isa_ok($foo, Sub);
|
| is $foo.(42), 142, "basic invocation of a perl5-like anonymous sub (1)";
|
| is $foo.(), 99, "basic invocation of a perl5-like anonymous sub (2)";
|
| }
|
|
|
| {
|
| my $foo = sub ($x) { 100 + $x };
|
| isa_ok($foo, Code);
|
| isa_ok($foo, Routine);
|
| isa_ok($foo, Sub);
|
| is $foo.(42), 142, "calling an anonymous sub with a positional param";
|
| #?rakudo skip 'calling positional parameters by name'
|
| is $foo.(x => 42), 142, "calling an anonymous sub with a positional param addressed by name";
|
| dies_ok { $foo.() },
|
| "calling an anonymous sub expecting a param without a param dies";
|
| dies_ok { $foo.(42, 5) },
|
| "calling an anonymous sub expecting one param with two params dies";
|
| }
|
|
|
| # Confirmed by p6l, see thread "Anonymous macros?" by Ingo Blechschmidt
|
| # L<"http://www.nntp.perl.org/group/perl.perl6.language/21825">
|
| #?rakudo skip 'macros, compile time binding'
|
| {
|
| # We do all this in a eval() not because the code doesn't parse,
|
| # but because it's safer to only call macro references at compile-time.
|
| # So we'd need to wrap the code in a BEGIN {...} block. But then, our test
|
| # code would be called before all the other tests, causing confusion. :)
|
| # So, we wrap the code in a eval() with an inner BEGIN.
|
| # (The macros are subject to MMD thing still needs to be fleshed out, I
|
| # think.)
|
| our &foo_macro ::= macro ($x) { "1000 + $x" };
|
| isa_ok(&foo_macro, Code);
|
| isa_ok(&foo_macro, Routine);
|
| #?pugs todo 'macros'
|
| isa_ok(&foo_macro, Macro);
|
|
|
| is foo_macro(3), 1003, "anonymous macro worked";
|
| }
|
|
|
| {
|
| my $mkinc = sub { my $x = 0; return sub { $x++ }; };
|
|
|
| my $inc1 = $mkinc();
|
| my $inc2 = $mkinc();
|
|
|
| is($inc1(), 0, "clousures: inc1 == 0");
|
| is($inc1(), 1, "clousures: inc1 == 1");
|
| is($inc2(), 0, "clousures: inc2 == 0");
|
| is($inc2(), 1, "clousures: inc2 == 1");
|
| }
|
Highlighted:
small|full
- In support of OO encapsulation, there is a new fundamental datatype: P6opaque. External access to opaque objects is always through method calls, even for attributes.
- Perl 6 has an optional type system that helps you write safer code that performs better. The compiler is free to infer what type information it can from the types you supply, but will not complain about missing type information unless you ask it to.
- Types are officially compared using name equivalence rather than structural equivalence. However, we're rather liberal in what we consider a name. For example, the name includes the version and authority associated with the module defining the type (even if the type itself is "anonymous"). Beyond that, when you instantiate a parametric type, the arguments are considered part of the "long name" of the resulting type, so one
Array of Int is equivalent to another Array of Int. (Another way to look at it is that the type instantiation "factory" is memoized.) Typename aliases are considered equivalent to the original type. In particular, the Array of Int syntax is just sugar for Array:of(Int), which is the canonical form of an instantiated generic type.
This name equivalence of parametric types extends only to parameters that can be considered immutable (or that at least can have an immutable snapshot taken of them). Two distinct classes are never considered equivalent even if they have the same attributes because classes are not considered immutable.
- Perl 6 supports the notion of properties on various kinds of objects. Properties are like object attributes, except that they're managed by the individual object rather than by the object's class.
According to S12, properties are actually implemented by a kind of mixin mechanism, and such mixins are accomplished by the generation of an individual anonymous class for the object (unless an identical anonymous class already exists and can safely be shared).
- Properties applied to objects constructed at compile-time, such as variables and classes, are also called traits. Traits cannot be changed at run-time. Changes to run-time properties are done via mixin instead, so that the compiler can optimize based on declared traits.
- Perl 6 is an OO engine, but you're not generally required to think in OO when that's inconvenient. However, some built-in concepts such as filehandles will be more object-oriented in a user-visible way than in Perl 5.
- A variable's type is a constraint indicating what sorts of values the variable may contain. More precisely, it's a promise that the object or objects contained in the variable are capable of responding to the methods of the indicated "role". See S12 for more about roles.
From t/spec/S02-builtin_data_types/type.t lines 10–40 (no results): (skip)
| # L<S02/"Built-In Data Types"/"A variable's type is a constraint indicating what sorts">
|
|
|
| plan 44;
|
|
|
| {
|
| ok(try {my Int $foo; 1}, 'compile my Int $foo');
|
| ok(try {my Str $bar; 1}, 'compile my Str $bar');
|
| }
|
|
|
| ok(do {my Int $foo; $foo ~~ Int}, 'Int $foo isa Int');
|
| ok(do {my Str $bar; $bar ~~ Str}, 'Str $bar isa Str');
|
|
|
| my Int $foo;
|
| my Str $bar;
|
|
|
| {
|
| #?pugs 1 todo
|
| dies_ok({$foo = 'xyz'}, 'Int restricts to integers');
|
| is(($foo = 42), 42, 'Int is an integer');
|
|
|
| #?pugs 1 todo
|
| dies_ok({$bar = 42}, 'Str restricts to strings');
|
| is(($bar = 'xyz'), 'xyz', 'Str is a strings');
|
| }
|
|
|
| my $baz of Int;
|
| {
|
| dies_ok({$baz = 'xyz'}, 'of Int restricts to integers');
|
| is(($baz = 42), 42, 'of Int is an integer');
|
| }
|
|
|
Highlighted:
small|full
# $x can contain only Int objects
my Int $x;
A variable may itself be bound to a container type that specifies how the container works, without specifying what kinds of things it contains.
# $x is implemented by the MyScalar class
my $x is MyScalar;
Constraints and container types can be used together:
# $x can contain only Int objects,
# and is implemented by the MyScalar class
my Int $x is MyScalar;
Note that $x is also initialized to the Int type object. See below for more on this.
my Dog $spot by itself does not automatically call a Dog constructor. It merely assigns an undefined Dog prototype object to $spot:
my Dog $spot; # $spot is initialized with ::Dog
my Dog $spot = Dog; # same thing
$spot.defined; # False
say $spot; # "Dog"
Any type name used as a value is an undefined instance of that type's prototype object, or type object for short. See S12 for more on that. Any type name in rvalue context is parsed as a single type value and expects no arguments following it. However, a type object responds to the function call interface, so you may use the name of a type with parentheses as if it were a function, and any argument supplied to the call is coerced to the type indicated by the type object. If there is no argument in the parentheses, the type object returns itself:
my $type = Num; # type object as a value
$num = $type($string) # coerce to Num
To get a real Dog object, call a constructor method such as new:
my Dog $spot .= new;
my Dog $spot = $spot.new; # .= is rewritten into this
You can pass in arguments to the constructor as well:
my Dog $cerberus .= new(heads => 3);
my Dog $cerberus = $cerberus.new(heads => 3); # same thing
- If you say
my int @array is MyArray;
you are declaring that the elements of @array are native integers, but that the array itself is implemented by the MyArray class. Untyped arrays and hashes are still perfectly acceptable, but have the same performance issues they have in Perl 5.
- To get the number of elements in an array, use the
.elems method. You can also ask for the total string length of an array's elements, in bytes, codepoints or graphemes, using these methods .bytes, .codes or .graphs respectively on the array. The same methods apply to strings as well. (Note that .bytes is not guaranteed to be well-defined when the encoding is unknown. Similarly, .codes is not well-defined unless you know which canonicalization is in effect. Hence, both methods allow an optional argument to specify the meaning exactly if it cannot be known from context.)
From t/spec/S02-builtin_data_types/unicode.t lines 6–31 (no results): (skip)
| #L<S02/"Built-In Data Types"/".bytes, .codes or .graphs">
|
|
|
| # LATIN CAPITAL LETTER A, COMBINING GRAVE ACCENT
|
| my Str $u = "\x[0041,0300]";
|
| ok $u does utf8, 'can specify Str is utf8';
|
| is $u.bytes, 3, 'combining À is three bytes as utf8';
|
| is $u.codes, 2, 'combining À is two codes';
|
| is $u.graphs, 1, 'combining À is one graph';
|
| is "foo\r\nbar".codes, 8, 'CRLF is 2 codes';
|
| is "foo\r\nbar".graphs, 7, 'CRLF is 1 graph';
|
|
|
| # Speculation, .chars is unspecced, also use Bytes etc.
|
| is $u.chars, 1, '.chars defaults to .graphs';
|
|
|
| #?pugs 10 todo ''
|
| use_ok 'Bytes', 'use bytes works';
|
| is $u.chars, 3, '.chars as bytes';
|
| use_ok 'Codes', 'use codes works';
|
| is $u.chars, 2, '.chars as codes';
|
| use_ok 'Graphs', 'use graphs works';
|
| is $u.chars, 1, '.chars as graphs';
|
| ok $u does utf16, 'can specify Str is utf16';
|
| is $u.bytes, 4, '.bytes in utf16';
|
| ok $u does utf32, 'can specify Str is utf32';
|
| is $u.bytes, 8, '.bytes in utf32';
|
|
|
Highlighted:
small|full
There is no .length method for either arrays or strings, because length does not specify a unit.
- Built-in object types start with an uppercase letter. This includes immutable types (e.g.
Int, Num, Complex, Rat, Str, Bit, Regex, Set, Block, List, Seq), as well as mutable (container) types, such as Scalar, Array, Hash, Buf, Routine, Module, and non-instantiable Roles such as Callable, Failure, and Integral.
From t/spec/S02-builtin_data_types/declare.t lines 9–505 (no results): (skip)
| # L<S02/"Built-In Data Types"/"Built-in object types start with an uppercase letter">
|
|
|
| # immutable types (e.g. Int, Num, Complex, Rat, Str, Bit, Regex, Set, Block, List, Seq)
|
|
|
| {
|
| my Int $namcu =2;
|
| isa_ok($namcu,Int);
|
| }
|
|
|
| {
|
| my Num $namcu =1.1;
|
| isa_ok($namcu,Num);
|
| }
|
|
|
| # Type mismatch in assignment; expected something matching type Complex but got something of type Num()
|
|
|
| #?rakudo skip 'Complex not type converted properly during assignment from Num'
|
| {
|
| my Complex $namcu =1.3;
|
| isa_ok($namcu,Complex);
|
| }
|
|
|
| #?rakudo skip 'Rat not implemented'
|
| {
|
| my Rat $namcu = 7 div 4;
|
| isa_ok($namcu,Rat);
|
| }
|
|
|
| {
|
| my Str $lerpoi = "broda";
|
| isa_ok($lerpoi,Str);
|
| }
|
|
|
| #?rakudo skip 'Bit not implemented'
|
| {
|
| my Bit $namcu =1;
|
| isa_ok($namcu,Bit);
|
| }
|
|
|
| {
|
| my Regex $morna;
|
| isa_ok($morna, Regex);
|
| }
|
|
|
| #?rakudo skip 'Set not implemented'
|
| {
|
| my Set $selcmima;
|
| isa_ok($selcmima, Set);
|
| }
|
|
|
| {
|
| my Block $broda;
|
| isa_ok($broda, Block);
|
| }
|
|
|
| {
|
| my List $liste;
|
| isa_ok($liste, List);
|
| }
|
|
|
| #?rakudo skip 'Seq not implemented'
|
| {
|
| my Seq $porsi;
|
| isa_ok($porsi, Seq);
|
| }
|
|
|
|
|
| # mutable (container) types, such as Scalar, Array, Hash, Buf, Routine, Module
|
| # Buf nacpoi
|
|
|
| #?rakudo skip 'Scalar not implemented'
|
| {
|
| my Scalar $brode;
|
| isa_ok($brode, Scalar);
|
| }
|
|
|
| {
|
| my Array $porsi;
|
| isa_ok($porsi, Array);
|
| }
|
|
|
| {
|
| my Hash $brodi;
|
| isa_ok($brodi, Hash);
|
| }
|
|
|
| #?rakudo skip 'Buf not implemented'
|
| {
|
| my Buf $nacpoi;
|
| isa_ok($nacpoi, Buf);
|
| }
|
|
|
| {
|
| my Routine $gunka;
|
| isa_ok($gunka, Routine);
|
| }
|
|
|
| {
|
| my Module $brodu;
|
| isa_ok($brodu, Module);
|
| }
|
|
|
|
|
| # non-instantiable Roles such as Callable, Failure, and Integral
|
|
|
| {
|
| my Callable $fancu ;
|
| isa_ok($fancu,Callable);
|
| }
|
|
|
| #?rakudo skip 'Integral not implemented'
|
| {
|
| my Integral $foo;
|
| isa_ok($foo,Integral);
|
| }
|
|
|
|
|
| # Non-object (native) types are lowercase: int, num, complex, rat, buf, bit.
|
|
|
| #?rakudo skip 'int not implemented'
|
| {
|
| my int $namcu =2;
|
| isa_ok($namcu,int);
|
| }
|
|
|
| #?rakudo skip 'num not implemented'
|
| {
|
| my num $namcu =1.1;
|
| isa_ok($namcu,num);
|
| }
|
|
|
| # Type mismatch in assignment; expected something matching type Complex but got something of type Num()
|
|
|
| #?rakudo skip 'complex not implemented'
|
| {
|
| my complex $namcu =1.3;
|
| isa_ok($namcu,complex);
|
| }
|
|
|
| #?rakudo skip 'rat not implemented'
|
| {
|
| my rat $namcu = 7 div 4;
|
| isa_ok($namcu,rat);
|
| }
|
|
|
| #?rakudo skip 'bit not implemented'
|
| {
|
| my bit $namcu =1;
|
| isa_ok($namcu,bit);
|
| }
|
|
|
| #?rakudo skip 'buf not implemented'
|
| {
|
| my buf $nacpoi;
|
| isa_ok($nacpoi, buf);
|
| }
|
|
|
| # junction StrPos StrLen uint Nil Whatever Object Failure
|
| # Exception Range Bag Signature Capture Blob Instant Duration
|
| # Keyhash KeySet KeyBag Pair Mapping IO Routine Sub Method
|
| # Submethod Macro Match Package Module Class Role Grammar Any
|
|
|
| #?rakudo skip 'junction not implemented'
|
| {
|
| my junction $sor;
|
| isa_ok($sor, junction);
|
| }
|
|
|
| #?rakudo skip 'StrPos not implemented'
|
| {
|
| my StrPos $pa;
|
| isa_ok($pa,StrPos );
|
| }
|
|
|
|
|
| #?rakudo skip 'StrLen not implemented'
|
| {
|
| my StrLen $re;
|
| isa_ok($re,StrLen );
|
| }
|
|
|
| {
|
| my Nil $ci;
|
| isa_ok($ci,Nil );
|
| }
|
|
|
| {
|
| my Whatever $vo;
|
| isa_ok($vo,Whatever );
|
| }
|
|
|
| #?rakudo skip 'Object not working'
|
| {
|
| my Object $mu;
|
| isa_ok($mu,Object );
|
| }
|
|
|
| {
|
| my Failure $xa;
|
| isa_ok($xa,Failure );
|
| }
|
|
|
| {
|
| my Exception $ze;
|
| isa_ok($ze,Exception );
|
| }
|
|
|
| {
|
| my Range $bi;
|
| isa_ok($bi,Range );
|
| }
|
|
|
| #?rakudo skip 'Bag not implemented'
|
| {
|
| my Bag $so;
|
| isa_ok($so,Bag );
|
| }
|
|
|
| {
|
| my Signature $pano;
|
| isa_ok($pano,Signature );
|
| }
|
|
|
| {
|
| my Capture $papa;
|
| isa_ok($papa,Capture );
|
| }
|
|
|
| #?rakudo skip 'Blob not implemented'
|
| {
|
| my Blob $pare;
|
| isa_ok($pare,Blob );
|
| }
|
|
|
| #?rakudo skip 'Instant not implemented'
|
| {
|
| my Instant $paci;
|
| isa_ok($paci,Instant );
|
| }
|
|
|
| #?rakudo skip 'Duration not implemented'
|
| {
|
| my Duration $pavo;
|
| isa_ok($pavo,Duration );
|
| }
|
|
|
| #?rakudo skip 'KeyHash not implemented'
|
| {
|
| my KeyHash $pamu;
|
| isa_ok($pamu,KeyHash );
|
| }
|
|
|
| #?rakudo skip 'KeySet not implemented'
|
| {
|
| my KeySet $paxa;
|
| isa_ok($paxa,KeySet );
|
| }
|
|
|
| #?rakudo skip 'KeyBag not implemented'
|
| {
|
| my KeyBag $paze;
|
| isa_ok($paze,KeyBag );
|
| }
|
|
|
| {
|
| my Pair $pabi;
|
| isa_ok($pabi,Pair );
|
| }
|
|
|
| {
|
| my Mapping $paso;
|
| isa_ok($paso,Mapping );
|
| }
|
|
|
| {
|
| my IO $reno;
|
| isa_ok($reno,IO );
|
| }
|
|
|
| {
|
| my Routine $repa;
|
| isa_ok($repa,Routine );
|
| }
|
|
|
| {
|
| my Sub $rere;
|
| isa_ok($rere, Sub );
|
| }
|
|
|
| {
|
| my sub bar() { say 'blah' };
|
| my Sub $rr = &bar;
|
| isa_ok($rr, Sub );
|
| }
|
|
|
| #?rakudo skip 'Sub Cannot handle typed variables with sigil &'
|
| {
|
| my sub baz() { return 1;};
|
| my sub bar() { return baz;} ;
|
| my &foo := &bar;
|
| is(&foo(), 1,'nested sub call');
|
| }
|
|
|
| {
|
| my sub baz() { return 1;};
|
| my sub bar() { return baz;} ;
|
| my $foo = &bar;
|
| is($($foo()), 1, 'nested sub call');
|
| }
|
|
|
|
|
| {
|
| my Method $reci;
|
| isa_ok($reci, Method );
|
| }
|
|
|
| {
|
| my Submethod $revo;
|
| isa_ok($revo, Submethod );
|
| }
|
|
|
| #?rakudo skip 'Macro not implemented'
|
| {
|
| my Macro $remu;
|
| isa_ok($remu,Macro );
|
| }
|
|
|
| {
|
| my Match $rexa;
|
| isa_ok($rexa,Match );
|
| }
|
|
|
| #?rakudo skip 'Package not implemented'
|
| {
|
| my Package $reze;
|
| isa_ok($reze,Package );
|
| }
|
|
|
| {
|
| my Module $rebi;
|
| isa_ok($rebi,Module );
|
| }
|
|
|
| #?rakudo skip 'Class not implemented'
|
| {
|
| my Class $reso;
|
| isa_ok($reso,Class );
|
| }
|
|
|
| #?rakudo skip 'Role causing Null PMC access in get_string()'
|
| {
|
| my Role $cino;
|
| isa_ok($cino, Role );
|
| }
|
|
|
| {
|
| my Grammar $cire;
|
| isa_ok($cire,Grammar );
|
| }
|
|
|
| {
|
| my Any $civo;
|
| isa_ok($civo, Any );
|
| }
|
|
|
| # http://svn.pugscode.org/pugs/src/perl6/CORE.pad had list of types pugs supports
|
|
|
| {
|
| my Bool $jetfu;
|
| isa_ok($jetfu, Bool);
|
| }
|
|
|
| {
|
| my Order $karbi;
|
| isa_ok($karbi, Order);
|
| }
|
|
|
| #?rakudo skip 'Matcher isa not implemented'
|
| {
|
| my Matcher $mapti;
|
| isa_ok($mapti, Matcher);
|
| }
|
|
|
| #?rakudo skip 'Proxy not implemented'
|
| {
|
| my Proxy $krati;
|
| isa_ok($krati, Proxy);
|
| }
|
|
|
| # CharLingua Byte Char AnyChar
|
|
|
| #?rakudo skip 'Char not implemented'
|
| {
|
| my Char $pav;
|
| isa_ok($pav, Char);
|
| }
|
|
|
| #?rakudo skip 'Byte not implemented'
|
| {
|
| my Byte $biv;
|
| isa_ok($biv, Byte);
|
| }
|
|
|
| #?rakudo skip 'AnyChar not implemented'
|
| {
|
| my AnyChar $lerfu;
|
| isa_ok($lerfu, AnyChar);
|
| }
|
|
|
| #?rakudo skip 'CharLingua not implemented'
|
| {
|
| my CharLingua $lerfu;
|
| isa_ok($lerfu, CharLingua );
|
| }
|
|
|
| #?rakudo skip 'Codepoint not implemented'
|
| {
|
| my Codepoint $cypy;
|
| isa_ok($cypy,Codepoint );
|
| }
|
|
|
| #?rakudo skip 'Grapheme not implemented'
|
| {
|
| my Grapheme $gy;
|
| isa_ok($gy,Grapheme );
|
| }
|
|
|
| # Positional Associative Abstraction Ordering Ordered
|
| # KeyExtractor Comparator OrderingPair HyperWhatever
|
|
|
| {
|
| my Positional $mokca;
|
| ok($mokca ~~ Positional,'Positional exists');
|
| }
|
|
|
| {
|
| my Associative $kansa;
|
| ok($kansa ~~ Associative,'Associative exists');
|
| }
|
|
|
| {
|
| my Abstraction $sucta;
|
| ok($sucta ~~ Abstraction,'Abstraction exists');
|
| }
|
|
|
| #?rakudo skip 'Ordering not implemented'
|
| {
|
| my Ordering $foo;
|
| isa_ok($foo,Ordering);
|
| }
|
|
|
| #?rakudo skip 'KeyExtractor not implemented'
|
| {
|
| my KeyExtractor $ckiku;
|
| isa_ok($ckiku, KeyExtractor);
|
| }
|
|
|
| # KeyExtractor Comparator OrderingPair HyperWhatever
|
|
|
| #?rakudo skip 'Comparator not implemented'
|
| {
|
| my Comparator $bar;
|
| isa_ok($bar,Comparator);
|
| }
|
|
|
| #?rakudo skip 'OrderingPair not implemented'
|
| {
|
| my OrderingPair $foop;
|
| isa_ok($foop,OrderingPair);
|
| }
|
|
|
| #?rakudo skip 'HyperWhatever not implemented'
|
| {
|
| my HyperWhatever $baz;
|
| isa_ok($baz,HyperWhatever);
|
| }
|
|
|
| # utf8 utf16 utf32
|
|
|
| #?rakudo skip 'utf8 not implemented'
|
| {
|
| my utf8 $ubi;
|
| isa_ok($ubi,utf8);
|
| }
|
|
|
| #?rakudo skip 'utf16 not implemented'
|
| {
|
| my utf16 $upaxa;
|
| isa_ok($upaxa,utf16);
|
| }
|
|
|
| #?rakudo skip 'utf32 not implemented'
|
| {
|
| my utf32 $ucire;
|
| isa_ok($ucire,utf32);
|
| }
|
|
|
Highlighted:
small|full
Non-object (native) types are lowercase: int, num, complex, rat, buf, bit. Native types are primarily intended for declaring compact array storage. However, Perl will try to make those look like their corresponding uppercase types if you treat them that way. (In other words, it does autoboxing. Note, however, that sometimes repeated autoboxing can slow your program more than the native type can speed it up.)
The junction type is considered a native type because its internal representation is fixed, and you may not usefully derive from it because the intent of junctions is to autothread any method calls on them.
Some object types can behave as value types. Every object can produce a "WHICH" value that uniquely identifies the object for hashing and other value-based comparisons. Normal objects just use their address in memory, but if a class wishes to behave as a value type, it can define a .WHICH method that makes different objects look like the same object if they happen to have the same contents.
- Variables with non-native types can always contain undefined values, such as
Object, Whatever and Failure objects. See S04 for more about failures (i.e. unthrown exceptions):
my Int $x = undef; # works
Variables with native types do not support undefinedness: it is an error to assign an undefined value to them:
From t/spec/S02-builtin_data_types/type.t lines 41–51 (no results): (skip)
| # L<S02/Built-In Data Types/Variables with native types do not support undefinedness>
|
| #?rakudo skip 'native types (causes false positives if marked with todo)'
|
| {
|
| eval_lives_ok('my int $alpha = 1', 'Has native type int');
|
| eval_dies_ok('my int $alpha = undef', 'native int type cannot be undef');
|
| lives_ok({my Int $beta = undef}, 'object Int type can be undef');
|
| eval_lives_ok('my num $alpha = 1', 'Has native type num');
|
| eval_dies_ok('my num $alpha = undef', 'native num type cannot be undef');
|
| lives_ok({my Num $beta = undef}, 'object Num type can be undef');
|
| }
|
|
|
Highlighted:
small|full
my int $y = undef; # dies
Conjecture: num might support the autoconversion of undef to NaN, since the floating-point form can represent this concept. Might be better to make that conversion optional though, so that the rocket designer can decide whether to self-destruct immediately or shortly thereafter.
Variables of non-native types start out containing an undefined value unless explicitly initialized to a defined value.
- Every object supports a
HOW function/method that returns the metaclass instance managing it, regardless of whether the object is defined:
'x'.HOW.methods; # get available methods for strings
Str.HOW.methods; # same thing with the prototype object Str
HOW(Str).methods; # same thing as function call
'x'.methods; # this is likely an error - not a meta object
Str.methods; # same thing
(For a prototype system (a non-class-based object system), all objects are merely managed by the same meta object.)
- Perl 6 intrinsically supports big integers and rationals through its system of type declarations.
Int automatically supports promotion to arbitrary precision, as well as holding Inf and NaN values. Note that Int assumes 2's complement arithmetic, so +^1 == -2 is guaranteed. (Native int operations need not support this on machines that are not natively 2's complement. You must convert to and from Int to do portable bitops on such ancient hardware.)
From t/spec/S02-builtin_data_types/num.t lines 5–19 (no results): (skip)
| #L<S02/Built-In Data Types/Perl intrinsically supports big integers>
|
|
|
| plan 48;
|
| {
|
| my $a = 1; "$a";
|
| isa_ok($a, Int);
|
| is($a, "1", '1 stringification works');
|
| }
|
|
|
| {
|
| my $a = -1; "$a";
|
| isa_ok($a, Int);
|
| is($a, "-1", '-1 stringification works');
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/num.t lines 64–92 (no results): (skip)
| #L<S02/Built-In Data Types/Perl intrinsically supports big integers>
|
|
|
| {
|
| my $a = 0b100; "$a";
|
| isa_ok($a, Int);
|
| is($a, "4", '0b100 (binary) stringification works');
|
| }
|
|
|
| {
|
| my $a = 0x100; "$a";
|
| isa_ok($a, Int);
|
| is($a, "256", '0x100 (hex) stringification works');
|
| }
|
|
|
| {
|
| my $a = 0o100; "$a";
|
| isa_ok($a, Int);
|
| is($a, "64", '0o100 (octal) stringification works');
|
| }
|
|
|
| {
|
| my $a = 1; "$a";
|
| is($a + 1, 2, 'basic addition works');
|
| }
|
|
|
| {
|
| my $a = -1; "$a";
|
| ok($a + 1 == 0, 'basic addition with negative numbers works'); # parsing bug
|
| }
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/num.t lines 125–132 (no results): (skip)
| #L<S02/Built-In Data Types/Perl intrinsically supports big integers>
|
|
|
| {
|
| my $a = "1.01";
|
| isa_ok(int($a), "Int");
|
| is(int($a), 1, "1.01 intifies to 1");
|
| }
|
|
|
Highlighted:
small|full
(Num may support arbitrary-precision floating-point arithmetic, but is not required to unless we can do so portably and efficiently. Num must support the largest native floating point format that runs at full speed.)
From t/spec/S02-builtin_data_types/num.t lines 111–124 (no results): (skip)
| #L<S02/Built-In Data Types/Num may support arbitrary-precision floating-point>
|
|
|
| {
|
| my $a = "1.01";
|
| isa_ok(+$a, "Num");
|
| is(+$a, 1.01, "1.01 numifies to 1.01");
|
| }
|
|
|
| {
|
| my $a = "0d01.01";
|
| isa_ok(+$a, "Num");
|
| is(+$a, 1, "0d01.01 numifies to 1");
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/num.t lines 133–155 (no results): (skip)
| #L<S02/Built-In Data Types/Num may support arbitrary-precision floating-point>
|
|
|
| {
|
| my $a = "0d0101";
|
| isa_ok(+$a, "Num");
|
| is(+$a, 101, "0d0101 numifies to 101");
|
| }
|
|
|
| {
|
| my $a = 2 ** 65; # over the 64 bit limit too
|
| is($a, 36893488147419103232, "we have bignums, not weeny floats");
|
| }
|
|
|
| is(42_000, 42000, 'underscores allowed (and ignored) in numeric literals');
|
| is(42_127_000, 42127000, 'multiple underscores ok');
|
| is(42.0_1, 42.01, 'underscores in fraction ok');
|
| is(4_2.01, 42.01, 'underscores in whole part ok');
|
|
|
| eval_dies_ok('4_2._0_1', 'single underscores are not ok directly after the dot');
|
| is(4_2.0_1, 42.01, 'single underscores are ok');
|
|
|
| is 0_1, 1, "0_1 is parsed as 0d1";
|
| is +^1, -2, '+^1 == -2 as promised';
|
Highlighted:
small|full
Rat supports arbitrary precision rational arithmetic. However, dividing two Int objects using infix:</> produces a fraction of Num type, not a ratio. You can produce a ratio by using infix:<div> on two integers instead.
From t/spec/S02-builtin_data_types/num.t lines 20–63 (no results): (skip)
| #L<S02/Built-In Data Types/Rat supports arbitrary precision rational arithmetic>
|
| #?rakudo skip "no Rat yet"
|
| {
|
| my $a = 1 div 1;
|
| isa_ok($a, Rat);
|
| is($a, "1", '1.0 stringification works');
|
| }
|
|
|
| {
|
| my $a = -1.0;
|
| isa_ok($a, Num);
|
| is($a, "-1", '-1 stringification works');
|
| }
|
|
|
| {
|
| my $a = 0.1;
|
| isa_ok($a, Num);
|
| is($a, "0.1", '0.1 stringification works');
|
| }
|
|
|
| {
|
| my $a = -0.1; "$a";
|
| isa_ok($a, Num);
|
| is($a, "-0.1", '-0.1 stringification works');
|
| }
|
|
|
| {
|
| my $a = 10.01; "$a";
|
| isa_ok($a, Num);
|
| is($a, "10.01", '10.01 stringification works');
|
| }
|
|
|
| {
|
| my $a = 1e3; "$a";
|
| ok $a ~~ Num, '1e3 conforms to Num';
|
| is($a, "1000", '1e3 stringification works');
|
| }
|
|
|
| {
|
| my $a = 10.01e3; "$a";
|
| isa_ok($a, Num);
|
| is($a, "10010", '10.01e3 stringification works');
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/num.t lines 93–110 (no results): (skip)
| #L<S02/Built-In Data Types/Rat supports arbitrary precision rational arithmetic>
|
|
|
| #?rakudo skip 'Rat, infix:<div>'
|
| isa_ok(1 div 1, Rat);
|
|
|
| {
|
| my $a = 80000.0000000000000000000000000;
|
| isa_ok($a, Num);
|
| ok($a == 80000.0, 'trailing zeros compare correctly');
|
| }
|
|
|
| {
|
| my $a = 1.0000000000000000000000000000000000000000000000000000000000000000000e1;
|
| #?rakudo skip "no Rat yet"
|
| isa_ok($a, Rat);
|
| ok($a == 10.0, 'trailing zeros compare correctly');
|
| }
|
|
|
Highlighted:
small|full
Lower-case types like int and num imply the native machine representation for integers and floating-point numbers, respectively, and do not promote to arbitrary precision, though larger representations are always allowed for temporary values. Unless qualified with a number of bits, int and num types represent the largest native integer and floating-point types that run at full speed.
Numeric values in untyped variables use Int and Num semantics rather than int and num.
- Perl 6 should by default make standard IEEE floating point concepts visible, such as
Inf (infinity) and NaN (not a number). Within a lexical scope, pragmas may specify the nature of temporary values, and how floating point is to behave under various circumstances. All IEEE modes must be lexically available via pragma except in cases where that would entail heroic efforts to bypass a braindead platform.
From t/spec/S02-builtin_data_types/infinity.t lines 5–40 (no results): (skip)
| # L<S02/"Built-In Data Types" /Perl 6 should by default make standard IEEE floating point concepts visible>
|
|
|
| {
|
| my $x = Inf;
|
|
|
| ok( $x == Inf , 'numeric equal');
|
| ok( $x eq 'Inf', 'string equal');
|
| }
|
|
|
| {
|
| my $x = -Inf;
|
| ok( $x == -Inf, 'negative numeric equal' );
|
| ok( $x eq '-Inf', 'negative string equal' );
|
| }
|
|
|
| #?rakudo todo 'integer Inf'
|
| {
|
| my $x = int( Inf );
|
| ok( $x == Inf, 'int numeric equal' );
|
| ok( $x eq 'Inf', 'int string equal' );
|
| }
|
|
|
| #?rakudo todo 'integer Inf'
|
| {
|
| my $x = int( -Inf );
|
| ok( $x == -Inf, 'int numeric equal' );
|
| ok( $x eq '-Inf', 'int string equal' );
|
| }
|
|
|
| # Inf should == Inf. Additionally, Inf's stringification (~Inf), "Inf", should
|
| # eq to the stringification of other Infs.
|
| # Thus:
|
| # Inf == Inf # true
|
| # and:
|
| # Inf eq Inf # same as
|
| # ~Inf eq ~Inf # true
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/nan.t lines 9–21 (no results): (skip)
| # L<S02/"Built-In Data Types" /Perl 6 should by default make standard IEEE floating point concepts visible>
|
|
|
| is 0 * Inf , NaN, "0 * Inf";
|
| is Inf / Inf, NaN, "Inf / Inf";
|
| is Inf - Inf, NaN, "Inf - Inf";
|
| # if we say that 0**0 and Inf**0 both give 1 (sse below), then for which
|
| # number or limit whould $number ** 0 be different from 1? so maybe just say
|
| # that NaN ** 0 == 1?
|
| #?rakudo skip 'unspecced and inconsistent'
|
| is NaN ** 0, NaN, "NaN ** 0";
|
|
|
| is 0**0 , 1, "0**0 is 1, _not_ NaN";
|
| is Inf**0 , 1, "Inf**0 is 1, _not_ NaN";
|
Highlighted:
small|full
The default floating-point modes do not throw exceptions but rather propagate Inf and NaN. The boxed object types may carry more detailed information on where overflow or underflow occurred. Numerics in Perl are not designed to give the identical answer everywhere. They are designed to give the typical programmer the tools to achieve a good enough answer most of the time. (Really good programmers may occasionally do even better.) Mostly this just involves using enough bits that the stupidities of the algorithm don't matter much.
- A
Str is a Unicode string object. There is no corresponding native str type. However, since a Str object may fill multiple roles, we say that a Str keeps track of its minimum and maximum Unicode abstraction levels, and plays along nicely with the current lexical scope's idea of the ideal character, whether that is bytes, codepoints, graphemes, or characters in some language. For all builtin operations, all Str positions are reported as position objects, not integers. These StrPos objects point into a particular string at a particular location independent of abstraction level, either by tracking the string and position directly, or by generating an abstraction-level independent representation of the offset from the beginning of the string that will give the same results if applied to the same string in any context. This is assuming the string isn't modified in the meanwhile; a StrPos is not a "marker" and is not required to follow changes to a mutable string. For instance, if you ask for the positions of matches done by a substitution, the answers are reported in terms of the original string (which may now be inaccessible!), not as positions within the modified string.
The subtraction of two StrPos objects gives a StrLen object, which is also not an integer, because the string between two positions also has multiple integer interpretations depending on the units. A given StrLen may know that it represents 18 bytes, 7 codepoints, 3 graphemes, and 1 letter in Malayalam, but it might only know this lazily because it actually just hangs onto the two StrPos endpoints within the string that in turn may or may not just lazily point into the string. (The lazy implementation of StrLen is much like a Range object in that respect.)
If you use integers as arguments where position objects are expected, it will be assumed that you mean the units of the current lexically scoped Unicode abstraction level. (Which defaults to graphemes.) Otherwise you'll need to coerce to the proper units:
From t/spec/S02-builtin_data_types/unicode.t lines 32–74 (no results): (skip)
| #L<S02/"Built-In Data Types"/"coerce to the proper units">
|
| $u = "\x[41,
|
| E1,
|
| 41, 0300,
|
| 41, 0302, 0323,
|
| E0]";
|
|
|
| # $u does utf8 etc. is conjectural(?)
|
| $u does utf8;
|
| #?pugs 9 todo ''
|
| is eval('substr $u, 3.as(Bytes), 1.as(Bytes)'), "\x[41]", 'substr with Bytes as units - utf8';
|
| is eval('substr $u, 3.as(Codes), 1.as(Codes)'), "\x[0300]", 'substr with Codes as units - utf8';
|
| is eval('substr $u, 4.as(Graphs), 1.as(Graphs)'), "\x[E0]", 'substr with Graphs as units - utf8';
|
| is eval('substr $u, 3.as(Graphs), 1.as(Codes)'), "\x[41]", 'substr with Graphs and Codes as units 1 - utf8';
|
| is eval('substr $u, 4.as(Codes), 1.as(Graphs)'), "\x[41, 0302, 0323]", 'substr with Graphs and Codes as units 2 - utf8';
|
| is eval('substr $u, 4.as(Bytes), 1.as(Codes)'), "\x[0300]", 'substr with Bytes and Codes as units 1 - utf8';
|
| is eval('substr $u, 1.as(Codes), 2.as(Bytes)'), "\x[E1]", 'substr with Bytes and Codes as units 2 - utf8';
|
| is eval('substr $u, 3.as(Bytes), 1.as(Graphs)'), "\x[41, 0300]", 'substr with Bytes and Graphs as units 1 - utf8';
|
| is eval('substr $u, 3.as(Graphs), 1.as(Bytes)'), "\x[41]", 'substr with Bytes and Graphs as units 2 - utf8';
|
|
|
| $u does utf16;
|
| #?pugs 9 todo ''
|
| is eval('substr $u, 4.as(Bytes), 2.as(Bytes)'), "\x[41]", 'substr with Bytes as units - utf16';
|
| is eval('substr $u, 3.as(Codes), 1.as(Codes)'), "\x[0300]", 'substr with Codes as units - utf16';
|
| is eval('substr $u, 4.as(Graphs), 1.as(Graphs)'), "\x[E0]", 'substr with Graphs as units - utf16';
|
| is eval('substr $u, 3.as(Graphs), 1.as(Codes)'), "\x[41]", 'substr with Graphs and Codes as units 1 - utf16';
|
| is eval('substr $u, 4.as(Codes), 1.as(Graphs)'), "\x[41, 0302, 0323]", 'substr with Graphs and Codes as units 2 - utf16';
|
| is eval('substr $u, 6.as(Bytes), 1.as(Codes)'), "\x[0300]", 'substr with Bytes and Codes as units 1 - utf16';
|
| is eval('substr $u, 1.as(Codes), 2.as(Bytes)'), "\x[E1]", 'substr with Bytes and Codes as units 2 - utf16';
|
| is eval('substr $u, 4.as(Bytes), 1.as(Graphs)'), "\x[41, 0300]", 'substr with Bytes and Graphs as units 1 - utf16';
|
| is eval('substr $u, 3.as(Graphs), 2.as(Bytes)'), "\x[41]", 'substr with Bytes and Graphs as units 2 - utf16';
|
|
|
| $u does utf32;
|
| #?pugs 9 todo ''
|
| is eval('substr $u, 8.as(Bytes), 4.as(Bytes)'), "\x[41]", 'substr with Bytes as units - utf32';
|
| is eval('substr $u, 3.as(Codes), 1.as(Codes)'), "\x[0300]", 'substr with Codes as units - utf32';
|
| is eval('substr $u, 4.as(Graphs), 1.as(Graphs)'), "\x[E0]", 'substr with Graphs as units - utf32';
|
| is eval('substr $u, 3.as(Graphs), 1.as(Codes)'), "\x[41]", 'substr with Graphs and Codes as units 1 - utf32';
|
| is eval('substr $u, 4.as(Codes), 1.as(Graphs)'), "\x[41, 0302, 0323]", 'substr with Graphs and Codes as units 2 - utf32';
|
| is eval('substr $u, 12.as(Bytes), 1.as(Codes)'), "\x[0300]", 'substr with Bytes and Codes as units 1 - utf32';
|
| is eval('substr $u, 1.as(Codes), 4.as(Bytes)'), "\x[E1]", 'substr with Bytes and Codes as units 2 - utf32';
|
| is eval('substr $u, 8.as(Bytes), 1.as(Graphs)'), "\x[41, 0300]", 'substr with Bytes and Graphs as units 1 - utf32';
|
| is eval('substr $u, 3.as(Graphs), 4.as(Bytes)'), "\x[41]", 'substr with Bytes and Graphs as units 2 - utf32';
|
Highlighted:
small|full
substr($string, Bytes(42), ArabicChars(1))
Of course, such a dimensional number will fail if used on a string that doesn't provide the appropriate abstraction level.
If a StrPos or StrLen is forced into a numeric context, it will assume the units of the current Unicode abstraction level. It is erroneous to pass such a non-dimensional number to a routine that would interpret it with the wrong units.
Implementation note: since Perl 6 mandates that the default Unicode processing level must view graphemes as the fundamental unit rather than codepoints, this has some implications regarding efficient implementation. It is suggested that all graphemes be translated on input to a unique grapheme numbers and represented as integers within some kind of uniform array for fast substr access. For those graphemes that have a precomposed form, use of that codepoint is suggested. (Note that this means Latin-1 can still be represented internally with 8-bit integers.)
For graphemes that have no precomposed form, a temporary private id should be assigned that uniquely identifies the grapheme. If such ids are assigned consistently thoughout the process, comparison of two graphemes is no more difficult than the comparison of two integers, and comparison of base characters no more difficult than a direct lookup into the id-to-NFD table.
Obviously, any temporary grapheme ids must be translated back to some universal form (such as NFD) on output, and normal precomposed graphemes may turn into either NFC or NFD forms depending on the desired output. Maintaining a particular grapheme/id mapping over the life of the process may have some GC implications for long-running processes, but most processes will likely see a limited number of non-precomposed graphemes.
If the program has a scope that wants a codepoint view rather than a grapheme view, the string visible to that lexical scope must also be translated to universal form, just as with output translation. Alternately, the temporary grapheme ids may be hidden behind an abstraction layer. In any case, codepoint scope should never see any temporary grapheme ids. (The lexical codepoint declaration should probably specify which normalization form it prefers to view strings under. Such a declaration could be applied to input translation as well.)
- A
Buf is a stringish view of an array of integers, and has no Unicode or character properties without explicit conversion to some kind of Str. (A buf is the native counterpart.) Typically it's an array of bytes serving as a buffer. Bitwise operations on a Buf treat the entire buffer as a single large integer. Bitwise operations on a Str generally fail unless the Str in question can provide an abstract Buf interface somehow. Coercion to Buf should generally invalidate the Str interface. As a generic type Buf may be instantiated as (or bound to) any of buf8, buf16, or buf32 (or to any type that provides the appropriate Buf interface), but when used to create a buffer Buf defaults to buf8.
Unlike Str types, Buf types prefer to deal with integer string positions, and map these directly to the underlying compact array as indices. That is, these are not necessarily byte positions--an integer position just counts over the number of underlying positions, where one position means one cell of the underlying integer type. Builtin string operations on Buf types return integers and expect integers when dealing with positions. As a limiting case, buf8 is just an old-school byte string, and the positions are byte positions. Note, though, that if you remap a section of buf32 memory to be buf8, you'll have to multiply all your positions by 4.
- The
utf8 type is derived from buf8, with the additional constraint that it may only contain validly encoded UTF-8. Likewise, utf16 is derived from buf16, and utf32 from buf32.
Note that since these are type names, parentheses must always be used to call them as coercers, since the listop form is not allowed for coercions. That is:
utf8 op $x
is always parsed as
(utf8) op $x
and never as
utf8(op $x)
- The
* character as a standalone term captures the notion of "Whatever", which is applied lazily by whatever operator it is an argument to. Generally it can just be thought of as a "glob" that gives you everything it can in that argument position. For instance:
From t/spec/S02-builtin_data_types/whatever.t lines 6–13 (no results): (skip)
| # L<S02/Built-In Data Types/"The * character as a standalone term captures the notion of">
|
| # L<S02/Native types/"If any native type is explicitly initialized to">
|
|
|
| {
|
| my $x = *;
|
| ok($x.isa(Whatever), 'can assign * to a variable and isa works');
|
| }
|
|
|
Highlighted:
small|full
if $x ~~ 1..* {...} # if 1 <= $x <= +Inf
my ($a,$b,$c) = "foo" xx *; # an arbitrary long list of "foo"
if /foo/ ff * {...} # a latching flipflop
@slice = @x[*;0;*]; # any Int
@slice = %x{*;'foo'}; # any keys in domain of 1st dimension
@array[*] # flattens, unlike @array[]
(*, *, $x) = (1, 2, 3); # skip first two elements
# (same as lvalue "undef" in Perl 5)
Whatever is an undefined prototype object derived from Any. As a type it is abstract, and may not be instantiated as a defined object. If for a particular MMD dispatch, nothing in the MMD system claims it, it dispatches to as an Any with an undefined value, and usually blows up constructively. If you say
say 1 + *;
you should probably not expect it to yield a reasonable answer, unless you think an exception is reasonable. Since the Whatever object is effectively immutable, the optimizer is free to recognize * and optimize in the context of what operator it is being passed to.
Most of the built-in numeric operators treat an argument of * as indicating the desire to create a function of a single unknown, so:
From t/spec/S02-builtin_data_types/whatever.t lines 14–33 (no results): (skip)
| # L<S02/Built-In Data Types/"Most of the built-in numeric operators treat an argument of">
|
|
|
| my $x = *-1;
|
| lives_ok { $x.WHAT }, '(*-1).WHAT lives';
|
| isa_ok $x, Code, '*-1 is of type Code';
|
| is $x.(5), 4, 'and we can execute that Code';
|
|
|
| isa_ok *.abs, Code, '*.abs is of type Code';
|
| my @a = map *.abs, 1, -2, 3, -4;
|
| is @a, [1,2,3,4], '*.meth created closure works';
|
|
|
| {
|
| # check that it also works with Enums - used to be a Rakudo bug
|
| # RT #63880
|
| enum A <b c>;
|
| isa_ok (b < *), Code, 'Enums and Whatever star interact OK';
|
| }
|
|
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
* - 1
produces a function of a single argument:
{ $_ - 1 }
Likewise, the single dispatcher recognizes *.meth and returns { $_.meth }, so it can be used where patterns are expected:
@primes = grep *.prime, 2..*;
These closures are of type Code:($), not Whatever, so that constructs can distinguish via multiple dispatch:
1,2,3 ... *
1,2,3 ... *+1
The bare * form may also be called as a function, and represents the identify function:
*(42) == 42
(* + 1)(42) == 43
But note that this is not what is happening above, or
1,2,3 ... *
would end up meaning:
1,2,3,3,3,3,3,3...
The ... operator is instead dispatching bare * to a routine that does dwimmery, and in this case decides to supply a function { * + 1 }.
The final element of an array is subscripted as @a[*-1], which means that when the subscripting operation discovers a Code object for a subscript, it calls it and supplies an argument indicating the number of elements in (that dimension of) the array. See S09.
A variant of * is the ** term, which is of type HyperWhatever. It is generally understood to be a multidimension form of * when that makes sense. When modified by an operator that would turn * into a function of one argument, ** instead turns into a function with a slurpy argument, of type Code:(*@). That is:
* - 1 means -> $x { $x - 1 }
** - 1 means -> *@x { map -> $x { $x - 1 }, @x }
Therefore @array[^**] represents @array[{ map { ^* }, @_ }], that is to say, every element of the array, no matter how many dimensions. (However, @array[**] means the same thing because (as with ... above), the subscript operator will interpret bare ** as meaning all the subscripts, not the list of dimension sizes. The meaning of Whatever is always controlled by its immediate context.)
Other uses for * and ** will doubtless suggest themselves over time. These can be given meaning via the MMD system, if not the compiler. In general a Whatever should be interpreted as maximizing the degrees of freedom in a dwimmy way, not as a nihilistic "don't care anymore--just shoot me".
Values with these types autobox to their uppercase counterparts when you treat them as objects:
bit single native bit
int native signed integer
uint native unsigned integer (autoboxes to Int)
buf native buffer (finite seq of native ints or uints, no Unicode)
num native floating point
complex native complex number
bool native boolean
Since native types cannot represent Perl's concept of undefined values, in the absence of explicit initialization, native floating-point types default to NaN, while integer types (including bit) default to 0. The complex type defaults to NaN + NaN\i. A buf type of known size defaults to a sequence of 0 values. If any native type is explicitly initialized to * (the Whatever type), no initialization is attempted and you'll get whatever was already there when the memory was allocated.
From t/spec/S02-builtin_data_types/whatever.t lines 7–13 (no results): (skip)
| # L<S02/Native types/"If any native type is explicitly initialized to">
|
|
|
| {
|
| my $x = *;
|
| ok($x.isa(Whatever), 'can assign * to a variable and isa works');
|
| }
|
|
|
Highlighted:
small|full
If a buf type is initialized with a Unicode string value, the string is decomposed into Unicode codepoints, and each codepoint shoved into an integer element. If the size of the buf type is not specified, it takes its length from the initializing string. If the size is specified, the initializing string is truncated or 0-padded as necessary. If a codepoint doesn't fit into a buf's integer type, a parse error is issued if this can be detected at compile time; otherwise a warning is issued at run time and the overflowed buffer element is filled with an appropriate replacement character, either U+FFFD (REPLACEMENT CHARACTER) if the element's integer type is at least 16 bits, or U+007f (DELETE) if the larger value would not fit. If any other conversion is desired, it must be specified explicitly. In particular, no conversion to UTF-8 or UTF-16 is attempted; that must be specified explicitly. (As it happens, conversion to a buf type based on 32-bit integers produces valid UTF-32 in the native endianness.)
These can behave as values or objects of any class, except that defined always returns false. One can create them with the built-in undef and fail functions. (See S04 for how failures are handled.)
Nil Empty list viewed as an item
Object Uninitialized (derivatives serve as type objects)
Whatever Wildcard (like undef, but subject to do-what-I-mean via MMD)
Failure Failure (lazy exceptions, thrown if not handled properly)
Whenever you declare any kind of type, class, module, or package, you're automatically declaring a undefined prototype value with the same name.
Whenever a Failure value is put into a typed container, it takes on the type specified by the container but continues to carry the Failure role. (The undef function merely returns the most generic Failure object. Use fail to return more specific failures. Use Object for the most generic non-failure undefined value. The Any type is also undefined, but excludes junctions so that autothreading may be dispatched using normal multiple dispatch rules.)
The Nil type is officially undefined as an item but interpolates as a null list into list context, and an empty capture into slice context. A Nil object may also carry failure information, but if so, the object behaves as a failure only in item context. Use Failure/undef when you want to return a hard failure that will not evaporate in list context.
Objects with these types behave like values, i.e. $x === $y is true if and only if their types and contents are identical (that is, if $x.WHICH eqv $y.WHICH).
Bit Perl single bit (allows traits, aliasing, undef, etc.)
Int Perl integer (allows Inf/NaN, arbitrary precision, etc.)
Str Perl string (finite sequence of Unicode characters)
Num Perl number
Rat Perl rational
Complex Perl complex number
Bool Perl boolean
From t/spec/S02-builtin_data_types/parsing-bool.t lines 8–15 (no results): (skip)
| # L<S02/Immutable types/"Perl boolean">
|
|
|
| is (try { 42 or Bool::False }), 42, "Bool::False as RHS";
|
| #?pugs todo 'parsing'
|
| is (try { Bool::False or 42 }), 42, "Bool::False as LHS";
|
|
|
| is (try { 42 or False }), 42, "False as RHS";
|
| is (try { False or 42 }), 42, "False as LHS";
|
Highlighted:
small|full
Exception Perl exception
Block Executable objects that have lexical scopes
List Lazy Perl list (composed of immutables and iterators)
Seq Completely evaluated (hence immutable) sequence
Range A pair of Ordered endpoints
From t/spec/S02-builtin_data_types/range.t lines 8–40 (no results): (skip)
| # L<S02/Immutable types/A pair of Ordered endpoints>
|
|
|
| my $r = 1..5;
|
| isa_ok $r, Range, 'Type';
|
| is $r.WHAT, Range, 'Type';
|
| is $r.perl, '1..5', 'canonical representation';
|
|
|
| # XXX unspecced: exact value of Range.perl
|
| is (1..5).perl, '1..5', ".perl ..";
|
| is (1^..5).perl, '1^..5', ".perl ^..";
|
| is (1..^5).perl, '1..^5', ".perl ..^";
|
| is (1^..^5).perl, '1^..^5', ".perl ^..^";
|
|
|
| my @r = $r;
|
| is @r, [1, 2, 3, 4, 5], 'got the right array';
|
|
|
| # Range of Str
|
|
|
| $r = 'a'..'c';
|
| isa_ok $r, Range;
|
| # XXX unspecced: exact value of Range.perl
|
| is $r.perl, '"a".."c"', 'canonical representation';
|
| @r = $r;
|
| is @r, [< a b c >], 'got the right array';
|
|
|
| # Stationary ranges
|
| is (1..1).perl, '1..1', "stationary num .perl ..";
|
| is (1..1), [1,], 'got the right array';
|
| is ('a'..'a').perl, '"a".."a"', "stationary str .perl ..";
|
| is ('a'..'a'), [< a >], 'got the right array';
|
|
|
| # Decreasing Ranges - see S03-operators/range for boundry tests
|
| {
|
Highlighted:
small|full
Set Unordered collection of values that allows no duplicates
Bag Unordered collection of values that allows duplicates
Signature Function parameters (left-hand side of a binding)
Capture Function call arguments (right-hand side of a binding)
Blob An undifferentiated mass of bits
Instant A point on the continuous atomic timeline (TAI)
Duration The difference between two Instants
Insofar as Lists are lazy, they're really only partially immutable, in the sense that the past is fixed but the future is not. The portion of a List yet to be determined by iterators may depend on mutable values. When an iterator is called upon to iterate and extend the known part of the list, some number of immutable values (which includes immutable references to mutable objects) are decided and locked in at that point. Iterators may have several different ways of iterating depending on the degree of laziness/eagerness desired in context. The iterator API is described in S07.
Instants and Durations are measured in atomic seconds with fractions. Notionally they are real numbers which may be implemented in either Num or Rat types. (Fixed-point implementations are strongly discouraged.) Interfaces that take Duration arguments, such as sleep(), may also take Num arguments, but Instant arguments must be explicitly created via any of various culturally aware time specification APIs that, by and large, are outside the CORE of Perl 6, with the possible exception of a constructor taking a native TAI value. In numeric context a Duration happily returns a Num representing seconds. If pressed for a number, an Instant will return the length of time in atomic seconds from the TAI epoch, but it will be unhappy about it. Systems which cannot provide a steady time base, such as POSIX systems, will simply have to make their best guess as to the correct atomic time.
Objects with these types have distinct .WHICH values that do not change even if the object's contents change. (Routines are considered mutable because they can be wrapped in place.)
Scalar Perl scalar
Array Perl array
From t/spec/S02-builtin_data_types/array.t lines 7–231 (no results): (skip)
| #L<S02/Mutable types/Array>
|
|
|
| {
|
| my $i = 0;
|
| $i++ for 1, 2, 3;
|
| is $i, 3, 'for 1, 2, 3 does three iterations';
|
| }
|
|
|
|
|
| {
|
| my $i = 0;
|
| $i++ for (1, 2, 3).item;
|
| is $i, 3, 'for (1, 2, 3).item does three iterations';
|
| }
|
|
|
| {
|
| my $i = 0;
|
| $i++ for [1, 2, 3];
|
| is $i, 1, 'for [1, 2, 3] does one iteration';
|
| }
|
|
|
|
|
| # array of strings
|
|
|
| my @array1 = ("foo", "bar", "baz");
|
| isa_ok(@array1, Array);
|
|
|
| is(+@array1, 3, 'the array1 has 3 elements');
|
| is(@array1[0], 'foo', 'got the right value at array1 index 0');
|
| is(@array1[1], 'bar', 'got the right value at array1 index 1');
|
| is(@array1[2], 'baz', 'got the right value at array1 index 2');
|
|
|
|
|
| is(@array1.[0], 'foo', 'got the right value at array1 index 0 using the . notation');
|
|
|
|
|
| # array with strings, numbers and undef
|
| my @array2 = ("test", 1, undef);
|
| {
|
| isa_ok(@array2, Array);
|
|
|
| is(+@array2, 3, 'the array2 has 3 elements');
|
| is(@array2[0], 'test', 'got the right value at array2 index 0');
|
| is(@array2[1], 1, 'got the right value at array2 index 1');
|
| ok(!@array2[2].defined, 'got the right value at array2 index 2');
|
| }
|
|
|
| # combine 2 arrays
|
| {
|
| my @array3 = (@array1, @array2);
|
| isa_ok(@array3, Array);
|
|
|
| is(+@array3, 6, 'the array3 has 6 elements');
|
| is(@array3[0], 'foo', 'got the right value at array3 index 0');
|
| is(@array3[1], 'bar', 'got the right value at array3 index 1');
|
| is(@array3[2], 'baz', 'got the right value at array3 index 2');
|
| is(@array3[3], 'test', 'got the right value at array3 index 3');
|
| is(@array3[4], 1, 'got the right value at array3 index 4');
|
| ok(!@array3[5].defined,'got the right value at array3 index 5');
|
| }
|
|
|
| {
|
| # array slice
|
| my @array4 = @array2[2, 1, 0];
|
| isa_ok(@array4, Array);
|
|
|
| is(+@array4, 3, 'the array4 has 3 elements');
|
| ok(!defined(@array4[0]), 'got the right value at array4 index 0');
|
| is(@array4[1], 1, 'got the right value at array4 index 1');
|
| is(@array4[2], 'test', 'got the right value at array4 index 2');
|
| }
|
|
|
| {
|
| # create new array with 2 array slices
|
| my @array5 = ( @array2[2, 1, 0], @array1[2, 1, 0] );
|
| isa_ok(@array5, Array);
|
|
|
| is(+@array5, 6, 'the array5 has 6 elements');
|
| ok(!defined(@array5[0]), 'got the right value at array5 index 0');
|
| is(@array5[1], 1, 'got the right value at array5 index 1');
|
| is(@array5[2], 'test', 'got the right value at array5 index 2');
|
| is(@array5[3], 'baz', 'got the right value at array5 index 3');
|
| is(@array5[4], 'bar', 'got the right value at array5 index 4');
|
| is(@array5[5], 'foo', 'got the right value at array5 index 5');
|
| }
|
|
|
| {
|
| # create an array slice with an array (in a variable)
|
|
|
| my @slice = (2, 0, 1);
|
| my @array6 = @array1[@slice];
|
| isa_ok(@array6, Array);
|
|
|
| is(+@array6, 3, 'the array6 has 3 elements');
|
| is(@array6[0], 'baz', 'got the right value at array6 index 0');
|
| is(@array6[1], 'foo', 'got the right value at array6 index 1');
|
| is(@array6[2], 'bar', 'got the right value at array6 index 2');
|
| }
|
|
|
| {
|
| # create an array slice with an array constructed with ()
|
| my @array7 = @array1[(2, 1, 0)];
|
| isa_ok(@array7, Array);
|
|
|
| is(+@array7, 3, 'the array7 has 3 elements');
|
| is(@array7[0], 'baz', 'got the right value at array7 index 0');
|
| is(@array7[1], 'bar', 'got the right value at array7 index 1');
|
| is(@array7[2], 'foo', 'got the right value at array7 index 2');
|
| }
|
|
|
| {
|
| # odd slices
|
| my $result1 = (1, 2, 3, 4)[1];
|
| is($result1, 2, 'got the right value from the slice');
|
|
|
| my $result2 = [1, 2, 3, 4][2];
|
| is($result2, 3, 'got the right value from the slice');
|
| }
|
|
|
| # swap two elements test moved to t/op/assign.t
|
|
|
| # empty arrays
|
| {
|
| my @array9;
|
| isa_ok(@array9, Array);
|
| is(+@array9, 0, "new arrays are empty");
|
|
|
| my @array10 = (1, 2, 3,);
|
| is(+@array10, 3, "trailing commas make correct array");
|
| }
|
|
|
| #?pugs skip "multi-dim arrays not implemented"
|
| #?rakudo skip "multi-dim arrays"
|
| {
|
| # declare a multidimension array
|
| eval_lives_ok('my @multidim[0..3; 0..1]', "multidimension array");
|
| my @array11 is shape(2,4);
|
|
|
| # XXX what should that test actually do?
|
| ok(eval('@array11[2,0] = 12'), "push the value to a multidimension array");
|
| }
|
|
|
| {
|
| # declare the array with data type
|
| my Int @array;
|
| lives_ok { @array[0] = 23 }, "stuffing Ints in an Int array works";
|
| dies_ok { @array[1] = $*ERR }, "stuffing IO in an Int array does not work";
|
| }
|
|
|
| #?pugs skip "no whatever star yet"
|
| {
|
| my @array12 = ('a', 'b', 'c', 'e');
|
|
|
| # indexing from the end
|
| is @array12[*-1],'e', "indexing from the end [*-1]";
|
|
|
| # end index range
|
| is ~@array12[*-4 .. *-2], 'a b c', "end indices [*-4 .. *-2]";
|
|
|
| # end index as lvalue
|
| @array12[*-1] = 'd';
|
| is @array12[*-1], 'd', "assigns to the correct end slice index";
|
| is ~@array12,'a b c d', "assignment to end index correctly alters the array";
|
| }
|
|
|
| #?pugs skip "no whatever star yet"
|
| {
|
| my @array13 = ('a', 'b', 'c', 'd');
|
| # end index range as lvalue
|
| @array13[*-4 .. *-1] = ('d', 'c', 'b', 'a'); # ('a'..'d').reverse
|
| is ~@array13, 'd c b a', "end range as lvalue";
|
|
|
| #hat trick
|
| my @array14 = ('a', 'b', 'c', 'd');
|
| my @b = 0..3;
|
| ((@b[*-3,*-2,*-1,*-4] = @array14)= @array14[*-1,*-2,*-3,*-4]);
|
|
|
| is ~@b,
|
| 'a d c b',
|
| "hat trick:
|
| assign to a end-indexed slice array from array
|
| lvalue in assignment is then lvalue to end-indexed slice as rvalue";
|
| }
|
|
|
| # This test may seem overly simplistic, but it was actually a bug in PIL2JS, so
|
| # why not write a test for it so other backends can benefit of it, too? :)
|
| {
|
| my @arr = (0, 1, 2, 3);
|
| @arr[0] = "new value";
|
| is @arr[0], "new value", "modifying of array contents (constants) works";
|
| }
|
|
|
| #?rakudo skip "access out of array bounds"
|
| #?pugs skip "no whatever star yet"
|
| {
|
| my @arr;
|
| lives_ok { @arr[*-1] }, "readonly accessing [*-1] of an empty array is ok (1)";
|
| ok !(try { @arr[*-1] }), "readonly accessing [*-1] of an empty array is ok (2)";
|
| dies_ok { @arr[*-1] = 42 }, "assigning to [*-1] of an empty array is fatal";
|
| dies_ok { @arr[*-1] := 42 }, "binding [*-1] of an empty array is fatal";
|
| }
|
|
|
| #?rakudo skip "access out of array bounds"
|
| #?pugs skip "no whatever star yet"
|
| {
|
| my @arr = (23);
|
| lives_ok { @arr[*-2] }, "readonly accessing [*-2] of an one-elem array is ok (1)";
|
| ok !(try { @arr[*-2] }), "readonly accessing [*-2] of an one-elem array is ok (2)";
|
| dies_ok { @arr[*-2] = 42 }, "assigning to [*-2] of an one-elem array is fatal";
|
| dies_ok { @arr[*-2] := 42 }, "binding [*-2] of an empty array is fatal";
|
| }
|
|
|
| {
|
| my @arr = <a normal array with nothing funny>;
|
| my $minus_one = -1;
|
|
|
| eval_dies_ok '@arr[-1]', "readonly accessing [-1] of normal array is compile-time error";
|
| #?rakudo todo '@array[$minus_one] should fail'
|
| dies_ok { @arr[ $minus_one ] }, "indirectly accessing [-1] " ~
|
| "through a variable is run-time error";
|
| #?rakudo 2 skip '@arr[-1] should fail'
|
| dies_ok { @arr[$minus_one] = 42 }, "assigning to [-1] of a normal array is fatal";
|
| dies_ok { @arr[$minus_one] := 42 }, "binding [-1] of a normal array is fatal";
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/nested_arrays.t lines 5–34 (no results): (skip)
| # L<S02/Mutable types/Array>
|
|
|
| =begin description
|
|
|
| Nested array tests; various interactions of arrayrefs, arrays, flattening and nesting.
|
|
|
| =end description
|
|
|
| plan 8;
|
|
|
| { # UNSPECCED
|
| my @a = (1,2,[3,4]);
|
| my $a = (1,2,[3,4]);
|
| my @b = [1,2,[3,4]];
|
| my $b = [1,2,[3,4]];
|
| my @c = (1,2,(3,4));
|
| my $c = (1,2,(3,4));
|
| my @d = [1,2,(3,4)];
|
| my $d = [1,2,(3,4)];
|
|
|
| is(+@a, 3, 'Array length, nested []');
|
| is(+$a, 3, 'Array object length, nested []');
|
| is(+@b, 1, 'Array length, nested [], outer []s');
|
| is(+$b, 3, 'Array object length, nested [], outer []s');
|
|
|
| is(+@c, 4, 'Array length, nested ()');
|
| is(+$c, 4, 'Array object length, nested ()');
|
| is(+@d, 1, 'Array length, nested (), outer []s');
|
| is(+$d, 4, 'Array object length, nested (), outer []s');
|
| }
|
Highlighted:
small|full
Hash Perl hash
KeyHash Perl hash that autodeletes values matching default
KeySet KeyHash of Bool (does Set in list/array context)
From t/spec/S02-builtin_data_types/keyset.t lines 5–27 (no results): (skip)
| # L<S02/Mutable types/"KeyHash of Bool">
|
|
|
| # A KeySet is a KeyHash of Bool, i.e. the values are Bool
|
|
|
| {
|
| my %h is KeySet;
|
|
|
| %h = (a => True, b => False, c => True);
|
| is +%h.elems, 2, 'Inititalization worked';
|
| lives_ok { %h<c> = 0 }, 'can set an item to 0';
|
| is %h.elems, 1, '... and an item is gone';
|
| is %h.keys.join, 'a', '... and the right one is gone';
|
| %h<c>++;
|
| is %h.keys.sort.join, 'ac', '++ on an item reinstates it';
|
| %h<c>++;
|
| is %h.keys.sort.join, 'ac', '++ on an existing item does nothing';
|
| %h<a>--;
|
| is ~%h.keys, 'c', '-- removes items';
|
| %h<b>--;
|
| is ~%h.keys, 'c', '... but only if they were there from the beginning';
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
KeyBag KeyHash of UInt (does Bag in list/array context)
Pair A single key-to-value association
From t/spec/integration/passing-pair-class-to-sub.t lines 4–29 (no results): (skip)
| # L<S02/Mutable types/A single key-to-value association>
|
| # There ought to be a better reference for this.
|
| # And this test is also a candidate to be moved with other subroutine tests.
|
|
|
|
|
| plan 2;
|
|
|
| # this used to be a pugs regression
|
| {
|
| my sub foo ($x) { $x.perl }
|
|
|
| my $pair = (a => 1);
|
| my $Pair = $pair.WHAT;
|
|
|
| lives_ok { foo($Pair) }, "passing ::Pair to a sub works";
|
| }
|
|
|
| # But this works:
|
| {
|
| my sub foo ($x) { $x.perl }
|
|
|
| my $int = 42;
|
| my $Int = $int.WHAT;
|
|
|
| lives_ok { foo($Int) }, "passing ::Int to a sub works";
|
| }
|
Highlighted:
small|full
From t/spec/S02-builtin_data_types/pair.t lines 7–320 (no results): (skip)
| # L<S02/Mutable types/A single key-to-value association>
|
| # basic Pair
|
|
|
| my $pair = 'foo' => 'bar';
|
| isa_ok($pair, Pair);
|
|
|
| # get key and value from the pair as many ways as possible
|
|
|
| #?rakudo 2 skip 'method($invocant:) syntax missing'
|
| is(key($pair:), 'foo', 'got the right key($pair:)');
|
| is(value($pair:), 'bar', 'got the right value($pair:)');
|
|
|
| is($pair.key(), 'foo', 'got the right $pair.key()');
|
| is($pair.value(), 'bar', 'got the right $pair.value()');
|
|
|
| is($pair.key, 'foo', 'got the right $pair.key');
|
| is($pair.value, 'bar', 'got the right $pair.value');
|
|
|
| # get both (kv) as many ways as possible
|
|
|
| my @pair1a = kv($pair);
|
| is(+@pair1a, 2, 'got the right number of elements in the list');
|
| #?rakudo 2 todo 'kv() ambiguity'
|
| is(@pair1a[0], 'foo', 'got the right key');
|
| is(@pair1a[1], 'bar', 'got the right value');
|
|
|
| my @pair1b = kv $pair;
|
| is(+@pair1b, 2, 'got the right number of elements in the list');
|
| #?rakudo 2 todo 'kv() ambiguity'
|
| is(@pair1b[0], 'foo', 'got the right key');
|
| is(@pair1b[1], 'bar', 'got the right value');
|
|
|
| my @pair1c = $pair.kv;
|
| is(+@pair1c, 2, 'got the right number of elements in the list');
|
| is(@pair1c[0], 'foo', 'got the right key');
|
| is(@pair1c[1], 'bar', 'got the right value');
|
|
|
| my @pair1d = $pair.kv();
|
| is(+@pair1d, 2, 'got the right number of elements in the list');
|
| is(@pair1d[0], 'foo', 'got the right key');
|
| is(@pair1d[1], 'bar', 'got the right value');
|
|
|
| # Pair with a numeric value
|
|
|
| my $pair2 = 'foo' => 2;
|
| isa_ok($pair2, Pair);
|
|
|
| is($pair2.value, 2, 'got the right value');
|
|
|
| # Pair with a Pair value
|
|
|
| my $pair3 = "foo" => ("bar" => "baz");
|
| isa_ok($pair3, Pair);
|
|
|
| my $pair3a = $pair3.value;
|
| isa_ok($pair3a, Pair);
|
| is($pair3a.key, 'bar', 'got right nested pair key');
|
| is($pair3a.value, 'baz', 'got right nested pair key');
|
|
|
| is($pair3.value.key, 'bar', 'got right nested pair key (method chaining)');
|
| is($pair3.value.value, 'baz', 'got right nested pair key (method chaining)');
|
|
|
| # Pair with a Pair key
|
|
|
| my $pair4 = ("foo" => "bar") => "baz";
|
| isa_ok($pair4, Pair);
|
|
|
| is($pair4.value, 'baz', 'got the right value');
|
|
|
| isa_ok($pair4.key, Pair);
|
| is($pair4.key.key, 'foo', 'got right nested key');
|
| is($pair4.key.value, 'bar', 'got right nested value');
|
|
|
| my $quux = (quux => "xyzzy");
|
| is($quux.key, 'quux', "lhs quotes" );
|
|
|
| {
|
| my $pair = :when<now>;
|
| is ~(%($pair)), "when\tnow\n", 'hash stringification';
|
| # hold back this one according to audreyt
|
| #ok $pair.does(Hash), 'Pair does Hash';
|
| #?pugs TODO "bug"
|
| ok (%($pair) ~~ Hash), '%() makes creates a real Hash';
|
| }
|
|
|
| # colonpair syntax
|
| {
|
| is(:foo.key, 'foo', 'got the right key :foo.key');
|
| #?rakudo todo 'RT #64478'
|
| isa_ok(:foo.value, Bool, ':foo.value isa Bool');
|
| ok( :foo, ':foo is True');
|
| ok( :foo.value, ':foo.value is True');
|
| is(:!foo.key, 'foo', 'got the right key :!foo.key');
|
| #?rakudo todo 'RT #64478'
|
| isa_ok(:!foo.value, Bool, ':!foo.value isa Bool');
|
| nok( :!foo.value, ':!foo.value is False');
|
| }
|
|
|
| # illustrate a bug
|
|
|
| {
|
| my $var = 'foo' => 'bar';
|
| sub test1 (Pair $pair) {
|
| isa_ok($pair,Pair);
|
| my $testpair = $pair;
|
| isa_ok($testpair,Pair); # new lvalue variable is also a Pair
|
| my $boundpair := $pair;
|
| isa_ok($boundpair,Pair); # bound variable is also a Pair
|
| is($pair.key, 'foo', 'in sub test1 got the right $pair.key');
|
| is($pair.value, 'bar', 'in sub test1 got the right $pair.value');
|
|
|
| }
|
| test1 $var;
|
| }
|
|
|
| my %hash = ('foo' => 'bar');
|
| for %hash.pairs -> $pair {
|
| isa_ok($pair,Pair) ;
|
| my $testpair = $pair;
|
| isa_ok($testpair, Pair); # new lvalue variable is also a Pair
|
| my $boundpair := $pair;
|
| isa_ok($boundpair,Pair); # bound variable is also a Pair
|
| is($pair.key, 'foo', 'in for loop got the right $pair.key');
|
| is($pair.value, 'bar', 'in for loop got the right $pair.value');
|
| }
|
|
|
| sub test2 (%h){
|
| for %h.pairs -> $pair {
|
| isa_ok($pair,Pair) ;
|
| is($pair.key, 'foo', 'in sub test2 got the right $pair.key');
|
| is($pair.value, 'bar', 'in sub test2 got the right $pair.value');
|
| }
|
| }
|
| test2 %hash;
|
|
|
| # See thread "$pair[0]" on p6l started by Ingo Blechschmidt:
|
| # L<"http://www.nntp.perl.org/group/perl.perl6.language/22593">
|
|
|
| sub test3 (%h){
|
| for %h.pairs -> $pair {
|
| isa_ok($pair,Pair);
|
| dies_ok({$pair[0]}, 'sub test3: access by $pair[0] should not work');
|
| dies_ok({$pair[1]}, 'sub test3: access by $pair[1] should not work');
|
| }
|
| }
|
| test3 %hash;
|
|
|
| =begin p6l
|
|
|
| Hm, Hash::pair? Never heard of that. --iblech
|
|
|
| sub test4 (%h){
|
| for %h.pair -> $pair {
|
| #?pugs todo 'bug'
|
| isa_ok($pair,Pair);
|
| is($pair.key, 'foo', 'sub test4: access by unspecced "pair" got the right $pair.key');
|
| is($pair.value, 'bar', 'sub test4: access by unspecced "pair" got the right $pair.value');
|
|
|
| }
|
| }
|
| test4 %hash;
|
|
|
| =end p6l
|
|
|
| my $should_be_a_pair = (a => 25/1);
|
| isa_ok $should_be_a_pair, Pair, "=> has correct precedence";
|
|
|
| =begin discussion
|
|
|
| Stated by Larry on p6l in:
|
| L<"http://www.nntp.perl.org/group/perl.perl6.language/20122">
|
|
|
| "Oh, and we recently moved => to assignment precedence so it would
|
| more naturally be right associative, and to keep the non-chaining
|
| binaries consistently non-associative. Also lets you say:
|
|
|
| key => $x ?? $y !! $z;
|
|
|
| plus it moves it closer to the comma that it used to be in Perl 5."
|
|
|
| (iblech) XXX: this contradicts current S03 so I could be wrong.
|
|
|
| =end discussion
|
|
|
| {
|
| # This should always work.
|
| my %x = ( "Zaphod" => (0 ?? 1 !! 2), "Ford" => 42 );
|
| is %x{"Zaphod"}, 2, "Zaphod is 2";
|
| is %x{"Ford"}, 42, "Ford is 42";
|
|
|
| # This should work only if => is lower precedence than ?? !!
|
| my %z = ( "Zaphod" => 0 ?? 1 !! 2, "Ford" => 42 );
|
| is %z{"Zaphod"}, 2, "Zaphod is still 2";
|
| is %z{"Ford"}, 42, "Ford is still 42";
|
| }
|
|
|
| # This is per the pairs-behave-like-one-element-hashes-rule.
|
| # (I asked p6l once, but the "thread" got warnocked. --iblech)
|
| # (I asked p6l again, now the thread did definitely not get warnocked:
|
| # L<"http://groups.google.de/group/perl.perl6.language/browse_thread/thread/e0e44be94bd31792/6de6667398a4d2c7?q=perl6.language+Stringification+pairs&">
|
| # Also see L<"http://www.nntp.perl.org/group/perl.perl6.language/23224">
|
| {
|
| my $pair = (a => 1);
|
| is ~$pair, "a\t1", "pairs stringify correctly (1)";
|
| is "$pair", "a\t1", "pairs stringify correctly (2)";
|
| }
|
|
|
| {
|
| my $pair = (a => [1,2,3]);
|
| is ~$pair, "a\t1 2 3", "pairs with arrayrefs as values stringify correctly (1)";
|
| is "$pair", "a\t1 2 3", "pairs with arrayrefs as values stringify correctly (2)";
|
| }
|
|
|
| # Per Larry L<"http://www.nntp.perl.org/group/perl.perl6.language/23525">:
|
| # Actually, it looks like the bug is probably that => is forcing
|
| # stringification on its left argument too agressively. It should only do
|
| # that for an identifier.
|
| {
|
| my $arrayref = [< a b c >];
|
| my $hashref = { :d(1), :e(2) };
|
|
|
| my $pair = ($arrayref => $hashref);
|
| is ~$pair.key, ~$arrayref, "=> should not stringify the key (1)";
|
| is ~$pair.value, ~$hashref, "=> should not stringify the key (2)";
|
|
|
| push $pair.key, "d";
|
| $pair.value<f> = 3;
|
| is ~$pair.key, ~$arrayref, "=> should not stringify the key (3)";
|
| is ~$pair.value, ~$hashref, "=> should not stringify the key (4)";
|
| is +$pair.key, 4, "=> should not stringify the key (5)";
|
| is +$pair.value, 3, "=> should not stringify the key (6)";
|
| }
|
|
|
| {
|
| my $arrayref = [< a b c >];
|
| my $hashref = { :d(1), :e(2) };
|
|
|
| my $pair = ($arrayref => $hashref);
|
| sub pair_key (Pair $pair) { $pair.key }
|
|
|
| is ~pair_key($pair), ~$arrayref,
|
| "the keys of pairs should not get auto-stringified when passed to a sub (1)";
|
|
|
| push $pair.key, "d";
|
| is ~pair_key($pair), ~$arrayref,
|
| "the keys of pairs should not get auto-stringified when passed to a sub (2)";
|
| is +pair_key($pair), 4,
|
| "the keys of pairs should not get auto-stringified when passed to a sub (3)";
|
| }
|
|
|
| # Per Larry: http://www.nntp.perl.org/group/perl.perl6.language/23984
|
| {
|
| my ($key, $val) = <key val>;
|
| my $pair = ($key => $val);
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { $pair.key = "KEY" }, "setting .key does not die";
|
| is $pair.key, "KEY", "setting .key actually changes the key";
|
| is $key, "key", "setting .key does not change the original var";
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { $pair.value = "VAL" }, "setting .value does not die";
|
| is $pair.value, "VAL", "setting .value actually changes the value";
|
| is $val, "val", "setting .value does not change the original var";
|
| }
|
|
|
| #?rakudo skip 'pair binding not implemented'
|
| {
|
| my ($key, $val) = <key val>;
|
| my $pair = ($key => $val);
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { $pair.key := "KEY" }, "binding .key does not die";
|
| is $pair.key, "KEY", "binding .key actually changes the key";
|
| is $key, "key", "binding .key does not change the original var";
|
| dies_ok { $pair.key = 42 }, "the .key was really bound"; # (can't modify constant)
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { $pair.value := "VAL" }, "binding .value does not die";
|
| is $pair.value, "VAL", "binding .value actually changes the value";
|
| is $val, "val", "binding .value does not change the original var";
|
| dies_ok { $pair.value = 42 }, "the .value was really bound"; # (can't modify constant)
|
| }
|
|
|
| #?rakudo skip 'pair binding not implemented'
|
| {
|
| my ($key, $val) = <key val>;
|
| my $pair = (abc => "def");
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { $pair.key := $key }, "binding .key does not die";
|
| is $pair.key, "key", "binding .key actually changes the key";
|
| $key = "KEY";
|
| is $key, "KEY", "binding .key to a var works (1)";
|
| #?pugs 5 todo 'bug'
|
| is $pair.key, "KEY", "binding .key to a var works (2)";
|
| $pair.key = "new";
|
| is $key, "new", "binding .key to a var works (3)";
|
| is $pair.key, "new", "binding .key to a var works (4)";
|
|
|
| lives_ok { $pair.value := $val }, "binding .value does not die";
|
| is $pair.value, "val", "binding .value actually changes the value";
|
| $val = "VAL";
|
| is $val, "VAL", "binding .value to a var works (1)";
|
| #?pugs 3 todo 'bug'
|
| is $pair.value, "VAL", "binding .value to a var works (2)";
|
| $pair.value = "new";
|
| is $val, "new", "binding .value to a var works (3)";
|
| is $pair.value, "new", "binding .value to a var works (4)";
|
| }
|
|
|
|
|
| ## These tests really belong in a different test file -- probably
|
| ## something in S06. --pmichaud
|
Highlighted:
small|full
Mapping Set of Pairs with no duplicate keys
Buf Perl buffer (a stringish array of memory locations)
IO Perl filehandle
Routine Base class for all wrappable executable objects
Sub Perl subroutine
Method Perl method
Submethod Perl subroutine acting like a method
Macro Perl compile-time subroutine
Regex Perl pattern
Match Perl match, usually produced by applying a pattern
Package Perl 5 compatible namespace
Module Perl 6 standard namespace
Class Perl 6 standard class namespace
Role Perl 6 standard generic interface/implementation
Grammar Perl 6 pattern matching namespace
Any Perl 6 object (default routine parameter type, excludes junction)
Object Perl 6 object (default block parameter type, either Any or junction)
From t/spec/S04-blocks-and-statements/pointy.t lines 92–111 (no results): (skip)
| # L<S02/Mutable types/"default block parameter type">
|
| # this means that Junctions don't autothread over pointy blocks
|
|
|
| {
|
| my @a = any(3, 4);
|
| my $ok = 0;
|
| my $iterations = 0;
|
| for @a -> $x {
|
| $ok = 1 if $x ~~ Junction;
|
| $iterations++;
|
| }
|
| ok $ok, 'Blocks receive junctions without autothreading';
|
| is $iterations, 1, 'no autothreading happened';
|
| my $b = -> $x { ... };
|
| ok $b.signature.perl !~~ /Any/,
|
| 'The .signature of a block does not contain Any';
|
| }
|
|
|
| # vim: ft=perl6
|
|
|
Highlighted:
small|full
From t/spec/S03-junctions/autothreading.t lines 276–293 (no results): (skip)
| # L<S02/Mutable types/"default block parameter type">
|
|
|
| # block parameters default to Object, so test that they don't autothread:
|
| #?rakudo skip '"No exception handler and no message"'
|
| {
|
| my $c = 0;
|
| for 1|2, 3|4, 5|6 -> $x {
|
| $c++;
|
| }
|
| is $c, 3, 'do not autothread over blocks by default';
|
| $c = 0;
|
| for 1|2, 3|4, 5|6 -> Any $x {
|
| $c++;
|
| }
|
| is $6, 3, 'do autothread over blocks with explicit Any';
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
A KeyHash differs from a normal Hash in how it handles default values. If the value of a KeyHash element is set to the default value for the KeyHash, the element is deleted. If undeclared, the default default for a KeyHash is 0 for numeric types, False for boolean types, and the null string for string and buffer types. A KeyHash of a Object type defaults to the undefined prototype for that type. More generally, the default default is whatever defined value an undef would convert to for that value type. A KeyHash of Scalar deletes elements that go to either 0 or the null string. A KeyHash also autodeletes keys for normal undef values (that is, those undefined values that do not contain an unthrown exception).
From t/spec/S02-builtin_data_types/keyhash.t lines 5–49 (no results): (skip)
| # L<S02/Mutable types/"A KeyHash differs from a normal Hash">
|
|
|
| # untyped KeyHash
|
| {
|
| my %h is KeyHash;
|
|
|
| %h = (a => 1, b => 3, c => -1, d => 7);
|
| is +%h.elems, 4, 'Inititalization worked';
|
| lives_ok { %h<d> = 0 }, 'can set an item to 0';
|
| is %h.elems, 3, '... and an item is gone';
|
| is %h.keys.sort.join(''), 'abc', '... and the right one is gone';
|
| %h<c>++;
|
| is %h.keys.sort.join(''), 'ab', '++ on an item with -1 deletes it';
|
| %h<a>--;
|
| is ~%h.keys, 'b', '-- also removes items when they go to zero';
|
| %h<b>--;
|
| is ~%h.keys, 'b', '... but only if they go to zero';
|
| %h<c> = 'abc';
|
| is ~%h<b c>, '2 abc', 'can store a string as well';
|
| %h<c> = '';
|
| is +%h, 1, 'setting a value to the null string also removes it';
|
| %h<b> = undef;
|
| is %h, 0, 'setting a value to undef also removes it';
|
| ok !%h, '... and the empty hash is false in boolean context';
|
| }
|
|
|
| # typed KeyHash
|
| {
|
| my Int %h is KeyHash;
|
|
|
| %h = (a => 1, b => 3, c => -1, d => 7);
|
| is +%h.elems, 4, 'Inititalization worked';
|
| lives_ok { %h<d> = 0 }, 'can set an item to 0';
|
| is %h.elems, 3, '... and an item is gone';
|
| is %h.keys.sort.join(''), 'abc', '... and the right one is gone';
|
| %h<c>++;
|
| is %h.keys.sort.join(''), 'ab', '++ on an item with -1 deletes it';
|
| %h<a>--;
|
| is ~%h.keys, 'b', '-- also removes items when they go to zero';
|
| %h<b>--;
|
| is ~%h.keys, 'b', '... but only if they go to zero';
|
| }
|
|
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
A KeySet is a KeyHash of booleans with a default of False. If you use the Hash interface and increment an element of a KeySet its value becomes true (creating the element if it doesn't exist already). If you decrement the element it becomes false and is automatically deleted. Decrementing a non-existing value results in a False value. Incrementing an existing value results in True. When not used as a Hash (that is, when used as an Array or list or Set object) a KeySet behaves as a Set of its keys. (Since the only possible value of a KeySet is the True value, it need not be represented in the actual implementation with any bits at all.)
A KeyBag is a KeyHash of UInt with default of 0. If you use the Hash interface and increment an element of a KeyBag its value is increased by one (creating the element if it doesn't exist already). If you decrement the element the value is decreased by one; if the value goes to 0 the element is automatically deleted. An attempt to decrement a non-existing value results in a Failure value. When not used as a Hash (that is, when used as an Array or list or Bag object) a KeyBag behaves as a Bag of its keys, with each key replicated the number of times specified by its corresponding value. (Use .kv or .pairs to suppress this behavior in list context.)
As with Hash types, Pair and Mapping are mutable in their values but not in their keys. (A key can be a reference to a mutable object, but cannot change its .WHICH identity. In contrast, the value may be rebound to a different object, just as a hash element may.)
Explicit types are optional. Perl variables have two associated types: their "value type" and their "implementation type". (More generally, any container has an implementation type, including subroutines and modules.) The value type is stored as its of property, while the implementation type of the container is just the object type of the container itself. The word returns is allowed as an alias for of.
The value type specifies what kinds of values may be stored in the variable. A value type is given as a prefix or with the of keyword:
my Dog $spot;
my $spot of Dog;
In either case this sets the of property of the container to Dog.
Subroutines have a variant of the of property, as, that sets the as property instead. The as property specifies a constraint (or perhaps coercion) to be enforced on the return value (either by explicit call to return or by implicit fall-off-the-end return). This constraint, unlike the of property, is not advertised as the type of the routine. You can think of it as the implicit type signature of the (possibly implicit) return statement. It's therefore available for type inferencing within the routine but not outside it. If no as type is declared, it is assumed to be the same as the of type, if declared.
sub get_pet() of Animal {...} # of type, obviously
sub get_pet() returns Animal {...} # of type
our Animal sub get_pet() {...} # of type
sub get_pet() as Animal {...} # as type
A value type on an array or hash specifies the type stored by each element:
my Dog @pound; # each element of the array stores a Dog
my Rat %ship; # the value of each entry stores a Rat
The key type of a hash may be specified as a shape trait--see S09.
The implementation type specifies how the variable itself is implemented. It is given as a trait of the variable:
my $spot is Scalar; # this is the default
my $spot is PersistentScalar;
my $spot is DataBase;
Defining an implementation type is the Perl 6 equivalent to tying a variable in Perl 5. But Perl 6 variables are tied directly at declaration time, and for performance reasons may not be tied with a run-time tie statement unless the variable is explicitly declared with an implementation type that does the Tieable role.
However, package variables are always considered Tieable by default. As a consequence, all named packages are also Tieable by default. Classes and modules may be viewed as differently tied packages. Looking at it from the other direction, classes and modules that wish to be bound to a global package name must be able to do the Package role.
A non-scalar type may be qualified, in order to specify what type of value each of its elements stores:
From t/spec/S02-builtin_data_types/declare.t lines 524–544 (no results): (skip)
| # L<S02/"Hierarchical types"/"A non-scalar type may be qualified">
|
| # my Egg $cup; my Egg @carton; my Array of Egg @box; my Array of Array of Egg @crate;
|
| # my Hash of Array of Recipe %book;
|
| # my Hash:of(Array:of(Recipe)) %book;
|
| # my Hash of Array of Recipe %book; my %book of Hash of Array of Recipe
|
|
|
| #?rakudo skip 'Not yet implemented Array of Int @box'
|
| {
|
| my Array of Int @box;
|
| ok(1,'Array of Int @box');
|
| }
|
|
|
| #?rakudo skip 'Not yet implemented Array of Array of Int @box'
|
| {
|
| my Array of Array of Int @box;
|
| ok(1,'Array of Array of Int @box');
|
| }
|
|
|
| # TODO FIXME
|
|
|
|
|
Highlighted:
small|full
my Egg $cup; # the value is an Egg
my Egg @carton; # each elem is an Egg
my Array of Egg @box; # each elem is an array of Eggs
my Array of Array of Egg @crate; # each elem is an array of arrays of Eggs
my Hash of Array of Recipe %book; # each value is a hash of arrays of Recipes
Each successive of makes the type on its right a parameter of the type on its left. Parametric types are named using square brackets, so:
my Hash of Array of Recipe %book;
actually means:
my Hash:of(Array:of(Recipe)) %book;
Because the actual variable can be hard to find when complex types are specified, there is a postfix form as well:
my Hash of Array of Recipe %book; # HoHoAoRecipe
my %book of Hash of Array of Recipe; # same thing
The as form may be used in subroutines:
my sub get_book ($key) as Hash of Array of Recipe {...}
Alternately, the return type may be specified within the signature:
my sub get_book ($key --> Hash of Array of Recipe) {...}
There is a slight difference, insofar as the type inferencer will ignore a as but pay attention to --> or prefix type declarations, also known as the of type. Only the inside of the subroutine pays attention to as, and essentially coerces the return value to the indicated type, just as if you'd coerced each return expression.
You may also specify the of type as the of trait (with returns allowed as a synonym):
my Hash of Array of Recipe sub get_book ($key) {...}
my sub get_book ($key) of Hash of Array of Recipe {...}
my sub get_book ($key) returns Hash of Array of Recipe {...}
Anywhere you can use a single type you can use a set of types, for convenience specifiable as if it were an "or" junction:
my Int|Str $error = $val; # can assign if $val~~Int or $val~~Str
Fancier type constraints may be expressed through a subtype:
From t/spec/S02-polymorphic_types/subset.t lines 11–27 (no results): (skip)
| # L<S02/Polymorphic types/"Fancier type constraints may be expressed through a subtype">
|
|
|
| subset Even of Int where { $_ % 2 == 0 };
|
|
|
| {
|
| my Even $x = 2;
|
| is $x, 2, 'Can assign value to a type variable with subset';
|
| };
|
|
|
| eval_dies_ok 'my Even $x = 3',
|
| "Can't assing value that violates type constraint via subst";
|
|
|
| {
|
| ok 2 ~~ Even, 'Can smartmatch against subsets 1';
|
| ok 3 !~~ Even, 'Can smartmatch against subsets 2';
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-polymorphic_types/subset.t lines 28–49 (no results): (skip)
| # L<S02/Polymorphic types/"Fancier type constraints may be expressed through a subtype">
|
|
|
| subset Digit of Int where ^10;
|
|
|
| {
|
| my Digit $x = 3;
|
| is $x, 3, "Can assign to var with 'subset' type constraint";
|
| $x = 0;
|
| is $x, 0, "one end of range";
|
| $x = 9;
|
| is $x, 9, "other end of range";
|
| }
|
|
|
| eval_dies_ok 'my Digit $x = 10',
|
| 'type constraints prevents assignment 1';
|
| eval_dies_ok 'my Digit $x = -1',
|
| 'type constraints prevents assignment 2';
|
| eval_dies_ok 'my Digit $x = 3.1',
|
| 'original type prevents assignment';
|
|
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
subset Shinola of Any where {.does(DessertWax) and .does(FloorTopping)};
if $shimmer ~~ Shinola {...} # $shimmer must do both interfaces
Since the terms in a parameter could be viewed as a set of constraints that are implicitly "anded" together (the variable itself supplies type constraints, and where clauses or tree matching just add more constraints), we relax this to allow juxtaposition of types to act like an "and" junction:
# Anything assigned to the variable $mitsy must conform
# to the type Fish and either the Cat or Dog type...
my Cat|Dog Fish $mitsy = new Fish but { Bool.pick ?? .does Cat
!! .does Dog };
[Note: the above is a slight lie, insofar as parameters are currently restricted for 6.0.0 to having only a single main type for the formal variable until we understand MMD a bit better.]
Parameters may be given types, just like any other variable:
From t/spec/S02-builtin_data_types/type.t lines 52–78 (no results): (skip)
| # L<S02/Parameter types/Parameters may be given types, just like any other variable>
|
| {
|
| sub paramtype (Int $i) {return $i+1}
|
| is(paramtype(5), 6, 'sub parameters with matching type');
|
| eval_dies_ok('paramtype("foo")', 'sub parameters with non-matching type dies');
|
| }
|
|
|
| {
|
| # test contributed by Ovid++
|
| sub fact (Int $n) {
|
| if 0 == $n {
|
| 1;
|
| }
|
| else {
|
| $n * fact($n - 1);
|
| }
|
| }
|
| is fact(5), 120, 'recursive factorial with type contstraints work';
|
| }
|
|
|
| # Num accepts Int too.
|
| {
|
| my Num $n;
|
| $n = 42;
|
| is $n, 42, 'Num accepts Int too';
|
| }
|
|
|
Highlighted:
small|full
sub max (int @array is rw) {...}
sub max (@array of int is rw) {...}
From t/spec/S06-signature/type-capture.t lines 7–41 (no results): (skip)
| # L<S02/Generic types/>
|
|
|
| # Check it captures built-in types.
|
| sub basic_capture(::T $x) { T }
|
| isa_ok(basic_capture(42), Int, 'captured built-in type');
|
| isa_ok(basic_capture(4.2), Num, 'captured built-in type');
|
|
|
| # User defined ones too.
|
| class Foo { }
|
| isa_ok(basic_capture(Foo.new), Foo, 'captured user defined type');
|
|
|
| # Check you can use captured type later in the signature.
|
| sub two_the_same(::T $x, T $y) { 1 }
|
| ok(two_the_same(42, 42), 'used captured type later in the sig');
|
| my $ok = 1;
|
| try {
|
| two_the_same(42, 4.2);
|
| $ok = 0;
|
| }
|
| ok($ok, 'used captured type later in the sig');
|
|
|
| # Check you can use them to declare variables.
|
| sub declare_cap_type(::T $x) {
|
| my T $y = 4.2;
|
| 1
|
| }
|
| ok(declare_cap_type(3.3), 'can use captured type in declaration');
|
| $ok = 1;
|
| try {
|
| declare_cap_type(42);
|
| $ok = 0;
|
| }
|
| ok($ok, 'can use captured type in declaration');
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
Within a declaration, a class variable (either by itself or following an existing type name) declares a new type name and takes its parametric value from the actual type of the parameter it is associated with. It declares the new type name in the same scope as the associated declaration.
sub max (Num ::X @array) {
push @array, X.new();
}
The new type name is introduced immediately, so two such types in the same signature must unify compatibly if they have the same name:
sub compare (Any ::T $x, T $y) {
return $x eqv $y;
}
On a scoped subroutine, a return type can be specified before or after the name. We call all return types "return types", but distinguish two kinds of return types, the as type and the of type, because the of type is normally an "official" named type and declares the official interface to the routine, while the as type is merely a constraint on what may be returned by the routine from the routine's point of view.
From t/spec/S02-builtin_data_types/type.t lines 79–135 (no results): (skip)
| # L<S02/Return types/a return type can be specified before or after the name>
|
| {
|
| # Check with explicit return.
|
| my sub returntype1 (Bool $pass) returns Str { return $pass ?? 'ok' !! -1}
|
| my sub returntype2 (Bool $pass) of Int { return $pass ?? 42 !! 'no'}
|
| my Bool sub returntype3 (Bool $pass) { return $pass ?? Bool::True !! ':('}
|
| my sub returntype4 (Bool $pass --> Str) { return $pass ?? 'ok' !! -1}
|
|
|
| is(returntype1(Bool::True), 'ok', 'good return value works (returns)');
|
| dies_ok({ returntype1(Bool::False) }, 'bad return value dies (returns)');
|
| is(returntype2(Bool::True), 42, 'good return value works (of)');
|
| dies_ok({ returntype2(Bool::False) }, 'bad return value dies (of)');
|
|
|
| is(returntype3(Bool::True), True, 'good return value works (my Type sub)');
|
| dies_ok({ returntype3(Bool::False) }, 'bad return value dies (my Type sub)');
|
|
|
| is(returntype4(Bool::True), 'ok', 'good return value works (-->)');
|
| dies_ok({ returntype4(Bool::False) }, 'bad return value dies (-->)');
|
| }
|
| {
|
| # Check with implicit return.
|
| my sub returntype1 (Bool $pass) returns Str { $pass ?? 'ok' !! -1}
|
| my sub returntype2 (Bool $pass) of Int { $pass ?? 42 !! 'no'}
|
| my Bool sub returntype3 (Bool $pass) { $pass ?? Bool::True !! ':('}
|
| my sub returntype4 (Bool $pass --> Str) { $pass ?? 'ok' !! -1}
|
|
|
| is(returntype1(Bool::True), 'ok', 'good implicit return value works (returns)');
|
| dies_ok({ returntype1(Bool::False) }, 'bad implicit return value dies (returns)');
|
| is(returntype2(Bool::True), 42, 'good implicit return value works (of)');
|
| dies_ok({ returntype2(Bool::False) }, 'bad implicit return value dies (of)');
|
|
|
| is(returntype3(Bool::True), True, 'good implicit return value works (my Type sub)');
|
| dies_ok({ returntype3(Bool::False) }, 'bad implicit return value dies (my Type sub)');
|
|
|
| is(returntype4(Bool::True), 'ok', 'good implicit return value works (-->)');
|
| dies_ok({ returntype4(Bool::False) }, 'bad implicit return value dies (-->)');
|
| }
|
|
|
| #?rakudo skip 'Rat not implemented, as not implemented'
|
| {
|
| # the following two are the same type of behavior
|
| # S02: "It is possible for the of type to disagree with the as type"
|
| my Rat sub returntype4 ($pass) as Num {$pass ?? 1.1 !! 1}
|
| my sub returntype5 ($pass --> Rat) as Num {$pass ?? 2.2 !! 2}
|
|
|
| is(returntype4(True), 1.1, 'good return value works (my Type sub as OtherType)');
|
| eval_dies_ok('returntype4(False)', 'bad return value dies (my Type sub as OtherType)');
|
| is(returntype5(True), 2.2, 'good return value works (--> Type as OtherType)');
|
| eval_dies_ok('returntype5(False)', 'bad return value dies (--> Type as OtherType)');
|
| }
|
|
|
| {
|
| eval_dies_ok('my Int Str $x', 'multiple prefix constraints not allowed');
|
| eval_dies_ok('sub foo(Int Str $x) { }', 'multiple prefix constraints not allowed');
|
| eval_dies_ok('sub foo(--> Int Str) { }', 'multiple prefix constraints not allowed');
|
| eval_dies_ok('our Int Str sub foo() { }', 'multiple prefix constraints not allowed');
|
| }
|
Highlighted:
small|full
our sub lay as Egg {...} # as type
our Egg sub lay {...} # of type
our sub lay of Egg {...} # of type
our sub lay (--> Egg) {...} # of type
my sub hat as Rabbit {...} # as type
my Rabbit sub hat {...} # of type
my sub hat of Rabbit {...} # of type
my sub hat (--> Rabbit) {...} # of type
If a subroutine is not explicitly scoped, it belongs to the current namespace (module, class, grammar, or package), as if it's scoped with the our scope modifier. Any return type must go after the name:
sub lay as Egg {...} # as type
sub lay of Egg {...} # of type
sub lay (--> Egg) {...} # of type
On an anonymous subroutine, any return type can only go after the sub keyword:
$lay = sub as Egg {...}; # as type
$lay = sub of Egg {...}; # of type
$lay = sub (--> Egg) {...}; # of type
but you can use a scope modifier to introduce an of prefix type:
$lay = my Egg sub {...}; # of type
$hat = my Rabbit sub {...}; # of type
Because they are anonymous, you can change the my modifier to our without affecting the meaning.
The return type may also be specified after a --> token within the signature. This doesn't mean exactly the same thing as as. The of type is the "official" return type, and may therefore be used to do type inferencing outside the sub. The as type only makes the return type available to the internals of the sub so that the return statement can know its context, but outside the sub we don't know anything about the return value, as if no return type had been declared. The prefix form specifies the of type rather than the as type, so the return type of
my Fish sub wanda ($x) { ... }
is known to return an object of type Fish, as if you'd said:
my sub wanda ($x --> Fish) { ... }
not as if you'd said
my sub wanda ($x) as Fish { ... }
It is possible for the of type to disagree with the as type:
my Squid sub wanda ($x) as Fish { ... }
or equivalently,
my sub wanda ($x --> Squid) as Fish { ... }
This is not lying to yourself--it's lying to the world. Having a different inner type is useful if you wish to hold your routine to a stricter standard than you let on to the outside world, for instance.
- The
$Package'var syntax is gone. Use $Package::var instead.
- Perl 6 includes a system of sigils to mark the fundamental structural type of a variable:
$ scalar (object)
@ ordered array
% unordered hash (associative array)
& code/rule/token/regex
:: package/module/class/role/subset/enum/type/grammar
@@ slice view of @
Within a declaration, the & sigil also declares the visibility of the subroutine name without the sigil within the scope of the declaration:
my &func := sub { say "Hi" };
func; # calls &func
Within a signature or other declaration, the :: sigil followed by an identifier marks a type variable that also declares the visibility of a package/type name without the sigil within the scope of the declaration. The first such declaration within a scope is assumed to be an unbound type, and takes the actual type of its associated argument. With subsequent declarations in the same scope the use of the sigil is optional, since the bare type name is also declared.
A declaration nested within must not use the sigil if it wishes to refer to the same type, since the inner declaration would rebind the type. (Note that the signature of a pointy block counts as part of the inner block, not the outer block.)
- Sigils indicate overall interface, not the exact type of the bound object. Different sigils imply different minimal abilities.
$x may be bound to any object, including any object that can be bound to any other sigil. Such a scalar variable is always treated as a singular item in any kind of list context, regardless of whether the object is essentially composite or unitary. It will not automatically dereference to its contents unless placed explicitly in some kind of dereferencing context. In particular, when interpolating into list context, $x never expands its object to anything other than the object itself as a single item, even if the object is a container object containing multiple items.
@x may be bound to an object of the Array class, but it may also be bound to any object that does the Positional role, such as a List, Seq, Range, Buf, or Capture. The Positional role implies the ability to support postcircumfix:<[ ]>.
Likewise, %x may be bound to any object that does the Associative role, such as Pair, Mapping, Set, Bag, KeyHash, or Capture. The Associative role implies the ability to support postcircumfix:<{ }>.
&x may be bound to any object that does the Callable role, such as any Block or Routine. The Callable role implies the ability to support postcircumfix:<( )>.
::x may be bound to any object that does the Abstraction role, such as a package, module, class, role, grammar, or any other type object, or any immutable value object that can be used as a type. This Abstraction role implies the ability to do various symbol table and/or typological manipulations which may or may not be supported by any given abstraction. Mostly though it just means that you want to give some abstraction an official name that you can then use later in the compilation without any sigil.
In any case, the minimal container role implied by the sigil is checked at binding time at the latest, and may fail earlier (such as at compile time) if a semantic error can be detected sooner. If you wish to bind an object that doesn't yet do the appropriate role, you must either stick with the generic $ sigil, or mix in the appropriate role before binding to a more specific sigil.
An object is allowed to support both Positional and Associative. An object that does not support Positional may not be bound directly to @x. However, any construct such as %x that can interpolate the contents of such an object into list context can automatically construct a list value that may then be bound to an array variable. Subscripting such a list does not imply subscripting back into the original object.
- Unlike in Perl 5, you may no longer put whitespace between a sigil and its following name or construct.
- Ordinary sigils indicate normally scoped variables, either lexical or package scoped. Oddly scoped variables include a secondary sigil (a twigil) that indicates what kind of strange scoping the variable is subject to:
$foo ordinary scoping
$.foo object attribute public accessor
$^foo self-declared formal positional parameter
$:foo self-declared formal named parameter
$*foo contextualizable global variable
$?foo compiler hint variable
$=foo pod variable
$<foo> match variable, short for $/{'foo'}
$!foo object attribute private storage
$~foo the foo sublanguage seen by the parser at this lexical spot
Most variables with twigils are implicitly declared or assumed to be declared in some other scope, and don't need a "my" or "our". Attribute variables are declared with has, though.
- Sigils are now invariant.
$ always means a scalar variable, @ an array variable, and % a hash variable, even when subscripting. In item context, variables such as @array and %hash simply return themselves as Array and Hash objects. (Item context was formerly known as scalar context, but we now reserve the "scalar" notion for talking about variables rather than contexts, much as arrays are disassociated from list context.)
- In string contexts, container objects automatically stringify to appropriate (white-space separated) string values. In numeric contexts, the number of elements in the container is returned. In boolean contexts, a true value is returned if and only if there are any elements in the container.
- To get a Perlish representation of any object, use the
.perl method. Like the Data::Dumper module in Perl 5, the .perl method will put quotes around strings, square brackets around list values, curlies around hash values, constructors around objects, etc., so that Perl can evaluate the result back to the same object. The .perl method will return a representation of the object on the assumption that, if the code is reparsed at some point, it will be used to regenerate the object as a scalar in item context. If you wish to interpolate the regenerated object in a list context, it may be necessary to use <prefix:<| >> to force interpolation.
From t/spec/S02-names_and_variables/perl.t lines 5–67 (no results): (skip)
| # L<S02/Names and Variables/To get a Perlish representation of any object>
|
|
|
| my @tests = (
|
| # Basic scalar values
|
| 42, 42/10, 4.2, sqrt(2),
|
| #?rakudo emit # 3e5 is converted to a Str when re-evaled
|
| 3e5,
|
| #?rakudo emit # Failure objects have no .perl method and +/-Inf/NaN fail
|
| Inf, -Inf, NaN,
|
|
|
| "a string", "", "\0", "\t", "\n", "\r\n", "\o7",
|
| '{', # "\d123", # XXX there is no \d escape!!!
|
| '}',
|
| '$a @string %with &sigils()',
|
|
|
| ?1, ?0,
|
| undef,
|
| #?rakudo emit # parse error
|
| rx:P5/foo/, rx:P5//, rx:P5/^.*$/,
|
|
|
| # References to scalars
|
| \42, \Inf, \-Inf, \NaN, \"string", \"", \?1, \?0, \undef,
|
|
|
| (a => 1),
|
| :b(2),
|
|
|
| # References to aggregates
|
| [], # empty array
|
| [ 42 ], # only one elem
|
| [< a b c>],
|
| {}, # empty hash
|
| { a => 42 }, # only one elem
|
| { :a(1), :b(2), :c(3) },
|
|
|
| [ 3..42 ],
|
| # Infinite arrays, commented because they take infram and inftime in
|
| # current Pugs
|
| #?pugs emit #
|
| #?rakudo emit # Inf takes infram and inftime
|
| [ 3..Inf ],
|
| #?pugs emit #
|
| #?rakudo emit # Inf takes infram and inftime
|
| [ -Inf..Inf ],
|
| #?pugs emit #
|
| #?rakudo emit # Inf takes infram and inftime
|
| [ 3..42, 17..Inf, -Inf..5 ],
|
|
|
| # Nested things
|
| { a => [1,2,3] }, # only one elem
|
| [ [1,2,3] ], # only one elem
|
| { a => [1,2,3], b => [4,5,6] },
|
| [ { :a(1) }, { :b(2), :c(3) } ],
|
| );
|
|
|
| plan 11 + 2*@tests;
|
| #?pugs emit force_todo 8, 45..50, 94, 96;
|
|
|
| #?pugs emit unless $?PUGS_BACKEND eq "BACKEND_PUGS" {
|
| #?pugs emit skip_rest "eval() not yet implemented in $?PUGS_BACKEND.";
|
| #?pugs emit exit;
|
| #?pugs emit }
|
|
|
|
|
Highlighted:
small|full
From t/spec/S02-names_and_variables/perl.t lines 68–148 (no results): (skip)
| # L<S02/Names and Variables/To get a Perlish representation of any object>
|
| # Quoting S02 (emphasis added):
|
| # To get a Perlish representation of any data value, use the .perl method.
|
| # This will put quotes around strings, square brackets around list values,
|
| # curlies around hash values, etc., **such that standard Perl could reparse
|
| # the result**.
|
| {
|
| for @tests -> $obj {
|
| my $s = (~$obj).subst(/\n/, '', :g);
|
| ok eval($obj.perl) eq $obj,
|
| "($s.perl()).perl returned something whose eval()ed stringification is unchanged";
|
| is ~WHAT(eval($obj.perl)), ~$obj.WHAT,
|
| "($s.perl()).perl returned something whose eval()ed .WHAT is unchanged";
|
| }
|
| }
|
|
|
| # Recursive data structures
|
| #?rakudo skip 'recursive data structure'
|
| {
|
| my $foo = [ 42 ]; $foo[1] = $foo;
|
| is $foo[1][1][1][0], 42, "basic recursive arrayref";
|
|
|
| #?pugs skip 'hanging test'
|
| is ~$foo.perl.eval, ~$foo,
|
| ".perl worked correctly on a recursive arrayref";
|
| }
|
|
|
| #?rakudo skip 'recursive data structure'
|
| {
|
| my $foo = { a => 42 }; $foo<b> = $foo;
|
| is $foo<b><b><b><a>, 42, "basic recursive hashref";
|
|
|
| #?pugs skip 'hanging test'
|
| is ~$foo.perl.eval, ~$foo,
|
| ".perl worked correctly on a recursive hashref";
|
| }
|
|
|
| #?rakudo skip '{...}.perl does not work'
|
| {
|
| my $foo = [ 42 ];
|
| my $bar = { a => 23 };
|
| $foo[1] = $bar;
|
| $bar<b> = $foo;
|
|
|
| is $foo[1]<b>[1]<b>[0], 42, "mixed arrayref/hashref recursive structure";
|
|
|
| #?pugs skip 'hanging test'
|
| is ~$foo.perl.eval, ~$foo,
|
| ".perl worked correctly on a mixed arrayref/hashref recursive structure";
|
| }
|
|
|
| {
|
| # test a bug reported by Chewie[] - apparently this is from S03
|
| is(eval((("f","oo","bar").keys).perl), <0 1 2>, ".perl on a .keys list");
|
| }
|
|
|
| {
|
| # test bug in .perl on result of hyperoperator
|
| # first the trivial case without hyperop
|
| my @foo = ([-1, -2], -3);
|
| is @foo.perl, '[[-1, -2], -3]', ".perl on a nested list";
|
|
|
| #?rakudo emit # parsefail on hyper operator
|
| my @hyp = -« ([1, 2], 3);
|
| # what it currently (r16460) gives
|
| #?rakudo 2 skip 'parsefail on hyper operator'
|
| #?pugs 2 todo 'bug'
|
| isnt @hyp.perl, '[(-1, -2), -3]', "strange inner parens from .perl on result of hyperop";
|
|
|
| # what it should give
|
| is @hyp.perl, '[[-1, -2], -3]', ".perl on a nested list result of hyper operator";
|
| }
|
|
|
| {
|
| # test for a rakudo (r29667) bug:
|
|
|
| my @list = (1, 2);
|
| push @list, eval @list.perl;
|
| #?rakudo todo "List.perl bug"
|
| is +@list, 4, 'eval(@list.perl) gives a list, not an array ref';
|
| }
|
Highlighted:
small|full
- To get a formatted representation of any scalar value, use the
.fmt('%03d') method to do an implicit sprintf on the value.
From t/spec/S02-names_and_variables/fmt.t lines 7–15 (no results): (skip)
| # L<S02/"Names and Variables"/"formatted representation"
|
| # of "any scalar value" ".fmt('%03d')">
|
| {
|
| is "Hi".fmt("[%s]"), "[Hi]", 'fmt() works with %s';
|
| is '3.141'.fmt("[%d]"), "[3]", "fmt() works with %d";
|
| #?rakudo todo 'precision in Num.fmt'
|
| is (5.6).fmt('%f'), '5.6', 'fmt() works with %f';
|
| }
|
|
|
Highlighted:
small|full
To format an array value separated by commas, supply a second argument: .fmt('%03d', ', '). To format a hash value or list of pairs, include formats for both key and value in the first string: .fmt('%s: %s', "\n").
From t/spec/S02-names_and_variables/fmt.t lines 16–30 (no results): (skip)
| # L<S02/"Names and Variables"/"format an array value"
|
| # "supply a second argument">
|
| {
|
| is (1.3,2.4,3).fmt("%d", "_"), "1_2_3", "fmt() works with plain lists";
|
| my @list = 'a'..'c';
|
| is @list.fmt('<%s>', ':'), '<a>:<b>:<c>', 'fmt() works with @ array';
|
|
|
| my $list = ['a', 'b', 'c'];
|
| is $list.fmt('[%s]', ','), '[a],[b],[c]', 'fmt() works with Array object';
|
|
|
| # single elem Array:
|
| $list = ['a'];
|
| is $list.fmt('<<%s>>', '!!!'), '<<a>>', 'fmt() works for single elem array';
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-names_and_variables/fmt.t lines 31–44 (no results): (skip)
| # L<S02/"Names and Variables"/"hash value" "formats for both key and value">
|
| {
|
| my $hash = {
|
| a => 1.3,
|
| b => 2.4,
|
| };
|
| my $str = $hash.fmt("%s:%d", "_");
|
| if $str eq "a:1_b:2" || $str eq "b:2_a:1" {
|
| pass "fmt() works with hashes";
|
| } else {
|
| flunk "fmt() fails to work with hashes";
|
| }
|
| }
|
|
|
Highlighted:
small|full
From t/spec/S02-names_and_variables/fmt.t lines 45–86 (no results): (skip)
| # L<S02/"Names and Variables"/"list of pairs" "formats for both key and value">
|
| #?rakudo skip ".fmt on list of pairs (?)"
|
| {
|
| # a single pair:
|
| my $pair = (100 => 'lovely');
|
| is $pair.fmt("%d ==> %s"), "100 ==> lovely", '.fmt works with a single pair';
|
|
|
| # list of a single pair:
|
| my @pairs = (100 => 'lovely');
|
| is(@pairs.fmt("%d ==> %s", "\n"), "100 ==> lovely", '.fmt works with lists of a single pair');
|
|
|
| # list of pair:
|
| @pairs = (a => 1.3, b => 2.4);
|
| is @pairs.fmt("%s:%d", "_"), "a:1_b:2", "fmt() works with lists of pairs";
|
| is @pairs.fmt("(%s => %f)", ""), "(a => 1.3)(b => 2.4)",
|
| "fmt() works with lists of pairs";
|
| }
|
|
|
| # Test defaults on $comma
|
| {
|
| is([1..3].fmt("%d"), "1 2 3", 'default $comma for array');
|
|
|
| my $hash = {
|
| a => 1.3,
|
| b => 2.4,
|
| };
|
| my $str = $hash.fmt("%s:%d");
|
| if $str eq "a:1\nb:2" || $str eq "b:2\na:1" {
|
| pass 'default $comma works with hashes';
|
| } else {
|
| flunk 'default $comma jfails to work with hashes';
|
| }
|
| }
|
|
|
| # Some failure modes
|
| {
|
| dies_ok { (1).fmt() }, 'scalar .fmt fails without $fmt';
|
| dies_ok { (1=>"a").fmt() }, 'pair .fmt fails without $fmt';
|
| dies_ok { (1,2).fmt() }, 'list .fmt fails without $fmt';
|
| dies_ok { [1,2].fmt() }, 'array .fmt fails without $fmt';
|
| dies_ok { {1=>"a"}.fmt() }, 'hash .fmt fails without $fmt';
|
| }
|
Highlighted:
small|full
- Subscripts now consistently dereference the container produced by whatever was to their left. Whitespace is not allowed between a variable name and its subscript. However, there are two ways to stretch the construct out visually. Since a subscript is a kind of postfix operator, there is a corresponding dot form of each subscript (
@foo.[1] and %bar.{'a'}) that makes the dereference a little more explicit. Constant string subscripts may be placed in angles, so %bar.{'a'} may also be written as %bar<a> or %bar.<a>. Additionally, you may insert extra whitespace using the unspace.
From t/spec/S02-literals/misc-interpolation.t lines 48–54 (no results): (skip)
| # L<S02/Names and Variables/form of each subscript>
|
| is("&func. () is where I live", '&func. () is where I live', '"&func. ()" should not interpolate');
|
| #?rakudo skip 'parse failure'
|
| #?DOES 1
|
| {
|
| is("&func_w_args("foo","bar"))", '[foo][bar])', '"&func_w_args(...)" should interpolate');
|
| }
|
Highlighted:
small|full
- Slicing is specified by the nature of the subscript, not by the sigil.
- The context in which a subscript is evaluated is no longer controlled by the sigil either. Subscripts are always evaluated in list context. (More specifically, they are evaluated in a variant of list context known as slice context, which preserves dimensional information so that you can do multi-dimensional slices using semicolons. However, each slice dimension evaluates its sublist in normal list context, so functions called as part of a subscript don't see a slice context. See S09 for more on slice context.)
From t/spec/S02-builtin_data_types/subscripts_and_context.t lines 9–57 (no results): (skip)
| # L<S02/Names and Variables/"The context in which a subscript is evaluated is no longer controlled by the sigil either.">
|
| {
|
| sub return_01 { my @sub_array = ("0", "1"); return @sub_array }
|
|
|
| my @array = <a b c d>;
|
| my @sliced = @array[return_01()];
|
| # @sliced *should* be <a b>, but it is <c>.
|
| # This is because return_012() is called in numeric context, and so return_012
|
| # returns the *number* of elems in @sub_array instead of the array @sub_array.
|
| is ~@sliced, "a b", "context inside of array subscripts for slices";
|
| }
|
|
|
| # Same for hashes.
|
| {
|
| sub return_ab { my @sub_array = <a b>; return @sub_array }
|
|
|
| my %hash = (a => 1, b => 2, c => 3);
|
| my @sliced = %hash{return_ab()};
|
| # @sliced *should* be ("1, "2").
|
| # The above for bug explanation.
|
| is ~@sliced, "1 2", "context inside of hash subscripts for slices";
|
| }
|
|
|
| # This time we return a single value.
|
| {
|
| sub return_3 { 3 }
|
| sub return_c { "c" }
|
|
|
| my @array = <a b c d e>;
|
| my %hash = (c => 12);
|
|
|
| is ~@array[return_3()], "d",
|
| "context inside of array subscripts in normal rvalue context";
|
| is ~%hash{return_c()}, 12,
|
| "context inside of hash subscripts in normal rvalue context";
|
|
|
| @array[return_3()] = "Z";
|
| %hash{return_c()} = 23;
|
|
|
| is @array[3], "Z", "context inside of array subscripts in lvalue context";
|
| is %hash<c>, 23, "context inside of hash subscripts in lvalue context";
|
|
|
| @array[3] = 15;
|
| @array[return_3()]++;
|
| %hash{return_c()}++;
|
|
|
| is @array[3], 16, 'context inside of array subscripts when used with &postfix:<++>';
|
| is %hash<c>, 24, 'context inside of hash subscripts when used with &postfix:<++>';
|
| }
|
Highlighted:
small|full
If you need to force inner context to item (scalar), we now have convenient single-character context specifiers such as + for numbers and ~ for strings:
$x = g(); # item context for g()
@x[f()] = g(); # list context for f() and g()
@x[f()] = +g(); # list context for f(), numeric item context for g()
@x[+f()] = g(); # numeric item context for f(), list context for g()
@x[f()] = @y[g()]; # list context for f() and g()
@x[f()] = +@y[g()]; # list context for f() and g()
@x[+f()] = @y[g()]; # numeric item context for f(), list context for g()
@x[f()] = @y[+g()]; # list context for f(), numeric item context for g()
%x{~f()} = %y{g()}; # string item context for f(), list context for g()
%x{f()} = %y{~g()}; # list context for f(), string item context for g()
Sigils used either as functions or as list prefix operators also force context, so these also work:
@x[$(g())] # item context for g()
@x[$ g()] # item context for g()
%x{$(g())} # item context for g()
%x{$ g()} # item context for g()
But note that these don't do the same thing:
@x[$g()] # call function in $g
%x{$g()} # call function in $g
- There is a need to distinguish list assignment from list binding. List assignment works much like it does in Perl 5, copying the values. There's a new
:= binding operator that lets you bind names to Array and Hash objects without copying, in the same way as subroutine arguments are bound to formal parameters. See S06 for more about binding.
- An argument list may be captured into an object with backslashed parens:
$args = \(1,2,3,:mice<blind>)
Values in a Capture object are parsed as ordinary expressions, then marked as positional or named. If the first positional is followed by a colon instead of a comma, it is marked as the invocant in case it finds itself in a context that cares.
Like List objects, Capture objects are immutable in the abstract, but evaluate their arguments lazily. Before everything inside a Capture is fully evaluated (which happens at compile time when all the arguments are constants), the eventual value may well be unknown. All we know is that we have the promise to make the bits of it immutable as they become known.
Capture objects may contain multiple unresolved iterators such as feeds or slices. How these are resolved depends on what they are eventually bound to. Some bindings are sensitive to multiple dimensions while others are not.
You may retrieve parts from a Capture object with a prefix sigil operator:
$args = \3; # same as "$args = \(3)"
@$args; # same as "Array($args)"
%$args; # same as "Hash($args)"
When cast into an array, you can access all the positional arguments; into a hash, all named arguments.
All prefix sigil operators accept one positional argument, evaluated in item context as a rvalue. They can interpolate in strings if called with parentheses. The special syntax form $() translates into $( $.ast // Str($/) ) to operate on the current match object; similarly @() and %() can extract positional and named submatches.
Capture objects fill the ecological niche of references in Perl 6. You can think of them as "fat" references, that is, references that can capture not only the current identity of a single object, but also the relative identities of several related objects. Conversely, you can think of Perl 5 references as a degenerate form of Capture when you want to refer only to a single item.
- A signature object (
Signature) may be created with colon-prefixed parens:
From t/spec/S02-names_and_variables/signature.t lines 10–114 (no results): (skip)
| #L<S02/Names and Variables/"A signature object">
|
|
|
| # Basic siglist binding --
|
| # $x := 42; # is sugar for
|
| # :($x) := 42; # which in turn is sugar for
|
| # :($x).infix:<:=>(42);
|
|
|
| #?rakudo skip '.infix:<:=>()'
|
| #?pugs skip "todo: signature objects"
|
| {
|
| my $x;
|
| my $siglist = eval :($x);
|
| $siglist.infix:<:=>(42);
|
| is($x, 42, "basic siglist binding works");
|
| dies_ok { $x++ }, "binding was really a binding, not an assignment";
|
| }
|
|
|
| # same with direct := syntax
|
| #?rakudo skip '.infix:='
|
| {
|
| my $x;
|
|
|
| my $siglist = :($x);
|
| $siglist := 42;
|
| #?pugs 2 todo 'feature'
|
| is($x, 42, "basic siglist binding works");
|
| dies_ok { $x++ }, "binding was really a binding, not an assignment";
|
| }
|
|
|
| #?rakudo skip '.infix:<:=>()'
|
| {
|
| my ($x, $y, $z);
|
| my $siglist = eval ':($x,$y,$z)';
|
| try { $siglist.infix:<:=>(1,2,3)};
|
| #?pugs todo 'feature'
|
| is("$x $y $z", "1 2 3", "siglist bindings works");
|
| }
|
|
|
| # Same, but more complex
|
| #?rakudo skip '.infix:<:=>()'
|
| {
|
| my ($x, @y, @rest);
|
| my $siglist = eval ':($x,@y,*@rest)';
|
| try { $siglist.infix:<:=>(42,[13,17],5,6,7) };
|
| #?pugs todo 'feature'
|
| is("$x!@y[]!@rest[]", "42!13 17!5 6 7", "complex siglist bindings works (1)");
|
| }
|
|
|
| #?rakudo skip '.infix:<:=>()'
|
| {
|
| my ($x);
|
| my $siglist = eval ':($x?)';
|
| try { $siglist.infix:<:=>() };
|
| #?pugs todo 'feature'
|
| ok(!VAR($x).defined, "complex siglist bindings works (2)");
|
| }
|
|
|
| # &sub.signature should return a Siglist object
|
| #?rakudo skip 'runtime error "arguments passed (0) - 4 params expected"'
|
| {
|
| sub foo1 ($a, $b) {}
|
| my $siglist = :($a, $b);
|
|
|
| ok $siglist,
|
| "a subroutine's siglist can be accessed via .signature (1-1)";
|
| #?pugs todo 'feature'
|
| ok $siglist === &foo1.signature,
|
| "a subroutine's siglist can be accessed via .signature (1-2)";
|
| }
|
|
|
| # Same as above, but more complex
|
| #?rakudo skip 'infix:<===>'
|
| {
|
| my sub foo (Num $a, $b?, *@rest) {}
|
| my $siglist = :(Num $a, $b?, *@rest);
|
|
|
| #?pugs todo 'feature'
|
| cmp_ok $siglist, &infix:<===>, &foo.signature ,
|
| "a subroutine's siglist can be accessed via .signature (2)";
|
| }
|
|
|
| #?rakudo 999 skip 'Rest not properly fudged'
|
| {
|
| my sub foo ($a, $b) {}
|
| my $siglist = :($a);
|
|
|
| ok !($siglist === try { &foo.signature }),
|
| "a subroutine's siglist can be accessed via .signature (3)";
|
| }
|
|
|
| # User-customized binding
|
| {
|
| my $x = 42;
|
| my $siglist = eval '(:($x)) but role {
|
| method infix:<:=> {
|
| # do nothing
|
| }
|
| }';
|
|
|
| try { $siglist.infix:<:=>(23) };
|
| is $x, 42, "user-defined binding worked as expected (1)";
|
| lives_ok { $x++}, "user-defined binding worked as expected (2)";
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
my ::MySig ::= :(Int, Num, Complex, Status)
Expressions inside the signature are parsed as parameter declarations rather than ordinary expressions. See S06 for more details on the syntax for parameters.
Signature objects bound to type variables (as in the example above) may be used within other signatures to apply additional type constraints. When applied to a Capture argument, the signature allows you to take the types of the capture's arguments from MySig, but declare the (untyped) variable names yourself via an additional signature in parentheses:
sub foo (Num Dog|Cat $numdog, MySig $a ($i,$j,$k,$mousestatus)) {...}
foo($mynumdog, \(1, 2.7182818, 1.0i, statmouse());
- Unlike in Perl 5, the notation
&foo merely stands for the foo function as a Routine object without calling it. You may call any Code object by dereferencing it with parens (which may, of course, contain arguments):
&foo($arg1, $arg2);
Whitespace is not allowed before the parens because it is parsed as a postfix. As with any postfix, there is also a corresponding .() operator, and you may use the "unspace" form to insert optional whitespace and comments between the backslash and either of the postfix forms:
&foo\ ($arg1, $arg2);
&foo\ .($arg1, $arg2);
&foo\#[
embedded comment
].($arg1, $arg2);
Note however that the parentheses around arguments in the "normal" named forms of function and method calls are not postfix operators, so do not allow the .() form, because the dot is indicative of an actual dereferencing operation, which the named forms aren't doing. You may, however, use "unspace" to install extra space before the parens in the forms:
foo() # okay
foo\ () # okay
foo.() # means foo().()
.foo() # okay
.foo\ () # okay
.foo.() # means .foo().()
$.foo() # okay
$.foo\ () # okay
$.foo.() # means $.foo().()
If you do use the dotty form on these special forms, it will assume you wanted to call the named form without arguments, and then dereference the result of that.
- With multiple dispatch,
&foo may actually be the name of a set of candidate functions (which you can use as if it were an ordinary function). However, in that case &foo by itself is not sufficient to uniquely name a specific function. To do that, the type may be refined by using a signature literal as a postfix operator:
&foo:(Int,Num)
It still just returns the Routine object. A call may also be partially applied by using the .assuming method:
&foo.assuming(1,2,3,:mice<blind>)
- Slicing syntax is covered in S09. A multidimensional slice will be done with semicolons between individual slice sublists. Each such slice sublist is evaluated lazily.
- To make a slice subscript return something other than values, append an appropriate adverb to the subscript.
From t/spec/S03-operators/subscript-adverbs.t lines 7–197 (no results): (skip)
| # L<S02/Names and Variables/appropriate adverb to the subscript>
|
|
|
| # Adverbs on array subscripts
|
| # :p
|
| {
|
| my @array = <A B>;
|
|
|
| isa_ok @array[0]:p, Pair,
|
| ":p on an array returned a Pair";
|
| is ~(@array[0]:p), "0\tA",
|
| ":p on an array returned the correct pair";
|
|
|
| lives_ok { (@array[0]:p).value = "a" }, 'can assign to (@array[0]:p).value';
|
| is @array[0], "a",
|
| ":p on an array returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(@array[0,1]:p), 2,
|
| ":p on an array returned a two-elem array";
|
| is ~(@array[0,1]:p), "0\ta 1\tB",
|
| ":p on an array returned a two-elem array consisting of the correct pairs";
|
|
|
| is +(@array[42,23]:p), 0, ":p on an array weeded out non-existing entries (1)";
|
| is ~(@array[42,23]:p), "", ":p on an array weeded out non-existing entries (2)";
|
| }
|
|
|
| # :kv
|
| {
|
| my @array = <A B>;
|
|
|
| is +(@array[0]:kv), 2,
|
| ":kv on an array returned a two-elem array";
|
| is ~(@array[0]:kv), "0 A",
|
| ":kv on an array returned the correct two-elem array";
|
|
|
| lives_ok {(@array[0]:kv)[1] = "a"}, 'can assign to :kv subscripts';
|
| is @array[0], "a",
|
| ":kv on an array returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(@array[0,1]:kv), 4,
|
| ":kv on an array returned a four-elem array";
|
| is ~(@array[0,1]:kv), "0 a 1 B",
|
| ":kv on an array returned the correct four-elem array";
|
|
|
| is +(@array[42,23]:kv), 0, ":kv on an array weeded out non-existing entries (1)";
|
| is ~(@array[42,23]:kv), "", ":kv on an array weeded out non-existing entries (2)";
|
| }
|
|
|
| # :k
|
| {
|
| my @array = <A B>;
|
|
|
| is +(@array[0]:k), 1,
|
| ":k on an array returned an one-elem array";
|
| is ~(@array[0]:k), "0",
|
| ":k on an array returned the correct one-elem array";
|
|
|
| is +(@array[0,1]:k), 2,
|
| ":k on an array returned a tow-elem array";
|
| is ~(@array[0,1]:k), "0 1",
|
| ":k on an array returned the correct two-elem array";
|
|
|
| is +(@array[42,23]:k), 0, ":k on an array weeded out non-existing entries (1)";
|
| is ~(@array[42,23]:k), "", ":k on an array weeded out non-existing entries (2)";
|
| }
|
|
|
| # :v
|
| {
|
| my @array = <A B>;
|
|
|
| is +(@array[0]:v), 1,
|
| ":v on an array returned an one-elem array";
|
| is ~(@array[0]:v), "A",
|
| ":v on an array returned the correct one-elem array";
|
|
|
| lives_ok {@array[0]:v = "a"}, 'can assign to @array[0]:v';
|
| is @array[0], "a",
|
| ":v on an array returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(@array[0,1]:v), 2,
|
| ":v on an array returned a tow-elem array";
|
| is ~(@array[0,1]:v), "a B",
|
| ":v on an array returned the correct two-elem array";
|
|
|
| is +(@array[42,23]:v), 0, ":v on an array weeded out non-existing entries (1)";
|
| is ~(@array[42,23]:v), "", ":v on an array weeded out non-existing entries (2)";
|
| }
|
|
|
| # Adverbs on hash subscripts
|
| # :p
|
| {
|
| my %hash = (0 => "A", 1 => "B");
|
|
|
| isa_ok %hash<0>:p, Pair,
|
| ":p on a hash returned a Pair";
|
| is ~(%hash<0>:p), "0\tA",
|
| ":p on a hash returned the correct pair";
|
|
|
| lives_ok { (%hash<0>:p).value = "a"}, 'can assign to %hash<0>:p.value';
|
| is %hash<0>, "a",
|
| ":p on a hash returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(%hash<0 1>:p), 2,
|
| ":p on a hash returned a two-elem array";
|
| is ~(%hash<0 1>:p), "0\ta 1\tB",
|
| ":p on a hash returned a two-elem array consisting of the correct pairs";
|
|
|
| is +(%hash<42 23>:p), 0, ":p on a hash weeded out non-existing entries (1)";
|
| is ~(%hash<42 23>:p), "", ":p on a hash weeded out non-existing entries (2)";
|
| }
|
|
|
| # :kv
|
| {
|
| my %hash = (0 => "A", 1 => "B");
|
|
|
| is +(%hash<0>:kv), 2,
|
| ":kv on a hash returned a two-elem array";
|
| is ~(%hash<0>:kv), "0 A",
|
| ":kv on a hash returned the correct two-elem array";
|
|
|
| lives_ok {(%hash<0>:kv)[1] = "a"}, 'can assign to %hash<0>:kv.[1]';
|
| is %hash<0>, "a",
|
| ":kv on a hash returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(%hash<0 1>:kv), 4,
|
| ":kv on a hash returned a four-elem array";
|
| is ~(%hash<0 1>:kv), "0 a 1 B",
|
| ":kv on a hash returned the correct four-elem array";
|
|
|
| is +(%hash<42 23>:kv), 0, ":kv on a hash weeded out non-existing entries (1)";
|
| is ~(%hash<42 23>:kv), "", ":kv on a hash weeded out non-existing entries (2)";
|
| }
|
|
|
| # :k
|
| {
|
| my %hash = (0 => "A", 1 => "B");
|
|
|
| is +(%hash<0>:k), 1,
|
| ":k on a hash returned an one-elem array";
|
| is ~(%hash<0>:k), "0",
|
| ":k on a hash returned the correct one-elem array";
|
|
|
| is +(%hash<0 1>:k), 2,
|
| ":k on a hash returned a tow-elem array";
|
| is ~(%hash<0 1>:k), "0 1",
|
| ":k on a hash returned the correct two-elem array";
|
|
|
| is +(%hash<42 23>:k), 0, ":k on a hash weeded out non-existing entries (1)";
|
| is ~(%hash<42 23>:k), "", ":k on a hash weeded out non-existing entries (2)";
|
| }
|
|
|
| # :v
|
| {
|
| my %hash = (0 => "A", 1 => "B");
|
|
|
| is +(%hash<0>:v), 1,
|
| ":v on a hash returned an one-elem array";
|
| is ~(%hash<0>:v), "A",
|
| ":v on a hash returned the correct one-elem array";
|
|
|
| lives_ok {%hash<0>:v = "a"}, 'can assing to %hash<0>:v';
|
| is %hash<0>, "a",
|
| ":v on a hash returns lvalues (like normal subscripts do as well)";
|
|
|
| is +(%hash<0 1>:v), 2,
|
| ":v on a hash returned a tow-elem array";
|
| is ~(%hash<0 1>:v), "a B",
|
| ":v on a hash returned the correct two-elem array";
|
|
|
| is +(%hash<42 23>:v), 0, ":v on a hash weeded out non-existing entries (1)";
|
| is ~(%hash<42 23>:v), "", ":v on a hash weeded out non-existing entries (2)";
|
| }
|
|
|
| # The adverbial forms weed out non-existing entries, but undefined (but
|
| # existing) entries should be unaffected by this rule.
|
| {
|
| my @array = (42, undef, 23);
|
|
|
| is +(@array[0,1,2]:kv), 6,
|
| "undefined but existing entries should not be weeded out (1)";
|
| is ~(@array[0,1,2]:kv), "0 42 1 2 23",
|
| "undefined but existing entries should not be weeded out (2)";
|
| }
|
|
|
| {
|
| my %hash = (0 => 42, 1 => undef, 2 => 23);
|
|
|
| is +(%hash<0 1 2>:kv), 6,
|
| "undefined but existing entries should not be weeded out (3)";
|
| is ~(%hash<0 1 2>:kv), "0 42 1 2 23",
|
| "undefined but existing entries should not be weeded out (4)";
|
| }
|
Highlighted:
small|full
@array = <A B>;
@array[0,1,2]; # returns 'A', 'B', undef
@array[0,1,2] :p; # returns 0 => 'A', 1 => 'B'
@array[0,1,2] :kv; # returns 0, 'A', 1, 'B'
@array[0,1,2] :k; # returns 0, 1
@array[0,1,2] :v; # returns 'A', 'B'
%hash = (:a<A>, :b<B>);
%hash<a b c>; # returns 'A', 'B', undef
%hash<a b c> :p; # returns a => 'A', b => 'B'
%hash<a b c> :kv; # returns 'a', 'A', 'b', 'B'
%hash<a b c> :k; # returns 'a', 'b'
%hash<a b c> :v; # returns 'A', 'B'
These adverbial forms all weed out non-existing entries. You may also perform an existence test, which will return true if all the elements of the slice exist:
if %hash<a b c> :exists {...}
likewise,
my ($a,$b,$c) = %hash<a b c> :delete;
From t/spec/S32-hash/delete.t lines 6–47 (no results): (skip)
| # L<S02/Names and Variables/:delete>
|
|
|
| sub gen_hash {
|
| my %h;
|
| my $i = 0;
|
| for 'a'..'z' { %h{$_} = ++$i; }
|
| return %h;
|
| }
|
|
|
| {
|
| my %h1 = gen_hash;
|
|
|
| my $b = %h1<b>;
|
| is %h1<b>:delete, $b, "Test for delete single key.";
|
| }
|
|
|
| #?rakudo todo 'Slices'
|
| {
|
| my %h1 = gen_hash;
|
| my @cde = %h1<c d e>;
|
| is %h1<c d e>:delete, @cde, "test for delete multiple keys.";
|
| }
|
|
|
|
|
| my %hash = (a => 1, b => 2, c => 3, d => 4);
|
|
|
| is +%hash, 4, "basic sanity (2)";
|
| is ~(%hash<a>:delete), "1",
|
| "deletion of a hash element returned the right value";
|
| is +%hash, 3, "deletion of a hash element";
|
| {
|
| is ~(%hash{"c", "d"}:delete), "3 4",
|
| "deletion of hash elements returned the right values";
|
| is +%hash, 1, "deletion of hash elements";
|
| }
|
| ok !defined(%hash{"a"}), "deleted hash elements are really deleted";
|
|
|
| {
|
| my $a = 1;
|
| eval_dies_ok '$a :delete', "Can't :delete a scalar";
|
| }
|
|
|
Highlighted:
small|full
deletes the entries "en passant" while returning them. (Of course, any of these forms also work in the degenerate case of a slice containing a single index.) Note that these forms work by virtue of the fact that the subscript is the topmost previous operator. You may have to parenthesize or force list context if some other operator that is tighter than comma would appear to be topmost:
1 + (%hash{$x} :delete);
$x = (%hash{$x} :delete);
($x) = %hash{$x} :delete;
(The situation does not often arise for the slice modifiers above because they are usually used in list context, which operates at comma precedence.)
- In numeric context (i.e. when cast into
Int or Num), a Hash object becomes the number of pairs contained in the hash. In a boolean context, a Hash object is true if there are any pairs in the hash. In either case, any intrinsic iterator would be reset. (If hashes do carry an intrinsic iterator (as they do in Perl 5), there will be a .reset method on the hash object to reset the iterator explicitly.)
- Sorting a list of pairs should sort on their keys by default, then on their values. Sorting a list of lists should sort on the first elements, then the second elements, etc. For more on
sort see S29.
- Many of the special variables of Perl 5 are going away. Those that apply to some object such as a filehandle will instead be attributes of the appropriate object. Those that are truly global will have global alphabetic names, such as
$*PID or @*ARGS.
From t/spec/S02-names_and_variables/varnames.t lines 7–17 (no results): (skip)
| # L<S02/Names and Variables/special variables of Perl 5 are going away>
|
|
|
| # things that should be valid
|
| # these tests are probably going to fail if declaring a magical var ever becomes unallowed
|
| eval_lives_ok 'my $!', '$! parses ok';
|
| eval_lives_ok 'my $/', 'as does $/';
|
|
|
| # things that should be invalid
|
| eval_dies_ok 'my $f!ao = "beh";', "but normal varnames can't have ! in their name";
|
| eval_dies_ok 'my $fo:o::b:ar = "bla"', "var names can't have colons in their names either";
|
|
|
Highlighted:
small|full
- Any remaining special variables will be lexically scoped. This includes
$_ and @_, as well as the new $/, which is the return value of the last regex match. $0, $1, $2, etc., are aliases into the $/ object.
- The
$#foo notation is dead. Use @foo.end or @foo[*-1] instead. (Or @foo.shape[$dimension] for multidimensional arrays.)
From t/spec/S02-names_and_variables/names.t lines 9–59 (no results): (skip)
| # L<S02/Names/>
|
| # syn r14552
|
|
|
| {
|
| my $mountain = 'Hill';
|
| $Terrain::mountain = 108;
|
| $Terrain::Hill::mountain = 1024;
|
| our $river = 'Terrain::Hill';
|
| is($mountain, 'Hill', 'basic variable name');
|
| is($Terrain::mountain, 108, 'variable name with package');
|
| #?rakudo skip "Parse error"
|
| is(Terrain::<$mountain>, 108, 'variable name with sigil not in front of package');
|
| is($Terrain::Hill::mountain, 1024, 'variable name with 2 deep package');
|
| #?rakudo 3 skip "Parse error"
|
| is(Terrain::Hill::<$mountain>, 1024, 'varaible name with sigil not in front of 2 package levels deep');
|
| is($Terrain::($mountain)::mountain, 1024, 'variable name with a package name partially given by a variable ');
|
| is($::($river)::mountain, 1024, 'variable name with package name completely given by variable');
|
| }
|
|
|
| {
|
| my $bear = 2.16;
|
| is($bear, 2.16, 'simple variable lookup');
|
| #?rakudo skip "Parse error"
|
| is($::{'bear'}, 2.16, 'variable lookup using $::{\'foo\'}');
|
| #?rakudo skip "Parse error"
|
| is(::{'$bear'}, 2.16, 'variable lookup using ::{\'$foo\'}');
|
| #?rakudo skip "Parse error"
|
| is($::<bear>, 2.16, 'variable lookup using $::<foo>');
|
| #?rakudo skip "unimpl get_pmc_keyed"
|
| is(::<$bear>, 2.16, 'variable lookup using ::<$foo>');
|
| }
|
|
|
| #?rakudo skip 'parse error'
|
| {
|
| my $::<!@#$> = 2.22;
|
| is($::{'!@#$'}, 2.22, 'variable lookup using $::{\'symbols\'}');
|
| is(::{'$!@#$'}, 2.22, 'variable lookup using ::{\'$symbols\'}');
|
| is($::<!@#$>, 2.22, 'variable lookup using $::<symbols>');
|
| is(::<$!@#$>, 2.22, 'variable lookup using ::<$symbols>');
|
|
|
| }
|
|
|
| # RT #65138, Foo::_foo() parsefails
|
| {
|
| module A {
|
| sub _b() { 'sub A::_b' }
|
| }
|
| is A::_b(), 'sub A::_b', 'A::_b() call works';
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
- An identifier is composed of an alphabetic character followed by any sequence of alphanumeric characters. The definitions of alphabetic and numeric include appropriate Unicode characters. Underscore is always considered alphabetic. An identifier may also contain isolated apostrophes or hyphens provided the next character is alphabetic.
A name is anything that is a legal part of a variable name (not counting the sigil). This includes
$foo # simple identifiers
$Foo::Bar::baz # compound identifiers separated by ::
$Foo::($bar)::baz # compound identifiers that perform interpolations
$42 # numeric names
$! # certain punctuational variables
When not used as a sigil, the semantic function of :: within a name is to force the preceding portion of the name to be considered a package through which the subsequent portion of the name is to be located. If the preceding portion is null, it means the package is unspecified and must be searched for according to the nature of what follows. Generally this means that an initial :: following the main sigil is a no-op on names that are known at compile time, though ::() can also be used to introduce an interpolation (see below). Also, in the absence of another sigil, :: can serve as its own sigil indicating intentional use of a not-yet-declared package name.
Unlike in Perl 5, if a sigil is followed by comma, semicolon, a colon not followed by an identifier, or any kind of bracket or whitespace (including Unicode brackets and whitespace), it will be taken to be a sigil without a name rather than a punctuational variable. This allows you to use sigils as coercion operators:
print $( foo() ) # foo called in item context
print @@( foo() ) # foo called in slice context
In declarative contexts bare sigils may be used as placeholders for anonymous variables:
my ($a, $, $c) = 1..3;
print unless (state $)++;
Outside of declarative contexts you may use * for a placeholder:
($a, *, $c) = 1..3;
Attempts to say something like:
($a, $, $c) = 1..3;
will result in the message, "Anonymous variable requires declarator".
- Ordinary package-qualified names look like in Perl 5:
$Foo::Bar::baz # the $baz variable in package Foo::Bar
Sometimes it's clearer to keep the sigil with the variable name, so an alternate way to write this is:
Foo::Bar::<$baz>
This is resolved at compile time because the variable name is a constant.
- The following pseudo-package names are reserved at the front of a name:
From t/spec/S02-names_and_variables/variables-and-packages.t lines 7–18 (no results): (skip)
| # L<S02/Names/"The following pseudo-package names are reserved">
|
| ok !eval('module MY; 1'), 'MY is an out of scope name';
|
| ok !eval('module OUR; 1'), 'OUR is an out of scope name';
|
| ok !eval('module GLOBAL; 1'), 'GLOBAL is an out of scope name';
|
| ok !eval('module PROCESS; 1'), 'PROCESS is an out of scope name';
|
| ok !eval('module OUTER; 1'), 'OUTER is an out of scope name';
|
| ok !eval('module CALLER; 1'), 'CALLER is an out of scope name';
|
| ok !eval('module CONTEXT; 1'), 'CONTEXT is an out of scope name';
|
| ok !eval('module SUPER; 1'), 'SUPER is an out of scope name';
|
| ok !eval('module COMPILING; 1'), 'COMPILING is an out of scope name';
|
|
|
|
|
Highlighted:
small|full
MY # Symbols in the current lexical scope (aka $?SCOPE)
OUR # Symbols in the current package (aka $?PACKAGE)
CORE # Outermost lexical scope, definition of standard Perl
GLOBAL # Interpreter-wide package symbols, really CORE::GLOBAL
PROCESS # Process-related globals (superglobals), CORE::PROCESS
COMPILING # Lexical symbols in the scope being compiled
CALLER # Contextual symbols in the immediate caller's lexical scope
CONTEXT # Contextual symbols in my or any caller's lexical scope
The following relative names are also reserved but may be used anywhere in a name:
OUTER # Symbols in the next outer lexical scope
UNIT # Symbols in the outermost lexical scope of compilation unit
SETTING # Lexical symbols in the unit's DSL (usually CORE)
PARENT # Symbols in this package's parent package (or lexical scope)
The following is reserved at the beginning of method names in method calls:
SUPER # Package symbols declared in inherited classes
Other all-caps names are semi-reserved. We may add more of them in the future, so you can protect yourself from future collisions by using mixed case on your top-level packages. (We promise not to break any existing top-level CPAN package, of course. Except maybe ACME, and then only for coyotes.)
The file's scope is known as UNIT, but there are one or more lexical scopes outside of that corresponding to the linguistic setting (often known as the prelude in other cultures). Hence, the SETTING scope is equivalent to UNIT::OUTER. For a standard Perl program SETTING is the same as CORE, but various startup options (such as -n or -p) can put you into a domain specific language, in which case CORE remains the scope of the standard language, while SETTING represents the scope defining the DSL that functions as the setting of the current file. See also the -L/--language switch described in S19-commandline. If a setting wishes to gain control of the main execution, it merely needs to declare a MAIN routine as documented in S06. In this case the ordinary execution of the user's code is suppressed; instead, execution of the user's code is entirely delegated to the setting's MAIN routine, which calls back to the user's lexically embedded code with YOU_ARE_HERE.
Note that, since the UNIT of an eval is the eval string itself, the SETTING of an eval is the language in effect at the point of the eval, not the language in effect at the top of the file. (You may, however, use OUTER::SETTING to get the setting of the code that is executing the eval.) In more traditional terms, the normal program is functioning as the "prelude" of the eval.
So the outermost lexical scopes nest like this, traversed via OUTER:
CORE <= SETTING < UNIT < (your_block_here)
The outermost packages scopes nest like this, traversed via PARENT:
GLOBAL < (your_package_here)
You main program starts up in the GLOBAL package and the UNIT lexical scope. Whenever anything is declared with "our" semantics, it inserts a name into both the current package and the current lexical scope. (And "my" semantics only insert into the current lexical scope.) Note that the standard setting, CORE, is a lexical scope, not a package; the various items that are defined within (or imported into) CORE are *not* in GLOBAL, which is pretty much empty when your program starts compiling, and mostly only contains things you either put there yourself, or some other module put there because you used that module. In general things defined within (or imported into) CORE should only be declared or imported with "my" semantics. All Perl code can see CORE anyway as the outermost lexical scope, so there's no need to also put such things into GLOBAL.
The GLOBAL package itself is rooted at CORE::GLOBAL. The PROCESS package is rooted at CORE::PROCESS. You will note that PROCESS is not the parent of GLOBAL. However, searching up the dynamic stack for context variables will look in all nested dynamic scopes (mapped automatically to each call's lexical scope, not package scope) out to UNIT; once all the dynamic scopes are exhausted, it also looks in the GLOBAL package and then in the PROCESS package, so $*OUT typically finds the process's standard output handle.
Any context variable declared with our in the user's main program (specifically, the part compiled with GLOBAL as the current package) is accessible (by virtue of being in GLOBAL) as a context variable even if not directly in the dynamic call chain. Note that context vars do *not* look in CORE for anything. (They might look in SETTING if you're running under a setting distinct from CORE, if that setting defines a dynamic scope outside your main program, such as for the -n or -p switch.)
- You may interpolate a string into a package or variable name using
::($expr) where you'd ordinarily put a package or variable name. The string is allowed to contain additional instances of ::, which will be interpreted as package nesting. You may only interpolate entire names, since the construct starts with ::, and either ends immediately or is continued with another :: outside the parens. Most symbolic references are done with this notation:
From t/spec/S02-names/symbolic-deref.t lines 11–130 (no results): (skip)
| # L<S02/Names/Most symbolic references are done with this notation:>
|
| {
|
| my $a_var = 42;
|
| my $b_var = "a_var";
|
|
|
| is $::($b_var), 42, 'basic symbolic scalar dereferentiation works';
|
| }
|
|
|
| {
|
| my @a_var = <a b c>;
|
| my $b_var = "a_var";
|
|
|
| is @::($b_var)[1], "b", 'basic symbolic array dereferentiation works';
|
| }
|
|
|
| {
|
| my %a_var = (a => 42);
|
| my $b_var = "a_var";
|
|
|
| is %::($b_var)<a>, 42, 'basic symbolic hash dereferentiation works';
|
| }
|
|
|
| {
|
| my &a_var := { 42 };
|
| my $b_var = "a_var";
|
|
|
| is &::($b_var)(), 42, 'basic symbolic code dereferentiation works';
|
| }
|
|
|
| {
|
| $pugs::is::cool = 42;
|
| my $cool = "cool";
|
| my $pugsis = 'pugs::is';
|
|
|
| is $::("pugs")::is::($cool), 42, 'not so basic symbolic dereferentiation works';
|
| is $::($pugsis)::($cool), 42, 'symbolic derefertiation with multiple packages in one variable works';
|
| eval_dies_ok('$::($pugsis)cool', '$::($foo)bar is illegal');
|
| }
|
|
|
| {
|
| my $result;
|
|
|
| try {
|
| my $a_var is context = 42;
|
| my $sub = sub { $::("CALLER")::("a_var") };
|
| $result = $sub();
|
| };
|
|
|
| is $result, 42, "symbolic dereferentation works with ::CALLER, too";
|
| }
|
|
|
| # Symbolic dereferentiation of Unicode vars (test primarily aimed at PIL2JS)
|
| {
|
| my $äöü = 42;
|
| is $::("äöü"), 42, "symbolic dereferentiation of Unicode vars works";
|
| }
|
|
|
| # Symbolic dereferentiation of globals
|
| {
|
| sub GLOBAL::a_global_sub () { 42 }
|
| is &::("*::a_global_sub")(), 42,
|
| "symbolic dereferentiation of globals works (1)";
|
|
|
| $*a_global_var = 42;
|
| is $::("*::a_global_var"), 42,
|
| "symbolic dereferentiation of globals works (2)";
|
| }
|
|
|
| # Symbolic dereferentiation of globals *without the star*
|
| {
|
| cmp_ok $::("*IN"), &infix:<===>, $*IN,
|
| "symbolic dereferentiation of globals works (3)";
|
| cmp_ok $::("IN"), &infix:<===>, $*IN,
|
| "symbolic dereferentiation of globals without the star works";
|
|
|
| # XXX - should be =:= rather than ~~, but &say =:= &say is currently false.:(
|
| #cmp_ok &::("*say"), &infix:<=:=>, &say,
|
| cmp_ok &::("*say"), &infix:<~~>, &say,
|
| "symbolic dereferentiation of global subs works";
|
| #cmp_ok &::("say"), &infix:<=:=>, &say,
|
| cmp_ok &::("say"), &infix:<~~>, &say,
|
| "symbolic dereferentiation of global subs without the star works (1)";
|
|
|
| ok &::("true")(42),
|
| "symbolic dereferentiation of global subs without the star works (2)";
|
| is &::("int")(3.1), 3,
|
| "symbolic dereferentiation of global subs without the star works (3)";
|
| }
|
|
|
| # Symbolic dereferentiation of type vars
|
| {
|
| cmp_ok ::Array, &infix:<===>, ::("Array"),
|
| "symbolic dereferentiation of type vars works (1)";
|
| }
|
|
|
| {
|
| class A::B::C {};
|
| my $ok = ::A::B::C === ::A::("B")::C;
|
| ok $ok, "symbolic dereferentiation of (own) type vars works (2)";
|
| }
|
|
|
| # Symbolic dereferentiation syntax should work with $?SPECIAL etc. too.
|
| # Note: I'm not 100% sure this is legal syntax. If it turns out it isn't, we'll
|
| # have to s/ok/dies_ok/.
|
| {
|
| try { this_will_die_and_therefore_set_dollar_exclamation_mark };
|
| ok $::("!"), "symbolic dereferentiation works with special chars (1)";
|
| # ok $::!, "symbolic dereferentiation works with special chars (2)";
|
| ok %::("*ENV"), "symbolic dereferentiation works with special chars (3)";
|
| # ok %::*ENV, "symbolic dereferentiation works with special chars (4)";
|
| }
|
|
|
| # Symdereffing should find package vars as well:
|
| {
|
| our $symderef_test_var = 42;
|
|
|
| is $::("symderef_test_var"), 42, "symbolic dereferentiation works with package vars";
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
$foo = "Bar";
$foobar = "Foo::Bar";
$::($foo) # package-scoped $Bar
$::("MY::$foo") # lexically-scoped $Bar
$::("*::$foo") # global $Bar
$::($foobar) # $Foo::Bar
$::($foobar)::baz # $Foo::Bar::baz
$::($foo)::Bar::baz # $Bar::Bar::baz
$::($foobar)baz # ILLEGAL at compile time (no operator baz)
Note that unlike in Perl 5, initial :: doesn't imply global. Package names are searched for from inner lexical scopes to outer, then from inner packages to outer. Variable names are searched for from inner lexical scopes to outer, but unlike package names are looked for in only the current package.
Use the MY pseudopackage to limit the lookup to the current lexical scope, and OUR to limit the scopes to the current package scope.
- When "strict" is in effect (which is the default except for one-liners), non-qualified variables (such as
$x and @y) are only looked up from lexical scopes, but never from package scopes.
To bind package variables into a lexical scope, simply say our ($x, @y). To bind global variables into a lexical scope, predeclare them with use:
use PROCESS <$IN $OUT>;
Or just refer to them as $*IN and $*OUT.
- To do direct lookup in a package's symbol table without scanning, treat the package name as a hash:
Foo::Bar::{'&baz'} # same as &Foo::Bar::baz
PROCESS::<$IN> # Same as $*IN
Foo::<::Bar><::Baz> # same as Foo::Bar::Baz
The :: before the subscript is required here, because the Foo::Bar{...} syntax is reserved for attaching a "WHENCE" initialization closure to an autovivifiable type object. (see S12).
Unlike ::() symbolic references, this does not parse the argument for ::, nor does it initiate a namespace scan from that initial point. In addition, for constant subscripts, it is guaranteed to resolve the symbol at compile time.
The null pseudo-package is reserved to mean the same search list as an ordinary name search. That is, the following are all identical in meaning:
$foo
$::{'foo'}
::{'$foo'}
$::<foo>
::<$foo>
That is, each of them scans lexical scopes outward, and then the current package scope (though the package scope is then disallowed when "strict" is in effect).
As a result of these rules, you can write any arbitrary variable name as either of:
$::{'!@#$#@'}
::{'$!@#$#@'}
You can also use the ::<> form as long as there are no spaces in the name.
- The current lexical symbol table is now accessible through the pseudo-package
MY. The current package symbol table is visible as pseudo-package OUR. The OUTER name refers to the MY symbol table immediately surrounding the current MY, and OUTER::OUTER is the one surrounding that one.
From t/spec/S02-names_and_variables/variables-and-packages.t lines 19–138 (no results): (skip)
| # L<S02/Names/The current lexical symbol table is now accessible>
|
|
|
| # XXX -- dunno why test test fails, but the next outer test works. --iblech
|
| { my $a = 1; {
|
| my $a=2; {
|
| my $a=3;
|
| is($a, 3, 'get regular a');
|
| is($OUTER::a, 2, 'get $OUTER::a');
|
| is($OUTER::OUTER::a, 1, 'get $OUTER::OUTER::a');
|
| }}}
|
|
|
| {
|
| my $a = 1;
|
| is $a, 1, 'get regular $a (1)';
|
|
|
| {
|
| my $a = 2;
|
| is $a, 2, 'get new regular $a (1)';
|
|
|
| {
|
| my $a = 3;
|
|
|
| is $a, 3, 'get very new regular $a';
|
| is $OUTER::a, 2, 'get $OUTER::a';
|
| is $OUTER::OUTER::a, 1, 'get $OUTER::OUTER::a';
|
| }
|
| }
|
| }
|
|
|
| # TODO: more smartlinks
|
|
|
| {
|
| my $a = 3;
|
| my $sub = { $a++ };
|
|
|
| {
|
| my $a = -10;
|
| is $a, -10, 'get regular $a';
|
| is $sub(), 3, 'get hidden $a (1)';
|
| is $sub(), 4, 'get hidden $a (2)';
|
| is $sub(), 5, 'get hidden $a (3)';
|
| }
|
| }
|
|
|
| {
|
| my $sub = -> $stop {
|
| my $x = 3;
|
| if $stop {
|
| $x++;
|
| } else {
|
| $sub(1);
|
| $x;
|
| }
|
| };
|
|
|
| is $sub(0), 3,
|
| "recursively called subref shouldn't stomp on the lexical vars of other instances";
|
| }
|
|
|
| {
|
| sub stomptest ($stop) {
|
| my $x = 3;
|
| if $stop {
|
| $x++;
|
| } else {
|
| stomptest 1;
|
| $x;
|
| }
|
| };
|
|
|
| is stomptest(0), 3,
|
| "recursively called sub shouldn't stomp on the lexical vars of other instances";
|
| }
|
|
|
| {
|
| is foo(), 0, "get variable not yet declared using a sub (1)";
|
| is foo(), 1, "get variable not yet declared using a sub (2)";
|
| is foo(), 2, "get variable not yet declared using a sub (3)";
|
|
|
| my $a;
|
| sub foo { $a++ }
|
| }
|
|
|
| {
|
| is bar(), 0, "runtime part of my not yet executed (1)";
|
| is bar(), 1, "runtime part of my not yet executed (2)";
|
| is bar(), 2, "runtime part of my not yet executed (3)";
|
|
|
| my $a = 3;
|
| sub bar { $a++ }
|
| }
|
|
|
| {
|
| is baz(), 3, "runtime part of my not yet executed (1)";
|
| is baz(), 4, "runtime part of my not yet executed (2)";
|
| is baz(), 5, "runtime part of my not yet executed (3)";
|
|
|
| my $a; BEGIN { $a = 3 };
|
| sub baz { $a++ }
|
| }
|
|
|
| {
|
| {
|
| my $a = 3;
|
| sub grtz { $a++ }
|
| }
|
|
|
| is grtz(), 3, "get real hidden var using a sub (1)";
|
| is grtz(), 4, "get real hidden var using a sub (1)";
|
| is grtz(), 5, "get real hidden var using a sub (1)";
|
| }
|
|
|
| {
|
| my $a;
|
| sub rmbl { $a++ }
|
|
|
| is rmbl(), 0, "var captured by sub is the right var (1)";
|
| $a++;
|
| is rmbl(), 2, "var captured by sub is the right var (2)";
|
| }
|
Highlighted:
small|full
our $foo = 41;
say $::foo; # prints 41, :: is no-op
{
my $foo = 42;
say MY::<$foo>; # prints "42"
say $MY::foo; # same thing
say $::foo; # same thing, :: is no-op here
say OUR::<$foo>; # prints "41"
say $OUR::foo; # same thing
say OUTER::<$foo>; # prints "41" (our $foo is also lexical)
say $OUTER::foo; # same thing
}
You may not use any lexically scoped symbol table, either by name or by reference, to add symbols to a lexical scope that is done compiling. (We reserve the right to relax this if it turns out to be useful though.)
- The
CALLER package refers to the lexical scope of the (dynamically scoped) caller. The caller's lexical scope is allowed to hide any user-defined variable from you. In fact, that's the default, and a lexical variable must have the trait "is context" to be visible via CALLER. ($_, $! and $/ are always contextual.) If the variable is not visible in the caller, it returns failure. Variables whose names are visible at the point of the call but that come from outside that lexical scope are controlled by the scope in which they were originally declared as contextual. Hence the visibility of CALLER::<$*foo> is determined where $*foo is actually declared, not by the caller's scope (unless that's where it happens to be declared). Likewise CALLER::CALLER::<$x> depends only on the declaration of $x visible in your caller's caller.
From t/spec/S02-names/caller.t lines 51–179 (no results): (skip)
| # L<S02/Names/The CALLER package refers to the lexical scope>
|
| {
|
| # $_ is always implicitly declared "is context".
|
| my sub foo () { $CALLER::_ }
|
| my sub bar () {
|
| $_ = 42;
|
| foo();
|
| }
|
|
|
| $_ = 23;
|
| is bar(), 42, '$_ is implicitly declared "is context" (1)';
|
| }
|
|
|
| {
|
| # $_ is always implicitly declared "is context".
|
| # (And, BTW, $_ is lexical.)
|
| my sub foo () { $_ = 17; $CALLER::_ }
|
| my sub bar () {
|
| $_ = 42;
|
| foo();
|
| }
|
|
|
| $_ = 23;
|
| #?pugs todo 'bug'
|
| is bar(), 42, '$_ is implicitly declared "is context" (2)';
|
| }
|
|
|
| {
|
| # ...but other vars are not
|
| my sub foo { my $abc = 17; $CALLER::abc }
|
| my sub bar {
|
| my $abc = 42;
|
| foo();
|
| }
|
|
|
| my $abs = 23;
|
| dies_ok { bar() },
|
| 'vars not declared "is context" are not accessible via $CALLER::';
|
| }
|
|
|
| # Vars declared with "is context" default to being rw in the creating scope and
|
| # readonly when accessed with $CALLER::.
|
| {
|
| my $foo is context = 42;
|
| $foo++;
|
| is $foo, 43, '"is context" vars are rw in the creating scope (1)';
|
| }
|
|
|
| {
|
| my $foo is context = 42;
|
| { $foo++ }
|
| is $foo, 43, '"is context" vars are rw in the creating scope (2)';
|
| }
|
|
|
| {
|
| my sub modify { $CALLER::foo++ }
|
| my $foo is context = 42;
|
| dies_ok { modify() }, '"is context" vars are ro when accessed with $CALLER::';
|
| }
|
|
|
| {
|
| my sub modify { $CALLER::_++ }
|
| $_ = 42;
|
| lives_ok { modify() }, '$_ is implicitly rw (1)';
|
| is $_, 43, '$_ is implicitly rw (2)';
|
| }
|
|
|
| {
|
| my sub modify { $CALLER::foo++ }
|
| my $foo is context is rw = 42;
|
| #?pugs 2 todo 'bug'
|
| lives_ok { modify() },
|
| '"is context" vars declared "is rw" are rw when accessed with $CALLER:: (1)';
|
| is $foo, 43,
|
| '"is context" vars declared "is rw" are rw when accessed with $CALLER:: (2)';
|
| }
|
|
|
| {
|
| my sub get_foo { try { $*foo } }
|
| my $foo is context = 42;
|
|
|
| is get_foo(), 42, '$* is short for $CONTEXT::';
|
| }
|
|
|
| # Rebinding caller's variables -- legal?
|
| {
|
| my $other_var = 23;
|
| my sub rebind_foo { $CALLER::foo := $other_var }
|
| my $foo is context = 42;
|
|
|
| #?pugs 2 todo 'bug'
|
| lives_ok { rebind_foo() }, 'rebinding $CALLER:: variables works (1)';
|
| is $foo, 23, 'rebinding $CALLER:: variables works (2)';
|
| $other_var++;
|
| #?pugs todo 'bug'
|
| is $foo, 24, 'rebinding $CALLER:: variables works (3)';
|
| }
|
|
|
| =begin pod
|
|
|
| Larry ruled that as erroneous.
|
|
|
| 15:13 < iblech> autrijus: :) BTW, WRT lex hoisting: sub foo { $CALLER::a }; { foo(); my $a
|
| = 3; foo() }
|
| 15:13 < autrijus> iblech: larry ruled it as erroneous.
|
| 15:13 < autrijus> i.e. foo()'s behaviour is undefined.
|
| 15:14 < iblech> ok then :)
|
| 15:14 < autrijus> it's essential we do that because
|
| 15:14 < autrijus> foo($a, my $a)
|
| 15:14 < autrijus> is legal
|
| 15:14 < autrijus> and will be simply hazadrous to implement either way.
|
| 15:14 < autrijus> s/implement/mandate/
|
|
|
| {
|
| if $*OS eq "browser" { # test works under PIL2JS :)
|
| my $sub = sub { $CALLER::a };
|
|
|
| # No declaration of $a yet.
|
| dies_ok { $sub() }, '$CALLER:: dies when accessing not yet declared vars';
|
|
|
| my $a = 3;
|
| is $sub(), 3, '$CALLER:: works now (accessing a declared var)';
|
| } else {
|
| flunk "Test loops infinitely";
|
| flunk "Test loops infinitely";
|
| }
|
| }
|
|
|
| =end pod
|
Highlighted:
small|full
Any lexical declared with the is context trait is by default considered readonly outside the current lexical scope. You may add a trait argument of <rw> to allow called routines to modify your value. $_, $!, and $/ are context<rw> by default. In any event, the declaring scope can always access the variable as if it were an ordinary variable; the restriction on writing applies only to access via the * twigil.
From t/spec/S32-list/map.t lines 160–271 (no results): (skip)
| # L<S02/Names/"$_, $!, and $/ are context<rw> by default">
|
|
|
| {
|
| my @array = <a b c d>;
|
| is ~(try { @array.map: { $_ ~= "c"; $_ ~ "d" } }), "acd bcd ccd dcd",
|
| 'mutating $_ in map works (1)';
|
| is ~@array, "ac bc cc dc",
|
| 'mutating $_ in map works (2)';
|
| }
|
|
|
| sub dbl ( Int $val ) { 2*$val };
|
| is( ~((1..3).map: { 2*$_ }),'2 4 6','intern method in map');
|
| is( ~((1..3).map: { dbl( $_ ) }),'2 4 6','extern method in map');
|
|
|
|
|
| # map with empty lists in the block
|
| # Test was primarily aimed at PIL2JS, which did not pass this test (fixed now).
|
| {
|
| my @array = <a b c d>;
|
| my @result = map { (), }, @array;
|
|
|
| is +@result, 0, "map works with the map body returning an empty list";
|
| }
|
|
|
| {
|
| my @array = <a b c d>;
|
| my @empty = ();
|
| my @result = map { @empty }, @array;
|
|
|
| is +@result, 0, "map works with the map body returning an empty array";
|
| }
|
|
|
| {
|
| my @array = <a b c d>;
|
| my @result = map { [] }, @array;
|
|
|
| is +@result, 4, "map works with the map body returning an empty arrayref";
|
| }
|
|
|
| #?pugs todo 'bug'
|
| {
|
| my @array = <a b c d>;
|
| my $empty = [];
|
| my @result = map { $empty }, @array;
|
|
|
| is +@result, 4, "map works with the map body returning an empty arrayref variable";
|
| }
|
|
|
| {
|
| my @array = <a b c d>;
|
| my @result = map { undef }, @array;
|
|
|
| is +@result, 4, "map works with the map body returning undef";
|
| }
|
|
|
| {
|
| my @array = <a b c d>;
|
| my $undef = undef;
|
| my @result = map { $undef }, @array;
|
|
|
| is +@result, 4, "map works with the map body returning an undefined variable";
|
| }
|
|
|
| {
|
| my @array = <a b c d>;
|
| my @result = map { () }, @array;
|
|
|
| is +@result, 0, "map works with the map body returning ()";
|
| }
|
|
|
| # test map with a block that takes more than one parameter
|
| {
|
| my @a=(1,4,2,5,3,6);
|
| my @ret=map -> $a,$b {$a+$b}, @a;
|
|
|
| is(@ret.elems,3,'map took 2 elements at a time');
|
| is(@ret[0],5,'first element ok');
|
| is(@ret[1],7,'second element ok');
|
| is(@ret[2],9,'third element ok');
|
|
|
| }
|
|
|
| # map shouldn't flatten array objects
|
| # used to be a pugs regression
|
| {
|
| my @foo = [1, 2, 3].map: { [100+$_, 200+$_] };
|
| is +@foo, 3, "map should't flatten our arrayref (1)";
|
| is +@foo[0], 2, "map should't flatten our arrayref (2)";
|
| is ~@foo[0], "101 201", "map should't flatten our arrayref (3)";
|
| }
|
|
|
| # .thing inside map blocks should still default to $_
|
| # used to be a pugs regression
|
| {
|
| is ~((1,2,3).map: { int($_) }), "1 2 3", "dependency for following test (1)";
|
| $_ = 4; is .int, 4, "dependency for following test (2)";
|
| is ~((1,2,3).map: { .int }), "1 2 3", 'int() should default to $_ inside map, too';
|
|
|
| is ~(({1},{2},{3}).map: { $_; $_() }), "1 2 3", 'lone $_ in map should work (1)';
|
| is ~(({1},{2},{3}).map: { $_() }), "1 2 3", 'lone $_ in map should work (2)';
|
| is ~(({1},{2},{3}).map: { .() }), "1 2 3", 'lone .() in map should work (2)';
|
| }
|
|
|
| {
|
| #?rakudo todo 'next in map'
|
| is (1..4).map({ next if $_ % 2; 2 * $_ }).join('|'),
|
| '2|4|8', 'next in map works';
|
| is (1..10).map({ last if $_ % 5 == 0; 2 * $_}).join(' '),
|
| '2 4 6 8', 'last in map works';
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
From t/spec/S32-list/grep.t lines 79–109 (no results): (skip)
| # L<S02/Names/"$_, $!, and $/ are context<rw> by default">
|
|
|
| {
|
| my @array = <a b c d>;
|
| is ~(try { @array.grep: { $_ ~= "c"; 1 } }), "ac bc cc dc",
|
| 'mutating $_ in grep works (1)';
|
| is ~@array, "ac bc cc dc",
|
| 'mutating $_ in grep works (2)';
|
| }
|
|
|
| # grep with last, next etc.
|
|
|
| {
|
| is (1..16).grep({last if $_ % 5 == 0; $_ % 2 == 0}).join('|'),
|
| '2|4', 'last works in grep';
|
| is (1..12).grep({next if $_ % 5 == 0; $_ % 2 == 0}).join('|'),
|
| '2|4|6|8|12', 'next works in grep';
|
| }
|
|
|
| # since the test argument to .grep is a Matcher, we can also
|
| # check type constraints:
|
|
|
| {
|
| is (2, [], 4, [], 5).grep(Int).join(','),
|
| '2,4,5', ".grep with non-Code matcher";
|
|
|
| is grep(Int, 2, [], 4, [], 5).join(','),
|
| '2,4,5', "grep() with non-Code matcher";
|
| }
|
|
|
| # vim: ft=perl6
|
Highlighted:
small|full
- The
CONTEXT pseudo-package is just like CALLER except that it starts in the current dynamic scope and from there scans outward through all dynamic scopes until it finds a contextual variable of that name in that context's lexical scope. (Use of $*FOO is equivalent to CONTEXT::<$FOO> or $CONTEXT::FOO.) If after scanning all the lexical scopes of each dynamic scope, there is no variable of that name, it looks in the GLOBAL package followed by the PROCESS package. If there is no such package variable, it then looks in CONTEXT::<%ENV> for the identifier of the variable, which, if not overridden in a dynamic scope, finds PROCESS::<%ENV>, that is, in the environment variables passed to program. If the value is not found there, it returns failure. If the variable is of the form $*FOO, the complete environment value is returned. If it is of the form @*FOO the string will be split either on colons or semicolons as appropriate to the current operating system. Usage of the %*FOO form is currently undefined.
From t/spec/S02-magicals/env.t lines 6–141 (no results): (skip)
| # L<S02/Names/environment variables passed to program>
|
| plan 14;
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
| =begin desc
|
|
|
| = DESCRIPTION
|
|
|
| Tests for %*ENV
|
|
|
| Tests that C<%*ENV> can be read and written to and that
|
| child processes see the modified C<%*ENV>.
|
|
|
| =end desc
|
|
|
| # It must not be empty at startup.
|
| ok +%*ENV.keys, '%*ENV has keys';
|
|
|
| # %*ENV should be able to get copied into another variable.
|
| my %vars = %*ENV;
|
| is +%vars.keys, +%*ENV.keys, '%*ENV was successfully copied into another variable';
|
|
|
| # XXX: Should modifying %vars affect the environment? I don't think so, but, of
|
| # course, feel free to change the following test if I'm wrong.
|
| %vars<PATH> = "42";
|
| ok %*ENV<PATH> ne "42",
|
| 'modifying a copy of %*ENV didn\'t affect the environment';
|
|
|
| # Similarily, I don't think creating a new entry in %vars should affect the
|
| # environment:
|
| #?rakudo 3 todo 'clarify the test'
|
| diag '%*ENV<PUGS_ROCKS>=' ~ %*ENV<PUGS_ROCKS>;
|
| ok !defined(%*ENV<PUGS_ROCKS>), "there's no env variable 'PUGS_ROCKS'";
|
| %vars<PUGS_ROCKS> = "42";
|
| diag '%*ENV<PUGS_ROCKS>=' ~ %*ENV<PUGS_ROCKS>;
|
| ok !defined(%*ENV<PUGS_ROCKS>), "there's still no env variable 'PUGS_ROCKS'";
|
|
|
| my ($redir,$squo) = (">", "'");
|
|
|
| my $expected = 'Hello from subprocess';
|
| %*ENV<PUGS_ROCKS> = $expected;
|
| # Note that the "?" preceeding the "(" is necessary, because we need a Bool,
|
| # not a junction of Bools.
|
| is %*ENV<PUGS_ROCKS>, $expected,'%*ENV is rw';
|
|
|
| my $tempfile = "temp-ex-output." ~ $*PID ~ "." ~ 1000.rand;
|
|
|
| my $command = qq!$*EXECUTABLE_NAME -e "\%*ENV.perl.say" $redir $tempfile!;
|
| diag $command;
|
| run $command;
|
|
|
| my $child_env = slurp $tempfile;
|
| my %child_env = eval $child_env;
|
| unlink $tempfile;
|
|
|
| my $err = 0;
|
| for %*ENV.kv -> $k,$v {
|
| # Ignore env vars which bash and maybe other shells set automatically.
|
| next if $k eq any <SHLVL _ OLDPWD PS1>;
|
| if (%child_env{$k} !~~ $v) {
|
| if (! $err) {
|
| flunk("Environment gets propagated to child.");
|
| $err++;
|
| };
|
| diag "Expected: $k=$v";
|
| diag "Got: $k=%child_env{$k}";
|
| } else {
|
| # diag "$k=$v";
|
| };
|
| };
|
| if (! $err) {
|
| ok(1,"Environment gets propagated to child.");
|
| };
|
|
|
| %*ENV.delete('PUGS_ROCKS');
|
| ok(!%*ENV.exists('PUGS_ROCKS'), 'We can remove keys from %*ENV');
|
|
|
| $command = qq!$*EXECUTABLE_NAME -e "\%*ENV.perl.say" $redir $tempfile!;
|
| diag $command;
|
| run $command;
|
|
|
| $child_env = slurp $tempfile;
|
| %child_env = eval $child_env;
|
| unlink $tempfile;
|
|
|
| ok(!%child_env.exists('PUGS_ROCKS'), 'The child did not see %*ENV<PUGS_ROCKS>');
|
|
|
| $err = 0;
|
| for %*ENV.kv -> $k,$v {
|
| # Ignore env vars which bash and maybe other shells set automatically.
|
| next if $k eq any <SHLVL _ OLDPWD PS1>;
|
| if (%child_env{$k} !~~ $v) {
|
| if (! $err) {
|
| flunk("Environment gets propagated to child.");
|
| $err++;
|
| };
|
| diag "Expected: $k=$v";
|
| diag "Got: $k=%child_env{$k}";
|
| } else {
|
|