[Pitch #3] Introduce user-defined dynamically "callable" types

@rxwei makes an interesting suggestion, although I do believe Swift naming guidelines elide 'with' for parameter labels.

I'm on the fence about whether the function itself should be a verb ("dynamically call") or simply a noun ("dynamic call"); if we're going to call the attribute @dynamicCallable and not @dynamicallyCallable, I think on balance I'd favor the original.

Swift naming guidelines elide ‘with’ for parameter labels.

@xwu I don't think it exists in the API design guidelines, but I'd appreciate it if you can point me to such writing.

Dynamic calls can be mutating the underlying object. I believe a verb-like name is more appropriate. For example:

let array: PyValue = [1, 2, 3]
array.append(4)

If we desugar this dynamic call, the dynamicallyCall name sounds very reasonable. It's an action (call "append"), and I can't tell whether it's mutating the underlying data or not.

array.dynamicallyCallMethod(named: "append", withArguments: 4)

While a noun-like name below seems to imply that the function is "returning a dynamic call named 'append' with argument 4", without performing the call action.

array.dynamicMethodCall(named: "append", withArguments: 4)

You can see it in the detailed write-up of how the guidelines are applied in SE-0005 and SE-0006. For example:

  • In migrating the standard library from Swift 2 to Swift 3 based on the naming guidelines, joinWithSeparator(_:) becomes joined(separator:).
  • In translating Objective-C names, fillWithBlendMode(_:alpha:) becomes fill(blendMode:alpha:) (and many other examples).

It's impossible to say whether a dynamic call is more likely to be mutating or not, nor do I think it's wise to associate that consideration with the name of the underlying method here. At best, the name could be ambiguous, but ultimately I don't think that the consideration is applicable since a dynamic call self-evidently rules out any way to know for sure.

I think withArguments here conforms with the guidelines. The obj -> swift grand renaming is not part of the guidelines as far as I know. At least I do not see evidence of this in the guidelines.

[Full bikeshed mode enabled]

Shouldn't it be called withParameters since we are defining the the kind of parameters that the function we are calling can take? I guess it is both parameters (the key) and arguments (the value to the key).

I do think that named could be dropped as Method describes the name.

  func dynamicallyCallMethod(S1, withArguments: [T5]) -> T6
// or
  func dynamicallyCall(method: S1, withArguments: [T5]) -> T6

3 Likes

IMO, withArguments conforms to the official guidelines. The guidelines also have an example using with:.

I don't have a strong opinion about named:. On one hand, I agree that dropping would make it simpler. OTOH, given that users will implement this method for syntactic sugar someCallableStuff(), there's no problem with this method being verbose for clarity.

I think "parameter" refers to a formal parameter in the function declaration. "Argument" refers to a value being passed in to match a parameter's type.

From The Swift Programming Language: Redirect

Every function has a function name, which describes the task that the function performs. To use a function, you “call” that function with its name and pass it input values (known as arguments) that match the types of the function’s parameters. A function’s arguments must always be provided in the same order as the function’s parameter list.

It falls under the guideline of “omit useless words”—the Objective-C to Swift renaming was conducted to adopt Swift API naming guidelines and demonstrates how those guidelines are intended to be applied. See SE-0005/6 for more.

Yes, “with:” conforms to the guidelines because, where a label is necessary, it cannot be omitted as then there would be no label. However, where there are other words in the label, “with” is elided, as the parentheses surrounding the arguments imply “with.” See SE-0005/6.

1 Like

+1 on removing with from the argument labels. It feels more swifty and clean.

1 Like

Cool. We are good then because withArguments is referring to the arguments of the dynamic call. This is much different than saying withString or something along those lines. I am not saying with should stay but we do need a label here.

I think a better name for this label should be passing. Or passingArguments or passingKeywordArguments

You call the greet(person:) function by passing it a String value after the person argument label, such as greet(person: "Anna"). -Swift Book (Emphasis mine)

I prefer just passing but being more descriptive is not too bad.

  func dynamicallyCall(passingArguments: [T1]) -> T2

  func dynamicallyCall(passingKeywordArguments: [String: T3]) -> T4

  func dynamicallyCallMethod(S1, passingArguments: [T5]) -> T6

  func dynamicallyCallMethod(S2, passingKeywordArguments: [S3: T7]) -> T8

@xwu's point is fair.

I think a better name for this label should be passing.

Passing feels like a needless word to me. arguments: and keywordArguments: imply that they are being passed.

OTOH, I do agree passing adds clarity for dynamicallyCall. dynamicallyCall(arguments:) reads "dynamically call these arguments", which would be the wrong meaning (although in cases it is expected to convey that meaning it would be called dynamicallyCallArguments(_:)).

In the normal case it would be true. My hesitation is based on the nested, "meta" aspect of the method; dynamicallyCall, dynamicallyCallMethod are just forwarding their call to the target language and while they do take arguments themselves to make this happen, they are just "forwarding" or passing the call down.

[Full bikeshed mode disabled]

Although @dynamicMemberLookup and @dynamicCallable are mainly designed for interoperability, but these features alone do not have any indication of meta-programming or language interop. They are just pure syntactic sugar.

Would dynamicallyCall(forwarding arguments: [T5]) -> T6 make sense? Or does the label need to include arguments here?

dynamicallyCall(forwarding:) becomes a little unclear.

x.dynamicallyCall(forwarding: y)

At the call site, I don't know what's being forwarded. I can't tell from the type signature either.

That makes sense, thank you.

At least c# seems to blend these distinctions when referencing to syntatic sugar like variadic parameters.

If you look at this from a different angle, isn't the dynamicCall methods just syntactic sugar for swift currently limited variadic parameters? I am more convinced the label should be called parameters instead of arguments

Just to give a pragmatic view here:

In userland Swift:

  • The left hand side of a function type is called "parameter types".
  • At a function call site, the stuff being passed are called "arguments".

This is also followed in the implementation detail of Swift. Concrete things being passed into a function are consistently referred to as "arguments", e.g. ApplyExpr arguments, ApplyInst arguments, SIL basic block arguments.

1 Like

Variadic parameters are part of a function type, so they follow the same terminology used in "function parameter types".

Github search Results for Swift

typealias Parameters 17,980 results

typealias Arguments 32 results

This may not mean much but it does seem much popular to call something parameters.