Omitting argument labels in function calls with matching argument names

I apologise if this has been brought up before. :slight_smile:

Given the function declaration:

func jibber (foo: Foo, bar: Bar)

The function invocation is required to be like this:

let foo = ...
let bar = ...
jibber (foo: foo, bar: bar)

Because the argument names match the corresponding argument labels, the invocation can be simplified by dropping the labels.

let foo = ...
let bar = ...

// without labels
jibber (foo, bar)

// with labels
jibber (foo: foo, bar: bar)

This should be something really easy to do and would make the language more ergonomic.

What do you think?

Edit: bad idea, see @tera's example below. :slight_smile:

1 Like

BTW, you can go just one step further and make parameter labels optional (when there is no ambiguity):

jibber(1, 2)

This looks ok on the surface... but imagine that later on you or someone else introduces another jibber call with no parameters:

func jibber(_ foo: Foo, _ bar: Bar) { /* does something else */ }

then all places where you already had jibber(foo, bar) are suddenly and silently wrong...

All in all, I don't think this is a good idea.

2 Likes

This form looks way better than argument labels and I recommend it for all nonmutating functions. :dotted_line_face:

(jibber as (_, _) -> _)(foo, bar)
1 Like

I have wanted this, usually when forwarding one set of arguments to another API where the “prepositiony” labels have run out (that is, we’re already past the first two or three arguments and now the clearest thing to write is a nouny label). I love it in Rust for field initialization, but tera’s example shows why we can’t just do it: it’d be ambiguous with having no label. This ambiguity would of course be even less common than the problem, but it can still come up, especially in a language that encourages you to think of different full names as different methods.

I’m sure I’ve seen people play with various more explicit syntaxes for this (e.g. :message) but none yet has really been convincing enough to say “yes, that would be a definite improvement while still being readable”).

5 Likes

That's an interesting idea!

jibber(:1, :2)

Note so aesthetically pleasant but solves the above example when jibber without labels is introduced. And if another jibber with other labels is introduced compiler would start complaining forcing me to put argument names.

Agree, I think we're lacking a palatable solution rather than in disagreement that there is room for improvement.

@Jessy does make an intriguing point about stripping labels; personally, I find the explicit coercion to be still a bit verbose but one can do as follows:

let foo = ...
let bar = ...
let flibber = jibber
flibber(foo, bar) // Look ma, no labels!
3 Likes

Me too! I think it's strange that

jibber(foo:bar:)(foo, bar)

and

postfix operator ®
postfix func ®<🤳>(self: 🤳) -> 🤳 { self }
jibber® (foo, bar)

both work but

(jibber)(foo, bar) // Missing argument labels 'foo:bar:' in call

does not.

This looks, to me, like a closure that takes labeled arguments, which is not supported.

(jibber)(foo: foo, bar: bar)
3 Likes

Yet, it is different in case of a mutating method:

struct S {
    mutating func jibber(foo: Int, bar: Int) {}
    mutating func bibber() {
        jibber(foo: 1, bar: 2)
        (jibber)(foo: 1, bar: 2) // 🛑 Cannot reference 'mutating' method as function value
    }
}
1 Like