[Accepted with Modification] SE-0253 - Callable values of user-defined nominal types

callSelf is really not bad. I like it.

I like that very much.

This ship has sailed. The proposal is accepted. The only change we can discuss is the function name.

func callSelf() {}

And I think it's better than callInstance() or callValue() (which I liked first) because "Self" grabs an attention and makes it feel something special.

If this is really going to be a function (which I very much disagree should happen, but oh well), then IMO there's really only a single of logical name for what it should be:

func _ (arg1: Type1)

This is not a feature like subscript, because we've decided we're still going to have func as part of the signature, which is not the case when declaring a subscript-able type. Therefore, I don't think we need an additional special keyword-name.

What we're technically describing here is the ability to call a function on a type, but where that function does not have a name. In Swift, the syntax for "this is unnamed" is to use _. So having the callable function be func _ (...) is consistent with existing syntax, because it means "This is a function, but it has no name", which implies you'd call it by omitting the name, which means it is the "callable" function:

struct Adder {
    let base: Int
    func _ (adding value: Int) -> Int { return base + value }
}

let a = Adder(base: 40)
let answer = a(adding: 2) // calling a function with an omitted name
32 Likes

I'd rather see a generalization of subscripts and functions where both can be unnamed, and where both can be declared globally, not just on types.

func (value: Int) -> Int { return value }
// The only inconsistency here is the set of rules for parameter labels.
subscript (value value: Int) -> Int { return value }

// usage
ModuleName(value: 42)
ModuleName[value: 42]

Syntactically wise the accepted form of the proposal is very very special and it just adds a lot mental load than a generalization of function names with precedence to get-only subscripts. In the current state I'd rather reject the whole proposal. (That is just my personal opinion.)

2 Likes

I don't care to bikeshed much but callFunction feels very strange. I'd prefer we use call, which makes more sense and reads better, and we could all just accept that some folks may define call methods that collide with this functionality, even if they don't use this functionality.

12 Likes

I very much like this approach, mostly because it prevents people from doing instance.callFunction(foo, bar: baz)

I don't see what's the problem with people directly calling the function?

3 Likes

callOnSelf?

I'm curious as to what value you see "perform" adding in this context?

still-likin'-functionCall-ly y'rs,

Dave

Dear Swift,

I wonder whether, since these special functions will be able to be called directly on an instance/value, without referring explicitly to a member function via dot syntax, this could be encoded in the name:

func implicitCall() { ... }

or

func callImplicitly() { ... }

It occurs to me that, at heart, the distinguishing feature of these functions is that they are enabled to be called implicitly. This is their principal (and perhaps only) point of difference to all other member functions, which must be called explicitly by name, using dot notation.

It is merely a thought which I hope is of some use or interest to you.

5 Likes

functionCall reads like a noun to me, along the lines of NSInvocation. That makes it feel extremely awkward to me and doesn’t feel like it fits with the API Design Guidelines. Even if an argument can be constructed that it does align with the guidelines somehow I don’t think I will be alone in reading it differently.

callFunction is even worse. Consider this:

struct Foo<T> {
    func callFunction(_ comparator: (T) -> Bool) -> Int { ... }
}
let foo = Foo<Int>()
foo.callFunction { $0 < 42 }

In this example, the call site reads as if the only thing it does is to call the function that is passed in. This is unlikely to be the case and would hardly make sense if it was (why wouldn’t the caller just invoke it directly). I think including the verb “perform” by explicitly stating performFunctionCall (or simply performCall) is much more descriptive and far more clear at the point of use.

Personally, I think the core team is making a mistake in forcing a name on what is really an anonymous method. I especially dislike the idea that “magic” member names automatically have special behavior without anything (protocol, attribute, etc) that “activates” the special behavior. I think this direction is a bad idea and I’m disappointed to see that the core team is inviting proposals to drop attributes that are currently used to enable “magic” members.

If you’re going insist on a named method, please prioritize clarity at the point of explicit use for the name even if explicit use is not intended. Which of these call sites do you think reads more clearly?

foo.callFunction { $0 < 42 }
foo.functionCall { $0 < 42 }
foo.performCall { $0 < 42 }
foo.performFunctionCall { $0 < 42 }

In the latter two, I think it is substantially more clear that foo is “performing” the call and playing the role of a function that is called. Personally, I like performCall the best out of the options the core team appears to be willing to consider. I think a plain call is also fine, but the core team has already ruled that out.

11 Likes

I agree with this. The only benefit I see to having an identifier is to allow partial application. For this, I feel it would be better if call became a keyword, in the same vein as init. However, the Core Team already vetoed this idea, so all we can do is hope to minimize the damage.

12 Likes

I don't agree there is no benefit in having it be a regular function: it is less disruptive if there is no change in grammar and is easier to adopt for the compiler and the rest of the ecosystem.

I agree though that being able to refer by name to the function pretending to be anonymous makes things awkward. I don't think there's a way around this if it remains a regular function. I too favor something were the function does not have a real name, but here we are.


Since we're bikeshedding, anybody thought of this one yet?

foo.actUpon { $0 < 42 }
foo.actUpon("this")

I find this one both fitting and hilarious because it sounds like a precise word about doing something vague.


There's something peculiar about trying to follow the naming guidelines here. Normally, the name of the function should go hand in hand with the parameter names, forming something grammatically okayish when put together. But in our case, the name of the function is expected to be disassociated from the name of the arguments when in use...

Take the example above, I called the function actUpon even though the correct name should have been act(upon:). But if you use it as a callable value you end up with foo(upon:) which is just silly.

So I don't think it makes sense to follow too strictly the guidelines about function naming here. We need new guidelines about naming parameters for those callable values as the current rules aren't cut for this. And perhaps the name of the "anonymous" function could be chosen so to fit parameters following the guidelines in a way that the result is grammatically okayish too.


First round: which one reads better?

foo(source: "hello.txt", destination: "world.txt")

func actUpon(source: String, destination: String)
func callFunction(source: String, destination: String)
func functionCall(source: String, destination: String)
func performCall(source: String, destination: String)
func performFunctionCall(source: String, destination: String)
func callSelf(source: String, destination: String)

It would be the exact same grammar as init. And there are a handful of compiler developers, and 100s of thousands, if not millions, of Swift language users. To optimize for ease of compiler writing seems like the wrong trade-off. Anyway, this an agree-to-disagree moment, because the CT has spoken, and this is not the place to try and relitigate.

1 Like

It was pointed out during review that there's no way to have a nameable function that will always read well when invoked by name, as well as by variable. (i.e. both var.callFunction(...) and var(...))

I love this idea, no func callSelf(…) {} or the like, just call(…) {}, right?

1 Like

Why have two ways to do something when one will suffice?

Thinking about it more, one disadvantage to having no name for this would be the inability to make a key path to this function.