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

I disagree but that's a personal opinion. I think that's far from "one of the worst conventions in Python".

Yes, which in my opinion is a great way to highlight it as a special function. Also, regex syntax highlighters can easily detect this type of methods, which is great for forums/Stack Overflow snippets where a full IDE environment is not available.

Example:

struct Callable {
  func __call__(_ args: PythonObject...) { }
  func __call__(_ kwargs: @keyboardArguments PythonObject...) { }
  func __call__(value: Int, string: String) { }
  func otherMethod() {}
}

Personally, I don't care what the identifier is as long as it's clear that it participates in magical Swift stuff. Just having selfCall, while not also having a @staticCallable marker somewhere, just isn't visually distinctive/distinguishing enough in my book.

4 Likes

It would be great to know what is a problem with a new call keyword. Maybe someone can explain, in two words. Thanks in advance.

5 Likes

If this really must be an ordinary function, then out of all the names mentioned I prefer callWith.

I'd still prefer the originally proposed call keyword though.

1 Like

Boys, boys, so much bikeshedding when the answer is so obvious.

prefix operator 📞
struct Callable {
  prefix func 📞 (hell: Yeah) { }
}

let johnny = Callable()
📞johnny

Give it a minute... Yeah, soak it all in.... You're one of us now.

Now that we all agree :telephone_receiver: is the true way of the Function Call, I shall leave you all with an imaginary quote from the fake movie The Motrix: "Do you hear that, Mr. Bikeshed? That's the sound of deadlines whooshing over your head."

:telephone_receiver:

24 Likes

Alright, thanks for the feedback, we'll talk about it.

9 Likes

Because in many languages, double underscore is for reserved identifiers and are not intended to be used by developers.

1 Like

Perfect, because it is indeed reserved for a special purpose:

Still why do we need to highlight it or google it by name? We google key-paths as a feature not as a type, right? From my perspective it's just equivalent to talk about first or tenth unnamed function just I would talk about first or tenth __call__ / callFunction function. The compiler is free to add an extra name like __call__ for mangling. I don't understand why the user should do that manually or remember a special name that can be misspelled as you already pointed out above. Not requiring the user to remember how to call a function to opt in into this functionality is basically the same as telling the user "just omit the name like on subscripts and done".

And yes I understand that I said my position multiple times already and that this does not provide any new view angle. ;)

1 Like

A few more options:

func _call():

  • Unlikely to conflict.
  • Short & easy to spell.
  • The _ will make a developer think twice about what's going on, and is already an indicator that a method isn't intended to be called directly.

func __call:

  • Doesn't overload single underscore. (I'd rather use one of Swift's many existing patterns than introduce a new one, but it's an option)
  • Not as redundant as Python.

My preferences from the discussion:

  1. call / standalone func keyword, and casting to a function with either self(_:), self.(_:) or an as cast. The keyword adds compiler checking, which is always nice, and feels more like a native (Swifty) part of the language. It also matches subscript, a similar feature*.
  2. func _, and casting with self._. Straightforward, clearly magical, ‘function underscore swift’ is going to have some results.
  3. func self or func selfCall, since they both have self in the name, and are very straight to the point.
  4. func _call, or func __call. Options like callWith, while following naming guidelines, aren't so obviously magical.

*Aren't subscripts just anonymous functions which support setters and happen to use square brackets? This feature is almost redundant when you think about it, it's just enabling round brackets rather than square.

On that note, how about @callSyntax subscript()? That's my new 1½.

1 Like

That is not in scope for this discussion, which is specifically asking for an ordinary function name:

It should be an ordinary function name, not () or anonymous methods or anything else that would not be valid syntax in the current compiler.

The core team also laid down the following requirements:

The Core Team wanted a name that (1) used "call" instead of some synonym (e.g. "invoke"), (2) wasn't just the bare word "call" (or anything else likely to be used separately), and (3) ideally fit naturally with the precedent of SE-0216. And we didn't like "staticallyCall", or anything else with "static" in it.

So far, after 152 messages, the options that have received some amount of reaction are:

callFunction
functionCall

callAsFunction
performCall

callSelf
selfCall

callWith

__call
__call__
3 Likes

I personally am not a fan of the underscored versions. I've noticed that items in swift are underscored for 2 main reasons:

  1. The item is unstable and could change at any moment so it is not currently intended for use outside of the standard library (like @_exported)

  2. Access control limitations or protocol requirements force the item to be public but it's really meant to be used like internal/private/fileprivate

While the 2nd reason sorta fits here because the function is not intended to be used directly, I don't think it's a great fit overall with swift and would still prefer callSelf. I work in python all day and would prefer that swift remain swifty and an underscored method feels too pythony for my liking.

If we set a precedence that underscored functions have special meaning then how long will it be until people are asking to replace all special keywords like init and subscript with underscored functions? :wink:


I do agree though that having it underscored would be very clear about what is going on and anyone coming from python would instantly understand its meaning. I just personally don't like it.


EDIT: I realize that init and subscript will probably never go away, but there may be other things in the future which are added as magical underscored functions. I don't like the idea of that happening, but other people may.

2 Likes

asFunction was mentioned further up, and I think this should be added to the list. Out of the names discussed which aim for readability over pointing out magic, I like this the most because it reads well in the explicit, conversion use-case (polynomial.asFunction), and it seems like it might be less refactor-prone, because it's clearly not meant to be called directly, but used to convert the value to a function (the magic is just a bonus).

Edit: An easier to spell alternative is asFunc, although that doesn't read as well for me.

4 Likes

I'd be in favor of callSelf.
However I feel like important that user is aware about the "magic" of this method. In that regard __call__ seems like a good candidate :man_shrugging:

Also, if @dynamicCallable is revised I feel important to uniformize method names and make sure they are kind-of similar. It might help into choosing namings (or not).

1 Like

If the core team felt that it was important the user was aware of the magic here, I don't believe they would've decided on this approach, as there are a variety of ways Swift can already indicate such a thing. Considerations for adding yet another way to indicate such a thing to work around the core teams naming requirements here seems... unproductive. I would like to see an indication of magical behavior too, but I'd rather not introduce yet another way of doing so.

5 Likes

What are the varieties of existing ways to indicate at the point of declaration that an ordinary function at the point of use has magic?

What's the point of this question?

1 Like

I would have preferred to just drop ‘func’ and just say ‘call()’. It mimics init/deinit and makes it clear what’s going on.
Any name that we suggest still will have the issue of not being clear enough. Would this be the first feature that with JUST a special name gives new powers to a type?

9 Likes

AFAIK a new kind of decl (like the original proposal) is out of scope.

Otherwise, I'd prefer we introduced an operator func modifier, more like C++: it clearly indicates that those members are not usable like regular member functions, even without type-level attributes, it can be used to avoid collisions and provide better diagnostics and auto-complete, and it scales to future features.

struct Foo {
  operator func call(...) { /* ... */ }
  operator func memberLookup(...) { /*...*/ }
}

And it could be used for our other operators, like + and +=:

struct Foo {
  operator func + (other: Foo) -> Foo { /* ... */ }
  mutating operator func += (other: Foo) { /* ... */ }
}

By the way, how much nicer is it to write += as a mutating instance member like that? As opposed to what we have now:

struct Foo {
  static func += (lhs: inout Foo, rhs: Foo) { /* ... */ }
}

The only niggle is that you couldn't refer to the function for things like partial application. It's nice that the core team considers that important, but you can't even refer to more basic operators like + for those things today!

let f = Int.+= // Error
let f: (inout Int, Int) = += // Error

A general solution to that would be welcome. Perhaps something like #operator(Foo.call) or #operator(infix, Int.+). That would allow us to disambiguate between the operator "call" and a non-operator function which was coincidentally named "call".

This is what I meant in the original review when I said we should be looking at these features like operators. init/deinit are not just like normal functions; there are certain things you must do in an init, and the compiler checks that you do them, there are chaining rules, etc.

2 Likes

I agree, removing the name altogether makes most sense, since that almost exactly parallels how you would call it.

func myFunc() is called as myInstance.myFunc()
func () would be called as myInstance()

This is very close to the expected myInstance.() which would be ugly. This notation also has the advantage that it doesn’t introduce any surprising magic - it’s pretty clear than having a function with an empty name could give special behaviour, but nobody would expect special behaviour form a function named “call”.

1 Like