The REPL (Where you see `>`) adds a `.say` if you don't print anything
yourself.
(Which makes sense since it is a Read Eval Print Loop.)

Normal code doesn't print anything unless you tell it to.

---

A bare `lines` can work, as long as it is obvious where the end of its
arguments are.

    for lines $*IN { .say }

There is no comma or any other infix operator after `$*IN` so the `{ .say
}` is not part of the arguments to `lines`.

---

The following does NOT parse the way you think it does.

    for lines ("a\nb\n") { .say }

It is parsed the same as:

    for lines(  ("a\nb\n"),  )    { .say }

Parenthesis only denote an argument list if it is immediately after the
function.
If there is space before the `()` it is seen as just a grouping construct.

    for lines () { .say }

The `()` creates an empty list, that list is then used as the only argument
to `lines()`

    for lines(  (),  )    { .say }


    List.new.lines.map( { .say } ); # <- basically the same as this

---

    for lines { say 'this is the block argument to lines' } but role {
method lines () { 'line 1', 'line 2', 'line 3' }} {.say}

That prints:

    line 1
    line 2
    line 3

That is because the argument to `lines()` is a block with a `.lines`
method, which return those values.

`lines()` doesn't actually care what its argument is, as long as it has a
method named `.ines`.

Again it is obvious where the end of the argument list of `lines()` is,
because there isn't a comma after `role {}` to continue it.

The `role {}` is part of the argument list because of the `but` infix
operator.

Note that the code in the block argument to `lines()` does not actually get
run, because `lines()` doesn't call it. `lines()` calls the `.lines` method.

---

A block can be an argument or part of a keyword like `for`

    foo  {;}
    foo( {;} )  # exactly the same

    for '…' {;}
    if '…' {;}
    when '…' {;}
    whenever '…' {;}

So when you do

     for lines {;}

The `lines()` argument list captures the block.

    for lines( {;} )

Which leaves nothing for the `for`.

Since a block CAN be an argument to a function, the block above MUST be an
argument to `lines()`.

If you don't want the block to be an argument, you have to make sure the
argument list to `lines` ends before the block.
It doesn't matter how it ends, only that it does.

You could also end the argument list to `lines()` by using a postfix
operator.

    sub postfix:< ^_^ > ( +C ) { C } # intentionally has a dumb “name”

    for lines^_^ { .say }

Since a postfix operator must come after something, it forces the argument
list to end first so it can attach to `lines()`.

Again, it doesn't matter how the argument list ends, only that it does.

---

    > {;}.lines;
    No such method 'lines' for invocant of type 'Block'. Did you mean
'line'?

The above is where that error comes from, because that is all `lines()`
does, is call a method of that name.

---

The parser actually has very simple rules about how it parses your code.

The biggest problem you have is that you are trying to deduce it yourself
and you keep coming up with wrong assumptions.
(Probably assumptions from other languages.)

The worst part is we keep trying to tell you this, and trying to correct
those wrong assumptions, but you keep making the same assumptions.

I don't know of a nicer way to tell you this.

---

I really want you to know the syntax rules, but I don't want to keep trying
to say the same thing over and over again.

I mean maybe I'm just not that good at explaining it.
I want to be better at that, so if I confuse you, please tell me. So that I
can be better in the future.


On Mon, Aug 5, 2019 at 5:22 PM William Michels via perl6-users <
perl6-us...@perl.org> wrote:

> Thank you Richard, for taking time to explain this. I've put comments
> below (inline):
>
> On Mon, Aug 5, 2019 at 10:26 AM Richard Hainsworth
> <rnhainswo...@gmail.com> wrote:
> >
> > William,
> >
> > I saw others were replying and between what Brad had said and what I had
> > said, I thought the explanations were pretty clear.
> >
> > So I'll try again.
> >
> > Comments in text below.
> >
> > On 03/08/2019 07:11, William Michels wrote:
> > > Hi Richard, I'm not able to come to the same conclusions.
> > > Specifically, the previous code examples starting with 'for lines()'
> > > always have to have parentheses following 'lines' (as in 'lines()
> > > {...}'), otherwise Perl_6 balks (examples 6 and 7 previously posted).
> > > This is whether 'information is being passed' or not (i.e. empty
> > > parentheses are required):
> > >
> > > #example 6:
> > > mbook:~ homedir$ perl6 -e ' for lines() { say .split(":")[0, 2, 1,
> > > 5].join("\t") };' six_fruits1.txt
> > > apple carrot banana favabean
> > > apricot cabbage basil fennel
> > > acai celery beets figs
> > >
> > > #example 6 no-parens:
> > > mbook:~ homedir$ perl6 -e ' for lines { say .split(":")[0, 2, 1,
> > > 5].join("\t") };' six_fruits1.txt
> > > ===SORRY!===
> > > Function 'lines' needs parens to avoid gobbling block
> > > at -e:1
> > > ------> say .split(":")[0, 2, 1, 5].join("\t") };
> > > Missing block (apparently claimed by 'lines')
> > > at -e:1
> > > ------> say .split(":")[0, 2, 1, 5].join("\t") };
> >
> > It seems to me that the error message is quite clear. Perl 6 knows
> > 'lines' can take something following it, and it is expecting to get a
> > list which can be interpreted as lines to act upon. But then 'for' has
> > nothing to work on. Perl6 has figured out that you actually want the
> > block to go with the 'for' and not 'lines', but since there is an
> > ambiguity, Perl 6 errors out, and tells you what the probable mistake is.
> >
> > It has nothing to do with whether 'lines' needs () or not, but to do
> > with writing unambiguous code. Rather than writing 'lines()', which
> > personally I find distasteful, I would write:
> >
> > perl6 -e '.split(":")[0,2,1,5].join("\t").say for lines' six_fruits1.txt
> >
> > 'lines' has no () , in your words 'is bare'. But this code is
> > unambiguous because it is clear that `.split` etc will take the topic
> > (aka $_) from the 'for' and 'lines' takes its data from @_, which is
> > provided from the file.
> >
> > >
> > > #example 7:
> > > mbook:~ homedir$ perl6 -e 'for lines() {.split(":")[0, 2, 1,
> > > 5].join("\t").say};' six_fruits1.txt
> > > apple carrot banana favabean
> > > apricot cabbage basil fennel
> > > acai celery beets figs
> > >
> > > #example 7 no-parens:
> > > mbook:~ homedir$ perl6 -e 'for lines {.split(":")[0, 2, 1,
> > > 5].join("\t").say};' six_fruits1.txt
> > > ===SORRY!===
> > > Function 'lines' needs parens to avoid gobbling block
> > > at -e:1
> > > ------> {.split(":")[0, 2, 1, 5].join("\t").say};
> > > Missing block (apparently claimed by 'lines')
> > > at -e:1
> > > ------> {.split(":")[0, 2, 1, 5].join("\t").say};
> > Same explanation as before. There is no difference between `say .split`
> > etc in your example 6, and `.split ... .say` in example 7. So the
> > problem you are running into has nothing to do with chaining .say or
> > using say as a sub.
> > >
> > > mbook:~ homedir$ perl6 --version
> > > This is Rakudo version 2019.07.1 built on MoarVM version 2019.07.1
> > > implementing Perl 6.d.
> > > mbook:~ homedir$
> > >
> > > Other Perl_6 users have replied with a general discussion of
> > > differences between using lines() as a method, or in a subroutine. I
> > > still have to review some of those comments. However, as I'm using the
> > > latest Perl_6 version (2019.07.1), I'm fairly confident in the results
> > > above (and below).
> > >
> > > Also, the examples below show that 'for lines[0..2] {...}' works,
> > > despite the previous error message insisting on parentheses ("Function
> > > 'lines' needs parens to avoid gobbling block at -e:1"). Taken
> > > together, the results above and below seem to indicate to me that the
> > > use of a bare 'lines' sub is disallowed, at least in a 'for' loop.
> >
> > Nope. What is disallowed is a bare 'lines' sub in an ambiguous context.
> > The idiom 'for lines { .say }' is ambiguous because
> >
> > the '{ .say }' has to be given to 'lines', but you want it to be given
> > to 'for'. And the reason for that choice of syntax was explained by Brad.
> >
> > This works:
> >
> > perl6 -e 'for ( lines ) { .split(":")[0, 2, 1, 5].join("\t") }'
> > six_fruits1.txt
> >
> > 'lines' is bare, but the brackets disambiguate the block from 'lines' .
> > Personally, I think this is also somewhat ugly.
> >
> > The following 'bare lines with a for' also 'works' in that no errors are
> > generated (Perl 6 does not disallow bare 'lines' with a for), but there
> > is no output so it doesn't give the desired output.
>
> Not working for me: the code doesn't return an error but it doesn't
> return an answer, either:
>
> mbook:~ homedir$ perl6 -e 'for (lines) { .split(":")[0, 2, 1,
> 5].join("\t") }' six_fruits1.txt
> mbook:~ homedir$
>
> Okay...I see what happened. The ".say" call needs to be added at the end:
>
> mbook:~ homedir$ perl6 -e 'for (lines) { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$
>
> >
> > perl6 -e 'for lines {} { .split(":")[0, 2, 1, 5].join("\t") }'
> > six_fruits1.txt
> >
> > Obviously, 'lines' has been given something to operate on, and in this
> > case it is interpreted not as a block, but an empty hash. And since
> > 'lines' had been given an explicit argument, Perl 6 did not know to
> > provide the data from six_fruits1.txt to anything, so the data was
> ignored.
>
> Okay, same result as earlier: I need to add ".say" at the end to get
> any output. I see that a separate "{}" returns nothing, but if I put
> the "{}" adjacent to "lines" as in "lines{}" all of a sudden it works
> and gives the correct answer. Not only does "lines{}" work, but
> "lines()" and "lines[]" works. What doesn't seem to work is a bare
> "lines" as noted previously:
>
> mbook:~ homedir$ perl6 -e 'for lines {} { .split(":")[0, 2, 1,
> 5].join("\t") }' six_fruits1.txt
> mbook:~ homedir$ perl6 -e 'for lines {} { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> mbook:~ homedir$ perl6 -e 'for lines{} { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$ perl6 -e 'for lines() { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$ perl6 -e 'for lines[] { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$ perl6 -e 'for lines { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> ===SORRY!===
> Function 'lines' needs parens to avoid gobbling block
> at -e:1
> ------> .split(":")[0, 2, 1, 5].join("\t").say }<EOL>
> Missing block (apparently claimed by 'lines')
> at -e:1
> ------> .split(":")[0, 2, 1, 5].join("\t").say }<EOL>
>
>
> >
> > Almost the same thing, note the space between 'lines' and '()':
> >
> > perl6 -e 'for lines () { .split(":")[0, 2, 1, 5].join("\t") }'
> > six_fruits1.txt
> >
> > Here, 'lines' is given an empty list, viz. ' ()', the preceding space
> > telling Perl 6 that '()' is a list, not a signature
> >
> > However, "bare lines" is not "disallowed" for a 'for'.
> >
> > Hope this helps.
>
> (I said bare 'lines', not 'bare lines'). But I think I see what you're
> saying. First I add the ".say" method below (otherwise i get no
> result), then i eliminate everything but { .say} inside the curly
> braces. This actually lets me feed an object (for lack of a better
> word) into the block:
>
> mbook:~ homedir$ perl6 -e 'for lines () { .split(":")[0, 2, 1,
> 5].join("\t") }' six_fruits1.txt
> mbook:~ homedir$ perl6 -e 'for lines() { .split(":")[0, 2, 1,
> 5].join("\t") }' six_fruits1.txt
> mbook:~ homedir$ perl6 -e 'for lines () { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> mbook:~ homedir$ perl6 -e 'for lines() { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$ perl6 -e 'for (lines) { .split(":")[0, 2, 1,
> 5].join("\t").say }' six_fruits1.txt
> apple carrot banana favabean
> apricot cabbage basil fennel
> acai celery beets figs
> mbook:~ homedir$ perl6 -e 'for lines () { .split(":")[0, 2, 1,
> 5].join("\t").say }'
> mbook:~ homedir$ perl6 -e 'for lines ("a\nb\n") { .split(":")[0, 2, 1,
> 5].join("\t").say }'
> Use of Nil in string context
>   in block  at -e line 1
> Use of Nil in string context
>   in block  at -e line 1
> Use of Nil in string context
>   in block  at -e line 1
> a
> Use of Nil in string context
>   in block  at -e line 1
> Use of Nil in string context
>   in block  at -e line 1
> Use of Nil in string context
>   in block  at -e line 1
> b
> mbook:~ homedir$ perl6 -e 'for lines ("a\nb\n") { .say }'
> a
> b
> mbook:~ homedir$
>
> Okay. How to express this in simple English? Writing a one-liner "for"
> loop for use on a shell command line, you can feed lines into a block
> using 'lines()' and adding the target file at the end. Or you can
> write "for lines ("line_1\nline_2\n") and then the functions you want
> to perform inside the block.
>
> Correct?  Best Regards, Bill.
>
>
>
> >
> >
> > > --Best Regards, Bill.
> > >
> > > #example 10:
> > > mbook:~ homedir$ perl6 -e 'for lines { say .split(":")[0, 2, 1,
> > > 5].join("\t") };' six_fruits1.txt
> > > ===SORRY!===
> > > Function 'lines' needs parens to avoid gobbling block
> > > at -e:1
> > > ------> say .split(":")[0, 2, 1, 5].join("\t") };
> > > Missing block (apparently claimed by 'lines')
> > > at -e:1
> > > ------> say .split(":")[0, 2, 1, 5].join("\t") };
> > >
> > > #example 11:
> > > mbook:~ homedir$ perl6 -e 'for lines() { say .split(":")[0, 2, 1,
> > > 5].join("\t") };' six_fruits1.txt
> > > apple carrot banana favabean
> > > apricot cabbage basil fennel
> > > acai celery beets figs
> > >
> > > #example 12:
> > > mbook:~ homedir$ perl6 -e 'for lines[0..2] { say .split(":")[0, 2, 1,
> > > 5].join("\t") };' six_fruits1.txt
> > > apple carrot banana favabean
> > > apricot cabbage basil fennel
> > > acai celery beets figs
> > >
> > >
> > > On Fri, Aug 2, 2019 at 1:20 AM Richard Hainsworth
> > >
> > > <rnhainswo...@gmail.com> wrote:
> > >> Not quite sure what sort of "rule" you want.
> > >>
> > >> You used () and [] which do different things and the results were
> exactly what I would expect. They are covered in the documentation.
> Basically, () without a space, eg xxxx() not xxxx (), is used to pass
> information to the sub or method xxxx.
> > >> If no information is being passed, then no need to use (). The way
> xxxx responds to no data provided depends on the way xxxx is written. The
> programmer could provide default values for the arguments it expects. Perl6
> also makes the value of the default variable, eg $_, to xxxx.
> > >> If you put a space between xxxx (), then () is interpreted as an
> empty list and provide to xxxx as a single piece of information.
> > >> [] Are used to dereference a list (sequence or array, they are all
> slightly different).
> > >>
> > >> I'll comment more below. Sometimes I think through the process using
> slightly different and less exact words. My comments are illustrative.
> > >>
> > >> On Fri, 2 Aug 2019, 04:50 William Michels, <w...@caa.columbia.edu>
> wrote:
> > >>> Hi Richard, I'm trying to figure out when the parentheses in
> 'lines()'
> > >>> can be dropped, and 'lines' used instead. Any pointers? I have about
> > >>> nine or so working examples below, but formulating a clear
> > >>> rule-of-thumb is proving elusive. Any help appreciated, --Best, Bill.
> > >>>
> > >>> # test file: six_fruits1.txt
> > >>> mbook:~ homedir$ cat six_fruits1.txt
> > >>> apple:banana:carrot:dragonfruit:eggplant:favabean
> > >>> apricot:basil:cabbage:dill:escarole:fennel
> > >>> acai:beets:celery:daikon:endive:figs
> > >>>
> > >>> mbook:~ homedir$ perl6 -e '.say for lines()' six_fruits1.txt
> > >>> apple:banana:carrot:dragonfruit:eggplant:favabean
> > >>> apricot:basil:cabbage:dill:escarole:fennel
> > >>> acai:beets:celery:daikon:endive:figs
> > >> Here 'lines ()' is the same as 'lines'.
> > >> The program in the string after -e is provided with the data inside
> the file.
> > >>>
> > >>> mbook:~ homedir$ perl6 -e '.say for lines' six_fruits1.txt
> > >>> apple:banana:carrot:dragonfruit:eggplant:favabean
> > >>> apricot:basil:cabbage:dill:escarole:fennel
> > >>> acai:beets:celery:daikon:endive:figs
> > >> The top two are equivalent
> > >>>
> > >>> mbook:~ homedir$ perl6 -e '.say for lines("a\nb\n")' six_fruits1.txt
> > >>> a
> > >>> b
> > >> Here you provided data to 'lines' which it was able to interpret as a
> set of lines. So it printed them. The data in six_fruits is ignored because
> you provided the data explicitly.
> > >>>
> > >>> mbook:~ homedir$ perl6 -e '.say for lines[0]' six_fruits1.txt
> > >>> apple:banana:carrot:dragonfruit:eggplant:favabean
> > >> Six_fruits has 3 lines. Lines has processed the data. But you wrote
> '[0]' which extracted the first line, and that was processed by the 'for'
> loop. In other words the 'for' was only given one piece of information to
> process.
> > >>>
> > >>> mbook:~ homedir$ perl6 -e '.say for lines[0..1]' six_fruits1.txt
> > >>> apple:banana:carrot:dragonfruit:eggplant:favabean
> > >>> apricot:basil:cabbage:dill:escarole:fennel
> > >> The '[0..1]' extracts the first three pieces of data from 'lines' and
> they are processed by the 'for'
> > >>>
> > >>> mbook:~ homedir$ perl6 -e ' for lines() { say .split(":")[0, 2, 1,
> > >>> 5].join("\t") };' six_fruits1.txt
> > >>> apple carrot banana favabean
> > >>> apricot cabbage basil fennel
> > >>> acai celery beets figs
> > >> Could be just 'lines', which provides a list of the lines in the file
> to the 'for'
> > >> Inside the 'for' the data is in the topic or default variable. You
> could access it as $_ but '.split' accesses it automatically. Split
> generates another list. '[0,2,1,5]' extracts the relevant elements of the
> list and generates another list which is passed to the'join'. The output
> from join is passed to 'say' which is written in sub form. Sub form means
> you write the name of the sub first, then you write where the data is
> coming from.
> > >>
> > >>> mbook:~ homedir$ perl6 -e ' for lines() {.split(":")[0, 2, 1,
> > >>> 5].join("\t").say};' six_fruits1.txt
> > >>> apple carrot banana favabean
> > >>> apricot cabbage basil fennel
> > >>> acai celery beets figs
> > >> Exactly the same for perl6 as before except that 'say' is written in
> sub form above and in method form here. Method form means you can append
> the 'say' to a chain of processing units.
> > >>>
> > >>> mbook:~ homedir$ perl6 -e 'for "six_fruits1.txt".IO.lines()
> > >>> {.split(/\:/)[0, 2, 1, 5].join("\t").say};'
> > >>> apple carrot banana favabean
> > >>> apricot cabbage basil fennel
> > >>> acai celery beets figs
> > >> Here just accessing the data in the file explicitly in perl6.
> > >>>
> > >>> mbook:~ homedir$ perl6 -e 'for "six_fruits1.txt".IO.lines
> > >>> {.split(/\:/)[0, 2, 1, 5].join("\t").say};'
> > >>> apple carrot banana favabean
> > >>> apricot cabbage basil fennel
> > >>> acai celery beets figs
> > >> Same as above. No () on lines
> > >>>
> > >>>
> > >>>
> > >>>
> > >>> On Mon, Jul 29, 2019 at 1:07 AM Richard Hainsworth
> > >>> <rnhainswo...@gmail.com> wrote:
> > >>>> Also no need for all the brackets
> > >>>>
> > >>>> .say for lines;
> > >>>>
> > >>>> This is quite idiomatic Perl 6 and not golfing
> > >>>>
> > >>>> On Mon, 29 Jul 2019, 07:13 Joseph Brenner, <doom...@gmail.com>
> wrote:
> > >>>>>> Hmmm. I would expect that to be in the Perl 5 to Perl 6 Migration
> Guides, but I do not see it there.
> > >>>>> Exactly, I was just looking there, and I ended up playing around
> with
> > >>>>> the method form of lines, and didn't think to try the function
> > >>>>> form of it.
> > >>>>>
> > >>>>> To summarize, if the goal is to write a "simple_echo" script that
> > >>>>> can work with a file name or with lines on standard input:
> > >>>>>
> > >>>>>     simple_echo lines.txt
> > >>>>>     cat lines.txt | simple_echo
> > >>>>>
> > >>>>> The perl5 version would probably be:
> > >>>>>
> > >>>>>    #!/usr/bin/env perl
> > >>>>>    while(<>){
> > >>>>>       print;
> > >>>>>    }
> > >>>>>
> > >>>>> The perl6 version would be something like:
> > >>>>>
> > >>>>>    #!/usr/bin/env perl6
> > >>>>>    use v6;
> > >>>>>    for lines() {
> > >>>>>        say $_;
> > >>>>>    }
> > >>>>>
> > >>>>>
> > >>>>> The kind of thing I was playing with was:
> > >>>>>
> > >>>>>    #!/usr/bin/env perl6
> > >>>>>    use v6;
> > >>>>>    my @lines = $*ARGFILES.IO.lines;
> > >>>>>    say @lines;
> > >>>>>
> > >>>>> That works for lines from a file, but not from standard input,
> and  the
> > >>>>> error message isn't tremendously helpful:
> > >>>>>
> > >>>>>    No such method 'lines' for invocant of type 'IO::Special'
> > >>>>>
> > >>>>>
> > >>>>>
> > >>>>>
> > >>>>> On 7/28/19, Bruce Gray <robertbrucegr...@gmail.com> wrote:
> > >>>>>>
> > >>>>>>> On Jul 28, 2019, at 6:20 PM, Joseph Brenner <doom...@gmail.com>
> wrote:
> > >>>>>>>
> > >>>>>>> I was just wondering if there's some direct analog in perl6 to
> the
> > >>>>>>> perl5 construct:
> > >>>>>>>
> > >>>>>>>   while(<>){ ... }
> > >>>>>>>
> > >>>>>>> If I'm planning on passing a filename on the command-line, I can
> just
> > >>>>>>> get it out of $*ARGFILES easily enough, but what if I also
> wanted it
> > >>>>>>> to work on lines passed in via standard input?
> > >>>>>>
> > >>>>>> `lines` , as a sub instead of a method, and no arguments.
> > >>>>>>
> > >>>>>> See: https://docs.perl6.org/routine/lines#(Cool)_routine_lines
> > >>>>>>        Without any arguments, sub lines operates on $*ARGFILES,
> which defaults to
> > >>>>>> $*IN in the absence of any filenames.
> > >>>>>>
> > >>>>>> For example:
> > >>>>>>        perl6 -e 'say .join("\t") for lines().rotor(4);'
> path/to/file.txt
> > >>>>>>
> > >>>>>> Hmmm. I would expect that to be in the Perl 5 to Perl 6 Migration
> Guides,
> > >>>>>> but I do not see it there.
> > >>>>>>
> > >>>>>> —
> > >>>>>> Hope this helps,
> > >>>>>> Bruce Gray (Util of PerlMonks)
> > >>>>>>
> > >>>>>>
>

Reply via email to