10

Functions can be called in a couple ways:

say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)

The latter seems to pass a Positional, but apart from that I don't know how else they differ. Are there any differences that are important to know? What types of situations would you use one over the other?

Pat
  • 36,282
  • 18
  • 72
  • 87
Kaiepi
  • 3,230
  • 7
  • 27
  • 3
    That `say:` is a label, not a function call. You should get the same result if you write `I'm-a-label: 1, 2, 3`. I'm guessing you're using the REPL which displays the value of an entered expression if you don't use `say`. – raiph May 07 '18 at 21:51
  • Oh, I didn't know that. Why does it sitll work, though? – Kaiepi May 07 '18 at 22:05
  • 1
    It doesn't work. if you put the two version in each their files and run them `say(1, 2, 3)` prints out "123" while the other one does nothing. Perl complaints about `Useless use of constant integer` x 3. Of course in a REPL each expression result gets printed so it isn't useless anymore. – Sylwester May 07 '18 at 22:24
  • 1
    That said, there are indeed many ways to write a function call. Each is more convenient or aesthetically appealing than the others in particular scenarios. There's `say(...)` (i.e. with a left parenthesis directly following the function name with no intervening space) or `say ...` (i.e. with at least one space following the function name) or `.(...) for &say, &note` and so on. And a couple forms for method calls too. – raiph May 07 '18 at 22:44
  • If you write `say: 1, 2, 3;Nil` into the REPL it will say `Nil` – Brad Gilbert May 08 '18 at 11:53
  • See also https://stackoverflow.com/questions/30982697/use-of-colon-in-method-and-function-calls-in-perl-6 – Christopher Bottoms May 08 '18 at 21:31

2 Answers2

7

@jjmerelo's answer covers the basics. This complementary answer, which aims at being somewhat exhaustive but hopefully not exhausting, covers traps, rare cases, and advice.

foo: valuea, valueb, ...

Surprisingly perhaps, this is not a call of a sub or method called foo.

Instead it's a statement that begins with a label, foo:.

The say: line in your question won't work in an ordinary program:

say: <a b c>; # Useless use of constant value a b c ...

The "Useless use" warning means the <a b c> doesn't get used in a useful way. The say: isn't doing anything with the list of values. It's just a label that doesn't do anything.

Presumably you are using something like the Perl 6 REPL. The REPL automatically says the last value in a line if it isn't otherwise used, thus making the line appear to work without a warning.

.a-method:

If a postfix method call using the form .a-method has no arguments other than the invocant (the argument to the left of the ., or the current topic if there isn't an explicit invocant) then you can just write it in the form:

42.say ;

You can optionally append a colon:

42.say: ;

There's no good reason to, but it's consistent with:

.a-method: arg2, arg3, ...

If you want to supply one or more arguments (other than the invocant) to a postfix .a-method call, then you have to pick one of two ways to introduce them.

One way is to write a colon immediately after the method name, before the argument(s). There must be no space between the method name and colon, and there must be space after the colon before the method argument(s).1

For example, the following uses a colon before the Numeric argument in the following method call:

say <abc 2 def ghi> .first: Numeric ; # 2

In the above line the method call expression (.first: Numeric) ends at the statement terminator (;). If there's an enclosing sub-expression such as an array subscript then the method call expression ends at the end of that sub-expression:

say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi

The argument list of a colon form method call is also brought to a close by a valid statement modifier like given:

say .first: Numeric given <abc 2 def ghi> ; # 2

a-sub arg1, arg2, ...

This is the corresponding form for subroutine calls. The only format differences are that the sub has no invocant or . before the sub name and you must omit the colon after the sub name.

.a-method( arg2, arg3, ... )

a-sub( arg1, arg2, ... )

The other common form used for both method and sub calls is to immediately follow the method or sub name with parens to delimit arguments. The opening paren must immediately follow, without any space between the routine name and (.

Here's parens used with the .first method:

say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3

This has the advantage that it's arguably prettier than the alternative of using outer parens:

say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3

If you want to put a sub call directly inside a double quoted string, you need to prefix the sub name with an & sigil and use the postfix parens form:

my @array = <abc 2 def ghi> ;
say "first number is &first(Numeric,@array)" ; # first number is 2

To put in a method call, you again have to use the postfix parens form, and you must also provide an explicit invocant (you can't just write "Some text .a-method()"):

my @array = <abc 2 def ghi> ;
say "first number is @array.first(Numeric)" ; # first number is 2

If there are no arguments (other than the invocant for a method call) you still need to use this form with empty parens if you want to interpolate a sub or method call in a string:

my @array = <abc 2 def ghi> ;
say "no method call @array[3].uc" ;     # no method call ghi.uc
say "with method call @array[3].uc()" ; # with method call GHI
say "&rand";                            # &rand
say "&rand()";                          # 0.929123203371282

.a-method ( arrgh, arrgh, ... ) ;

This won't work.

Because the .a-method isn't followed by a colon, the method call is considered complete.

That means the next thing must be either an expression/statement ender like ;, or a postfix operator that will operate on the result of the method call, or an infix operator that will operate on the result and some following argument.

But ( arrgh, arrgh, ... ) is none of these. So you get a "Two terms in a row" compilation error.

.a-method:( arrgh, arrgh, ... ) ;

.a-method: ( arrgh, arrgh, ... ) ;

In general, DO NOT MIX use of a : with use of parens around arguments as part of a method call. There is no good reason to do so because it will either not work, or work only by accident, or work but very likely confuse readers.

Doing so without a space between the colon and opening paren yields a cryptic compilation error:

This type (QAST::WVal) does not support positional operations

Leaving a space appears to work -- but typically only by luck:

say .first: (Numeric) given <abc 2 def ghi> ; # 2

The (Numeric) is a single value in parens which yields Numeric so this line is the same as:

say .first: Numeric given <abc 2 def ghi> ; # 2

But if there are two or more arguments in parens, things will go awry. Use one of these forms:

say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1

which correctly yield the array index ("key") of the 2 element rather than:

say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil

which yields Nil because the .first method doesn't do anything useful with a single argument that's a list of the form (Numeric, :k).

Of course, you may occasionally want to pass a single argument that's a list of values in parens. But you can do so without using a colon. For the sake of clarity, it's my advice that you instead write this as:

invocant.a-method(( valuea, valueb, ... ));

a-sub ( arrgh1, arrgh2, ... ) ;

As just explained for method calls, this passes ONE argument to a-sub, namely the single list ( arrgh1, arrgh2, ... ) which will seldom be what the writer means.

Again, my advice is to instead write this as:

`a-sub( valuea, valueb, ... ) ;`

or:

`a-sub  valuea, valueb, ...   ;`

if you mean to pass multiple arguments, or if you wish to pass a list as a single argument, then:

`a-sub(( valuea, valueb, ... )) ;`

.a-method : arrgha, arrghb, ...

a-sub : arrgha, arrghb, ...

For the method form this will net you a "Confused" compilation error.

The same is true for the sub form if a-sub takes no arguments. If a-sub takes arguments you'll get a "Preceding context expects a term, but found infix : instead" compilation error.

.&a-sub

There's a call form which lets you call a routine declared as a sub -- but use the .method call syntax. The following feeds the "invocant" qux on the left of the dot as the first argument to a sub called a-sub:

qux.&a-sub

Use a : or parentheses as usual to pass additional arguments to a-sub:

sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42;                # True

(In my original version of this section I wrote that one can not pass additional arguments. I had tested this and thought one could not. But I must have just gotten confused by something. @Enheh's comment led me to retest and discover that one can pass additional arguments just as with ordinary method calls. Thank you @Enheh. :))

a-method( invocant: arg2, arg3, ... )

a-method invocant: arg2, arg3, ...

Called "Indirect object notation" in the design docs, these formats are an undocumented and very rarely seen form of method call in which the call mimics the method declaration -- the method name comes first and then the invocant followed by a colon:

say first <abc 2 def ghi>: Numeric ; # 2

Note that say is a sub call because the next token, first, isn't followed by a colon. In contrast first is a method call because the token after it is followed by a colon.

Footnotes

1 All comments about spaces/spacing in this answer ignore unspacing.

raiph
  • 31,607
  • 3
  • 62
  • 111
  • 2
    "You can not pass additional arguments to `bar` in this form." If you add parentheses, though, you can pass additional arguments: `foo.&bar(42, "baz")` – Enheh May 17 '18 at 09:27
  • 1
    ++ I never knew about the indirect object syntax, but it totally makes sense is very cool. It allows you to convert any object's method into a psuedo imperative syntax. `@foo.push: $a` is the OO form of pushing, and `push @foo, $a` is the imperative form, but the latter required defining a global sub `push`. The IO syntax allows you to get imperative style on *any* method, albeit with an colon instead of comma for the first separator: `push @foo: $a`. (In fact, I think I've actually used this form without realizing it a few times, so I'd say it's actually fairly intuitive) – user0721090601 Mar 28 '19 at 16:12
  • @guifa I just realized the indirect syntax mimics method declaration syntax (and have edited my answer accordingly). That's probably another big part of the reason it's intuitive for you. – raiph May 25 '19 at 12:28
  • 1
    Also the example you gave us a great one of where it can be helpful. *say first @foo: condition* reads beautifully (say the first foo that's condition), but whereas a lot of built-in methods have sub variants, it's not universal This syntax effectively makes it so. – user0721090601 May 25 '19 at 14:16
  • 1
    "*There must be no space between the method name and opening parenthesis1*" I think it should read `:` instead of "paranthesis" here (thanks for the answer). – Mustafa Aydın May 04 '21 at 18:08
  • 1
    The raku OO model supports (i) the public attribute variant ```has $.x is rw;``` and (ii) the private attribute variant with getter/setter methods ```has $!y; multi method y {$!y} multi method y ($val) {$!y=$val}```. To set an attribute I then have two strikingly similar syntaxes: (i) ```$obj.x = 41;``` and (ii) ```$obj.y: 42;```. IMHO, the method colon variant is a big help to keep code that calls private attributes as lean as code that calls public attributes thus encouraging strict encapsulation. (see https://p6steve.wordpress.com/2020/05/07/raku-objects-confusing-or-what/) – librasteve May 05 '21 at 20:40
  • Oh - and the imperative form ```y $obj: $val``` also echoes the signature use of colon to delineate the invocant - ```method y ( $obj : $val ) {...}``` ... very neat! – librasteve May 05 '21 at 20:59
  • 1 of 2 @p6steve I like your `$obj.y: 42;` idiom for two reasons. A) It reads well. B) One can code side-effects without using the `Proxy` class. I think an answer using your idiom would be a great addition to [this 2015 SO](https://stackoverflow.com/questions/31665384/how-does-one-write-custom-accessor-methods-in-perl6). Even though the asker wrote in their Q they wanted to use `obj.y =` syntax, they also left a "Kind of ugly ... lot of boilerplate" comment on the answer they accepted. I think they and/or readers of that Q may prefer use of your idiom given that it's pretty and avoids `Proxy`. – raiph May 06 '21 at 00:02
  • 2 of 2 The doc (and your usage, presumably following the doc) contradicts Larry's terminology on "private"/"public" relative to this topic. For very good reasons, he said *all **attributes** are always **private*** and *all **accessors** are always **public***. What jnthn was trying to convey is related. For good OO design, especially if they will be used in a concurrent setting, methods should pretty much *never* correspond to attributes. For example, if a clock time class has attributes for hour and minutes, there shouldn't be one method to update the hour, and another to update the minutes. – raiph May 06 '21 at 00:19
  • cher @raiph - we need a practical terminology for ```has $.x;``` vs. ```has $!y;``` ... ```public attribute``` vs. ```private attribute``` is most comprehensible to beginners and experts [albeit that experts know that "public attribute" elides "private attribute && (generated) public accessor methods"] – librasteve May 06 '21 at 21:12
  • @p6steve I completely agree. I've not suggested the doc should change. It's a classic example of what Damian memorably called [a pedagogical facilitation](https://www.youtube.com/watch?v=ORjyXcLDd9M#t=1m24s). I'm just pointing out that Larry consciously and carefully chose *not* to "keep it simple" in that way for very good reasons, the same ones that underlie what jnthn was trying to convey, and what I tried to with "methods should pretty much never correspond to attributes", even if they're not used in a concurrent setting, but especially if they ever will be. – raiph May 06 '21 at 22:44
6

As Raiph tells you above, say: is a label. So you didn't say anything (even though you thought you did) and -- outside use of the REPL -- the compiler will complain that your use of <a b c> was useless:

say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:␤Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)␤»

However, you often can use a : notation instead of parentheses in method calls. Consider the four routine calls below (two subroutine calls then two method calls):

my @numbers = (33, 77, 49, 11, 34);
say map  *.is-prime, @numbers  ;  # simplest subroutine call syntax
say map( *.is-prime, @numbers );  # same meaning, but delimiting args
say @numbers.map( *.is-prime ) ;  # similar, but using .map *method*
say @numbers.map: *.is-prime   ;  # same, but using : instead of parens

These sentences will all return the same (False False False True False).

In general, as you see above with map, you can use () in method calls wherever you would use :, but the opposite is not true; : can be used only in method calls.

Use () if the arguments need to be delimited precisely, as Raiph comments below.

This answer focuses on the basics. See Raiph's answer for more exhaustive coverage of the precise details of routine call syntax. (As an important example, the meaning of these calls normally changes if there's any spaces between the routine name and the colon (:) or opening parenthesis (()).

raiph
  • 31,607
  • 3
  • 62
  • 111
jjmerelo
  • 22,578
  • 8
  • 40
  • 86