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

If this absolutely must have a regular looking function that's magically transformed into a call, I think callSelf sounds the best out of all the versions I've seen so far.

I imagine an anonymous function would form a key path much like subscripts:

\Callable.(label:)
4 Likes

Ok, that could work.

This would be just perfect. :slight_smile:

Referring to unnamed methods could also be made possible by the key-path syntax.

let closure1: (Callable) -> (Param) -> ReturnType = \Callable.(label:)
let closure2: (Param) -> ReturnType = self(label:)

Can we get some clarification on how exactly this change of mind came about? I've been pondering this for the last couple days, and I really can't understand how the core team has flipped on this so quickly...

These features have barely been released properly yet we're already rethinking them? I'm a little worried this reflects an issue in SE where we're simply not staging/nurturing these features enough before they get "accepted". Or that the Core Team isn't making their voices heard in the initial design/discussion, which I think is a problem.

Now, a little more concretely. Can we get some guidance on when we should be thinking about using an attribute for a new feature vs just accepting magical behavior? TBH I'm pretty surprised by this desire to allow for magic to happen without calling something out. The closest analogy I can think of is Python's __NNNN__ methods, where the magic is clear from the __, but everything else is a standard method definition. And it's clear from the __ that the particular method is not something meant to be called standalone.

10 Likes

Using func _(label: String) would allow us to do \Callable._(label:). It also

  1. Gives it a symbol
  2. Has precedence in the language with saying "don't give this parameter a name"
  3. Can't have been used before since it's currently not legal
11 Likes

But there is no point in requiring an underscore for unnamed functions. Precedence from my point of view are get-only subscripts, which do not require a name (but potentially could). The only place where an underscore might be sufficient is for properties as they likely would require a type:

struct Callable1 {
  // this example would be illegal as the computed property and the unnamed
  // method would collide, but you get the idea
  var _: (Int) -> Int {
    return self(label:)
  }

  func (label value: Int) -> Int {
    return value
  }
}

Requiring an underscore for unnamed functions is just unnecessary visual noise.


We could potentially have unnamed properties as well (but likely computed only?).

struct Callable2 {
  var (Int) -> Int {
    return function(label:)
  }

  func function(label value: Int) -> Int {
    return value
  }
}
1 Like

It’s also worth pointing out that you can write an _ name today using backticks:

struct S {
    func `_`() {}
}
let s = S()
S._()
2 Likes

It is a noun, along the lines of subscript (which is a noun in my Mac's Dictionary.app, referring to a syntactic construct). The normal calculus of the naming guidelines don't apply here, for so many reasons:

  • It will very seldom appear at the use site—that's the whole point of this feature—so it's more important that the declaration read well and be easy to talk about.
  • It's a name with special status in the language because it confers syntax sugar.
  • It's precedented in the way people talk about programming, where we talk about "defining a function call operator." Here it would be a "function call method."

feels-natural-enough-to-me-and-I-never-argued-for-callFunction-ly y'rs,
Dave

4 Likes

The problem is that the thing it names is not itself a function call. I think this is super awkward. The fact that you argue for discarding Swift’s naming conventions for this method is a strong argument (IMO) that it should not be named at all.

This whole approach to the design of this feature is much too magical for my taste. Difficulty in naming is a symptom, not the root problem.

8 Likes

Personally, as a professional Swift programmer, I would expect the method name to be spelled “_”. Alternatively, I would expect some special syntax à la subscripts.

Honestly, if I saw a method called “callFunction” in our codebase, I would think that someone probably forgot to fix their temporary (bad) API naming in a hurry. Then I would log an issue to refactor it.

12 Likes

The thing it names is not a function call in exactly the same way the thing subscript names is not a subscript, so I don't see that as a problem. I understand that it raises a visceral feeling of awkwardness for you, but IMO that shouldn't be used an argument in designing language features. I'm hardly discarding Swift's naming conventions—after all, they were developed by me in collaboration with others and I authored all of the wording, and I still think they do the job they were meant to do: provide guidance that leads to readable code in the vast majority of cases. That said, they were never meant to be read as an absolute prescription that could anticipate every naming decision.

This name is a special case, and deserves to be considered differently. There are going to continue to be special cases as the language evolves, and we can't leave them all un-named, so I do not buy the argument that "existing naming conventions don't work here" implies "there should be no name."

4 Likes

Rethinking this feature as a statically callable + dynamically callable I think the best option would be to reconsider the subscript-like syntax from the first proposal. It would read great and we would not need to use magic function names (which unlike Python __call__-style ones cannot be easily differentiated).

I think it is a big enough feature to deserve its own syntax:

// The Grand `Callable` Unification:

struct Callable {
  // Dynamic with `*args`
  call(_ args: PythonObject...) { }

  // Dynamic with `**kwargs`
  call(_ kwargs: @keyboardArguments PythonObject...) { }

  // Static
  call(value: Int, string: String) { }

  // Static + Dynamic
  call(value: Int, _ kwargs: @keyboardArguments PythonObject...) { }
}
1 Like

I don't like the idea of making call-syntax delegate methods have no name. I think that anonymous function declarations (func _(...)) should be reserved for other potential language features that use a function declaration to extend an existing function's behavior, such as dynamic method replacement and differentiable programming. In these use cases, the function declarations do not need a name.

@dynamicallyReplacing(foo)
dynamic func _() { ... }

@differentiating(sinf(_:))
func _(x: Float) -> (value: Float, differential: (Float) -> Float) { ... }
1 Like

subscript isn't dressed as a normal member and I think that makes a meaningful difference here.

I am not arguing that my feelings should carry any weight at all. I'm arguing that I think the proposed naming is unclear and likely to be confusing for people. Clarity is our goal in naming, isn't it?

I'm not arguing that in general. And my argument isn't strictly about existing naming conventions. My argument is that most people don't seem to be 100% convinced by or comfortable with any of the long list of names that have been suggested. My guess is that if the core team was confident in naming we wouldn't even be having this discussion. Underlying the general discomfort and difficulty in making a decision is the fact that there really isn't a great name because this is actually modeling a signature without a name.

2 Likes

Yes, that's our goal, but AFAICT you haven't actually made the argument that functionCall is unclear and will confuse people. How do you imagine it causing confusion? What problematic thing will people think when they see it?

I'll admit that functionCall is better than most of the alternatives discussed in the thread. I see your argument for analogy with subscript. But I'm not convinced that it makes sense to use that grammatical form for a normal member when we have the perfectly good verb forms performCall and performFunctionCall available. It is the difference in grammatical form while using normal member syntax that will be unfamiliar and could trip people up.

This is somewhat my fault as an individual; remember that the original announcement here just picked a name. I wasn't totally comfortable with not giving the broader community an opportunity to bike-shed, and the rest of the core team is humoring me a bit here. I was hoping that explicitly laying down boundaries for the discussion would actually keep it on track, and that hasn't been a complete success, but I think it's helped a little.

The most promising candidates right now seem to be functionCall and callSelf, so if there are any last-minute thoughts, please get them in within the next day.

5 Likes

I feel func function would be a good one-word name for this.

1 Like

I quite like the first naming suggestion which is callAsFunction().

3 Likes