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

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

I understand the desire to view subscript as precedent, but it necessarily has different syntax because it declares accessors.

Completely agreed.

-Chris

I would love to know if anyone is strongly opposed to the staticallyCall syntax. I think callFunction is clearly opposed by some people so I think staticallyCall at least could be a better compromise agreement.

1 Like

The core team discussed staticallyCall and other variants that incorporated the word "static". The problem is that this can exist as protocol requirements and on classes, which are dynamically dispatched (through existentials and subclasses). We'd prefer to avoid the opportunity for confusion if possible.

Joe has some ideas (I don't know if they are baked or not) and would like to see the existing dynamic callable feature get revised (beyond removing the attribute and restrictions) to do something like kwargs in Python. This could lead to the existing entrypoint getting renamed, so I wouldn't over index on the dynamicallyCall entrypoint naming.

-Chris

3 Likes

If you are referring to my post I used "awkward" in the sense of alien to Swift syntax and inconsistent with other API, not in the "awkward to read" sense.

Is func () (s: String, i: Int) -> Void { } disqualified by this?

func () would by my preference

2 Likes

What about callValue? Since the function defines a way to call a value directly, that seems pretty clear and concise to me. It can leave things open to allowing types to be called directly too with a different function name, if in the future a different name is desired for that.

Yes.

1 Like

Ok, that's interesting. So, adding kwargs to Swift would mean that dynamicallyCall and staticallyCall could be centralized in a unique magic function-name.

struct BothCallable {
  // Dynamic with `*args`
  func callFunction(_ args: PythonObject...) { }

  // Dynamic with `**kwargs` (invented syntax)
  func callFunction(_ kwargs: [String : PythonObject...]) { }

  // Static
  func callFunction(value: Int, date: Date) { }

  // Static + Dynamic
  func callFunction(_ value: Int, _ kwargs: [String : PythonObject...]) { }
}

Taking that in account the naming feels a little bit more reasonable :smile:

1 Like

What about:

func callDelegate() { ... }
2 Likes

I would like to better understand the rationale for rejecting anonymous / unnamed methods. Is it primarily that you feel very strongly about being able to name the entity directly?

Given the insistence on a compound, currently valid identifier, how about performCall or performFunctionCall? Those read substantially better to me than callFunction which feels quite awkward and is easy to interpret in ways that don’t align with what the method actually does. The potential for confusion with callFunction is especially prevalent if the signature accepts a single argument of function type.

11 Likes

The syntax func callFunction is delightfully redundant.

What sets apart this particular function from every other function--which, as noted by others, are all callable--isn't that it's a function. Rather, it's that it enables a callable value.

Let's name it as such:

  func callValue(/* args */) {
    /* ... */
  }

// or

  func callInstance(/* args */) {
    /* ... */
  }

Edit: or @lancep's suggestion below, callSelf (or even selfCall?).

4 Likes

I would prefer callAsFunction() as suggested before. It clearly conveys what happens.

2 Likes

This might be really horrible, but what about func callSelf?

17 Likes

I don’t feel too bad about this one actually. At least it clarifies the object being called much better than callFunction.

3 Likes

I wouldn’t see that as a conflict, but every regular function name could be used by someone who doesn’t want a callable type (that could mean that this ability is just ignored, though).

„Googlability“ however is imho only a strong argument when it‘s about marketing - and in the context of this feature, I hardly see any benefit at all:
People might search for „@someAttribute func“, or „method without a name“ when they encounter it. On the other hand, „func callFunction“ is not even a trigger for research - it‘s just a method with an odd name and no indication that there is something special about it.

3 Likes

func functionCall reads better than func callFunction IMO.

4 Likes

+1 for selfCall

3 Likes

Probably mentioned before, but what about having global functions of the form call(_:) whose first argument is the type to call, and that can be quasi-overloaded with various types and parameters?

For example:

struct Filter {}

// Note: These are global functions, not instance/static methods
func call(_ filter: Filter) {} // No arguments
func call(_ filter: Filter, alpha: Double) // Additional parameters

let filter = Filter()
filter()
filter(alpha: 0.5)

If you want to reference the function, that’s easy enough, plus you can disambiguate between overloads by providing argument labels:

func call(filter: Filter, alpha: Double) {}
let function = call(filter:alpha:)

Why do I think this is worth considering?

  • Supports both types and metatypes
  • Follows API guidelines. “call object” -> call(_ object: Object)
  • Far less likely to collide than a call member function (in my unscientific opinion)
  • Still allows for referencing the function directly/passing as parameter
  • Doesn’t require new keyword or anything. Essentially same as proposed solution but just global function

Since it doesn’t require any additional language features, it’s (hopefully) just as simple to implement. Only thing I can think of is maybe that would be too difficult for the compiler with all the overloads?

Also +1 for selfCall or callSelf