Require parameter names when referencing to functions

How about specifying parameter names as well as their types? That seems to be the only resolution to the ambiguity brought up by @wadetregaskis in response to @beccadax.

I very much like this proposal.

However, wouldn't this syntax change be source breaking?

This is clearly a big hole in Swift as it hinders Swift's expressivity and introduces tons of ambiguities in its current state. Though this is a breaking change, I only foresee it causing more problems in the future, so I hope this gets changed sooner rather than later.

7 Likes

@soroush Did this ever get a proposal?

Sadly, no. I feel like we didn't really make any decisions about which direction the proposal should go in, and it kind of fell off my radar. Happy to work on a proposal if we have a direction in mind!

I think it's definitely time this is resurrected and implemented. One should be required, as suggested, to use the full name of a function (e.g with its labels) when referring to a function -- with no chance of it being returned to its current state.

All of them, or just external argument labels (so, excluding _ names)?

I think everyone had agreed on foo(_:bar:). The only disagreement was on when, if ever, foo would be allowed.

foo on its own should never be allowed unless there are NO parameters.

func foo() must be referred to as foo
func foo(bar: Int) must be referred to as foo(bar:)
func foo(_ bar: Int) must be referred to as foo(_:)

I think the best thing to do is vote for one of the 5 options above. @austintatious, it sounds like you’re a hard 2?

1 Like

Is there no exceptions for operators and functions without external parameter names? There was much derision (including from myself) for forcing ugly map calls.

1 Like

Two (three) cents from me:

Functions without arguments could be written with a Void argument (just like Void is used for return values):

foo(Void)

If you leave out the comma, wouldn’t it be okay to leave out _, too?

let x = array.reduce(0, max(::))

Shouldn’t the return type be part of the syntax?

func foo(_ x: Int) -> String { "\(x)" }
func foo(_ x: Int) -> Int { x*2 }

let a : [Int] = [ 1, 2, 3 ].map(foo(:))   // Should have to be: .map(foo(:)->Int)

Swift isn't C. The "myFunc(void)" syntax from C(++) is a legacy irregularity. It was required because "myFunc()" was (kind of) the same as "myFunc(...)" in ancient C! And void isn't an actual type that can be used for objects.

But in Swift, Void is a full type with instances, and said instances can be passed around. So a function that can take a single argument of type Void is a legitimate signature. Since we have regularity, we represent zero arguments by actually having zero arguments.

5 Likes

I'm probably alone here, but I would prefer to keep the status quo.
Currently, when no additional information is given at the reference site, if you have both a variable and a function sharing the same name, the first one gets chosen.

1: let f = 2
2: func f(_ x: Double) {}
3: func f(_ x: String) {}
4: func f<T: Decodable>(_ x: T) {}
5: func f<T: Encodable>(_ x: T) {}
6: 
7: let g = f  // g: Int = 2

Also, as others already mentioned, you cannot always disambiguate by means of the parameters labels only. To correctly disambiguate the two f functions at lines 2 and 3, you need to explicitly annotate f's type signature (and I don't know if the fs at lines 4 and 5 can be referenced at all since the type signature is generic).

What I suggest is to add an heuristic to first type check assuming that the ambiguous reference is a variable and to proceed with functions only if it fails. I know it's an heuristic, but from the example above it looks like it's already been used, so it's the currently expected behavior.

If Sequence.count has worked with no issues until now, it should still work as expected after adding Sequence.count(where:). In order to reference the latter, you need to either use

let l = [0, 1, 2].count(where:)
let l = [0, 1, 2].count as ((Int) throws -> Bool) throws -> Int
1 Like