# SE-0216: User-defined dynamically callable types

**URL:** <https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615>\
**Category:** Proposal Reviews\
**Created:** [June 13, 2018, 3:30am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615 "2018-06-13T03:30:40Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 14, 2018, 6:56pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/21 "2018-06-14T18:56:11Z")

</div>

The errors occurs with the current logic because the `JSDynamicMemberLookup` protocol does not define a `subscript(dynamicMember:)` method, it only lists it as a requirement.

The following does compile, however:

```swift
@dynamicMemberLookup
public protocol JSDynamicMemberLookup {}
extension JSDynamicMemberLookup {
  subscript(dynamicMember name: String) -> JSValue {
    get { fatalError() }
    set { fatalError() }
  }
}

```

This behavior is easier to implement: `@dynamicMemberLookup` (and `@dynamicCallable`) is simply a sugar for a concrete method. Supporting the protocol requirement case is non-trivial.

I'm curious what your use case for a protocol like `JSDynamicMemberLookup` is?

---

<div class="post-metadata">

**Author:** ![pvieito](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pvieito/32/33613_2.png) [@pvieito](https://forums.swift.org/u/pvieito)\
**Post date:** [June 14, 2018, 8:27pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/22 "2018-06-14T20:27:27Z")

</div>

> What is your evaluation of the proposal?

Strong +1.

> Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

> Does this proposal fit well with the feel and direction of Swift?

Yes, it complements `@dynamicMemberLookup` and hugely improves Swift's dynamic languages interoperability story.

> How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Reading the proposal, following the pitches and using the implementation available with Swift for TensorFlow.

---

<div class="post-metadata">

**Author:** ![johnfairh](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/johnfairh/32/760_2.png) [@johnfairh](https://forums.swift.org/u/johnfairh)\
**Post date:** [June 14, 2018, 8:59pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/23 "2018-06-14T20:59:22Z")

</div>

> [@dan-zheng](#):
>
> [...] implementation difference/difficulty.

Fair enough; it did dawn on me as I read your patch that 'these two things are not the same'...

May take you up on the tips offer later in the summer.

---

<div class="post-metadata">

**Author:** ![benrimmington](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/benrimmington/32/695_2.png) [@benrimmington](https://forums.swift.org/u/benrimmington)\
**Post date:** [June 14, 2018, 9:45pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/24 "2018-06-14T21:45:32Z")

</div>

> [@dan-zheng](#):
>
> I'm curious what your use case for a protocol like `JSDynamicMemberLookup` is?

My use case is `@dynamicMemberLookup` for the `JSContext` and `JSValue` classes (imported from Objective-C) without subclassing.

```swift
import Foundation
import JavaScriptCore

@dynamicMemberLookup
public protocol JSDynamicMemberLookup: AnyObject {}

extension JSDynamicMemberLookup where Self: JSValue {

  public subscript(dynamicMember name: String) -> JSValue {
    get {
      return objectForKeyedSubscript(name as NSString)
    }
    set {
      setObject(newValue, forKeyedSubscript: name as NSString)
    }
  }
}

extension JSValue: JSDynamicMemberLookup {}

extension JSDynamicMemberLookup where Self: JSContext {

  public subscript(dynamicMember name: String) -> JSValue {
    get {
      return globalObject![dynamicMember: name]
    }
    set {
      globalObject![dynamicMember: name] = newValue
    }
  }
}

extension JSContext: JSDynamicMemberLookup {}

let context = JSContext()!
context.Math.PI.toDouble() == .pi // true

```

---

<div class="post-metadata">

**Author:** ![hartbit](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hartbit/32/738_2.png) [@hartbit](https://forums.swift.org/u/hartbit)\
**Post date:** [June 15, 2018, 5:52am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/25 "2018-06-15T05:52:32Z")

</div>

I’m worried about the fact that the proposal has relegated support for Smalltalk-like languages to the future directions section. I think it’s important for this proposal be able to support writing bindings to Ruby and I’m concerned that if it’s not done now, it may never be done.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 15, 2018, 6:49am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/26 "2018-06-15T06:49:46Z")

</div>

I see, good point!  
Indeed, with the current design there is no way to (usefully) extend an existing type with `@dynamicMemberLookup` or `@dynamicCallable` behavior.

This is a notable deficiency in cases where one can't change the declaration of the existing type.  
Here, I might argue that the proper solution is for JavaScriptCore to implement `@dynamicMemberLookup` and `@dynamicCallable` behavior. 🙂

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 15, 2018, 7:04am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/27 "2018-06-15T07:04:54Z")

</div>

Your concern is valid. It seems to me the only major language interop fully supported by `@dynamicCallable` at the moment is Python. However, many people are interested in Ruby/Objective-C/JavaScript interop, which all require `@dynamicMemberCallable` behavior.

Personally, I don't see `@dynamicMemberCallable` as something that's in danger of never being done. I have a clear idea of how to implement it and I'm happy to share ideas if anyone's interested in tackling it. Existing code for dynamic member lookup/callable behavior should definitely be reused/generalized.

I might tackle it myself over a weekend, when I get a chance.

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [June 15, 2018, 6:39pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/28 "2018-06-15T18:39:53Z")

</div>

+1 to the proposal. It's an important feature with many intriguing applications, and seems like a good design.

The use of `ExpressibleByDictionaryLiteral` is particularly elegant, allowing implementations which can assume unique keywords to use dictionaries, while not ruling out those that can't.

* * *

I'll echo David’s concerns about `@dynamicMemberCallable` being left behind. It does seem best to review it separately, but that proposal should not lag too far behind the heels of this one.

* * *

This confused me:

> This proposal does not introduce the ability to provide dynamically callable static/class members.

Does this refer to something like `MyType.dynamicThing("foo")`? If so, wouldn’t that be supported via `static var dynamicThing: MyCallableType`? If not, what does it mean?

---

<div class="post-metadata">

**Author:** ![beccadax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/beccadax/32/34217_2.png) [@beccadax](https://forums.swift.org/u/beccadax)\
**Post date:** [June 15, 2018, 8:21pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/29 "2018-06-15T20:21:40Z")

</div>

The general class of functionality this provides is important, but I have some misgivings about this design.

Swift is moving towards a model where argument labels are part of the name of a function, not part of the function itself. We've discussed this direction many times in the past—for instance, when we [removed argument labels from function types](https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md) and [made associated value labels part of a case's name](https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md).

`dynamicCall(withKeywordArguments:)` ignores this direction in the language's evolution. The semantics of these calls are fundamentally quite different from a normal Swift function. I worry that, as we try to evolve the AST to improve the extremely messy way we currently handle Swift calls, we may hit limits imposed by this proposal. I don't want to end up in a situation where we're handling Swift code worse in order to handle Python code better.

My other concern about this design is that it is laser-focused on Python. The proposal gestures at how one feature might be used in Javascript, but it's not ideal for that. It doesn't handle Smalltalk and Ruby at all (and IMHO, misses the main challenge of Ruby—it doesn't need to access argument labels early, it needs to handle ambiguity between calls and property accesses). It doesn't help us with any problems outside language bridging.

On a bikeshedding front, I worry that the distinction between "dynamic callable" and "dynamic function" (as in the `dynamic` keyword) may be too subtle, and that if the `@dynamicMemberCallable` feature suggested in "future directions" is later adopted, the distinction between `@dynamicCallable`, `@dynamicMemberCallable`, and `@dynamicMemberLookup` will also be too subtle.

* * *

I think this functionality should be implemented as two orthogonal features:

1. Add a variadic list of argument labels to the lookup done by `@dynamicMemberLookup`. This would let you look up a member whose name includes argument labels.

2. Add a way to use plain function call syntax on a non-function type, along the lines of C++'s `operator ()`. Python would declare one of these with a variadic list of arguments, but the feature would be able to serve many other use cases, too.

This design would be worse for Python, but better for everything else. It would slot more readily into Swift's semantics and it would cover many more use cases. The downsides are that it makes the Python bridge's job harder (it would need to save the labels seen in the `@dynamicMemberLookup` call inside the instance it returned), and that a user would not be able to assign a `PyObject` representing a function to a variable and then call it with keyword arguments using the ordinary function syntax (which they wouldn't be able to do with a Swift closure, either). I think a little bridge awkwardness is worth having a more flexible, semantically simpler feature.

* * *

I think good dynamic bridging is a great thing to add to the language, but I don't think this particular design is right for Swift. It is too limiting and too limited. I urge the core team to reject SE-0216 and ask for a different solution to the same problem.

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [June 15, 2018, 8:31pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/30 "2018-06-15T20:31:14Z")

</div>

> [@beccadax](#):
>
> It doesn't handle Smalltalk and Ruby at all (and IMHO, misses the main challenge of Ruby—it doesn't need to access argument labels early, it needs to handle ambiguity between calls and property accesses).

As I understand them based on this proposal and past discussion, `@dynamicMemberCallable` plus `@dynamicMemberLookup` will handle Ruby just fine. A Ruby bridge would not use `@dynamicCallable` at all.

---

<div class="post-metadata">

**Author:** ![beccadax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/beccadax/32/34217_2.png) [@beccadax](https://forums.swift.org/u/beccadax)\
**Post date:** [June 15, 2018, 8:48pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/31 "2018-06-15T20:48:06Z")

</div>

`@dynamicMemberCallable` is not part of this proposal, so if we need `@dynamicMemberCallable` to handle Ruby, this proposal doesn't handle Ruby. That's my point—the proposal is very narrowly targeted at handling the needs of a Python bridge, and is too inflexible to handle even minor variations in requirements.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 15, 2018, 8:51pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/32 "2018-06-15T20:51:51Z")

</div>

> [@beccadax](#):
>
> I worry that, as we try to evolve the AST to improve the extremely messy way we currently handle Swift calls, we may hit limits imposed by this proposal. I don't want to end up in a situation where we're handling Swift code worse in order to handle Python code better.

Could you expand on possible "limits imposed by this proposal", or problematic AST changes that you envision?

I think, from an implementation perspective, `@dynamicMemberLookup` and `@dynamicCallable` are exceptionally simple because they are syntactic sugar. This would make them flexible to changes in the AST.

> [@beccadax](#):
>
> 1. Add a variadic list of argument labels to the lookup done by `@dynamicMemberLookup` . This would let you look up a member whose name includes argument labels.

Can you share a use-case where it's necessary in a language to look up a "member whose name includes argument labels"?

> [@beccadax](#):
>
> Add a way to use plain function call syntax on a non-function type, along the lines of C++'s `operator ()` . Python would declare one of these with a variadic list of arguments, but the feature would be able to serve many other use cases, too.

[I explored "regular callable" behavior](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/5) and I agree that it is more general.  
However, for keyword-argument calls, it doesn't desugar argument labels like `@dynamicCallable` does. This is unnatural for language interop:

```swift
// With @dynamicCallable.
np.random.randint(-10, 10, dtype: np.float)
// With regular callable sugar. The empty strings are killer.
np.random.randint(["": -10, "": 10, "dtype": np.float])

```

I think "regular callable sugar" and `@dynamicCallable` solve different problems and should be evaluated separately.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 15, 2018, 9:02pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/33 "2018-06-15T21:02:56Z")

</div>

> [@beccadax](#):
>
> `@dynamicMemberCallable` is not part of this proposal, so if we need `@dynamicMemberCallable` to handle Ruby, this proposal doesn't handle Ruby. That's my point—the proposal is very narrowly targeted at handling the needs of a Python bridge, and is too inflexible to handle even minor variations in requirements.

Perhaps the solution is as simple as not creating a new attribute `@dynamicMemberCallable`, but rather extending `@dynamicCallable` to handle the two new `dynamicallyCallMethod` methods using ambiguity resolution. Would this address your concern?

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [June 15, 2018, 9:09pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/34 "2018-06-15T21:09:13Z")

</div>

> [@beccadax](#):
>
> `@dynamicMemberCallable` is not part of this proposal, so if we need `@dynamicMemberCallable` to handle Ruby, this proposal doesn't handle Ruby.

To be fair, the proposal does say as much. I see this as a family of three proposed features which when taken together do appear to handle all the dynamic language bridging needs, at least all the ones I'm aware of — and do so within an overall division of labor that makes sense to me.

Now if the third (`@dynamicMemberCallable`) proposal never materializes, then I do share your concern! Assuming it does, however, I don’t see a problem with this overall design vis-a-vis Ruby.

That’s not to comment on your other concerns; I just don’t see that lack of Ruby support in particular should count against this proposal. That’s more of a comment on Ruby itself, which has does not have any notion of callable things separate from method dispatch.

---

<div class="post-metadata">

**Author:** ![Dante-Broggi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dante-broggi/32/7955_2.png) [@Dante-Broggi](https://forums.swift.org/u/Dante-Broggi)\
**Post date:** [June 15, 2018, 9:30pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/35 "2018-06-15T21:30:52Z")

</div>

> [@dan-zheng](#):
>
> Can you share a use-case where it's necessary in a language to look up a "member whose name includes argument labels"?

@beccadax's argument seems to me to be that due to [SE-0111](https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md) and [SE-0155](https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md), **Swift itself** is becoming a language where it's necessary to look up a "member whose name includes argument labels".

---

<div class="post-metadata">

**Author:** ![beccadax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/beccadax/32/34217_2.png) [@beccadax](https://forums.swift.org/u/beccadax)\
**Post date:** [June 15, 2018, 9:35pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/36 "2018-06-15T21:35:30Z")

</div>

> [@dan-zheng](#):
>
> Could you expand on possible "limits imposed by this proposal", or problematic AST changes that you envision?

Notionally, a call in Swift has two separate steps:

1. Look up the callee.
2. Call it with some arguments.

In theory, `CallExpr` should just have a callee expression, a list of argument expressions, and a few more odds and ends. In practice, it currently has a lot more than that, and most of it is redundant:

- `getArg()`, the argument tuple
- `getArguments()`, the individual argument expressions
- `getArgumentLabels()`, the labels for the arguments
- `getArgumentLabelLocs()`, the locations of the labels

The argument tuple is kind of transitional and I believe we hope to move away from it in the future, but the labels and label locations are also redundant. When a callee supports argument labels, dealing with them is primarily the responsibility of the callee. That means those labels and locations should already be present in a `DeclName` and `DeclNameLoc` somewhere beneath `getFn()`, so there's no need to have them in `CallExpr`.

`@dynamicCallable` is an exception. Because `@dynamicCallable` allows argument labels to be applied to arbitrary expressions, we cannot be certain that the callee contains an appropriate `DeclName` and `DeclNameLoc` for the argument label information, and we'd need to keep it in the `CallExpr`.

(This is all based on my understanding, which could be wrong.)

> [@dan-zheng](#):
>
> Can you share a use-case where it's necessary in a language to look up a "member whose name includes argument labels"?

If you mean a language whose bridge would require the argument labels to look up a member, the obvious answer is Smalltalk derivatives, whose selectors would most likely be bridged by mapping names like `insert(atIndex:)` to `insertAtIndex:`. Smalltalk derivatives are a notable use case on Apple platforms because Objective-C has a Smalltalk-derived design, and Mac apps have often used [Smalltalk-derived scripting languages](https://en.wikipedia.org/wiki/F-Script_(programming_language)) to match it.

It's also worth noting that if you wanted to write a lightweight "SwiftScript" designed to pair well with Swift, you would need this design. If your bridging features wouldn't really support a language very like your own, that seems like a red flag to me.

> [@dan-zheng](#):
>
> [I explored "regular callable" behavior](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/5) and I agree that it is more general.  
> However, for keyword-argument calls, it doesn't desugar argument labels like `@dynamicCallable` does. This is unnatural for language interop:
> 
> ```swift
> // With @dynamicCallable.
> np.random.randint(-10, 10, dtype: np.float)
> // With regular callable sugar. The empty strings are killer.
> np.random.randint(["": -10, "": 10, "dtype": np.float])
> 
> ```

I agree that this is awkward. The way I would handle it would be something like:

```swift
// Sugared
np.random.randint(-10, 10, dtype: np.float)
// After desugaring @dynamicMemberLookup, with regular callable sugar
np[dynamicMember: "random"]\
    [dynamicMember: "randint", argumentLabels: nil, nil, "dtype"]\
      (-10, 10, np.float)

```

This does mean you can't write:

```swift
let randint = np.random.randint
randint(-10, 10, dtype: np.float) // callables can't have argument labels

```

But you couldn't do that if `randint` were a Swift method, either. You'd have to write:

```swift
let randint = np.random.randint(_:_:dtype:)
randint(-10, 10, np.float)

```

Which would work on both a pure Swift type and a Python-bridged type.

**Edit to add:**

> [@dan-zheng](#):
>
> Perhaps the solution is as simple as not creating a new attribute `@dynamicMemberCallable` , but rather extending `@dynamicCallable` to handle the two new `dynamicallyCallMethod` methods using ambiguity resolution. Would this address your concern?

That would make the proposal cover more language designs, but then I would point out that it lacks coherence—it proposes four separate mechanisms to cover the four use cases we've thought of with nothing to really unify them. It's a brute-force solution to a problem that seems like it should have a more elegant one.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 16, 2018, 2:30am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/37 "2018-06-16T02:30:01Z")

</div>

> [@beccadax](#):
>
> The argument tuple is kind of transitional and I believe we hope to move away from it in the future, but the labels and label locations are also redundant. When a callee supports argument labels, dealing with them is primarily the responsibility of the callee. That means those labels and locations should already be present in a `DeclName` and `DeclNameLoc` somewhere beneath `getFn()` , so there's no need to have them in `CallExpr` .
> 
> `@dynamicCallable` is an exception. Because `@dynamicCallable` allows argument labels to be applied to arbitrary expressions, we cannot be certain that the callee contains an appropriate `DeclName` and `DeclNameLoc` for the argument label information, and we'd need to keep it in the `CallExpr` .

Thanks for the detailed explanation and your forward-facing thinking!  
I completely agree with your assessment of argument labels and that the current `CallExpr` representation stores them redundantly.

If `CallExpr` is changed so that argument labels are stored in the callee (rather than the argument), I feel that the current implementation of `@dynamicCallable` can adapt without significant changes.  
Argument-label information will still be available in the AST somewhere: all `@dynamicCallable` does is extract that information and create a new `CallExpr`.

* * *

More importantly, I agree about the importance of representing "methods whose name includes argument labels". As you said, this seems critical for supporting Smalltalk-derived languages and isn't supported in the current `@dynamicCallable` implementation at all.

Your idea of introducing a new member lookup method `subscript[dynamicMember:argumentsLabels:]` and implementing "regular callable behavior" seems to solve this problem.

It would look something like:

```swift
@callable // Enables "regular callable" sugar.
struct RubyObject {
  subscript(
    dynamicMember name: String, argumentLabels: [String?]
  ) -> ([RubyObject]) -> RubyObject {
    return { (arguments: [RubyObject]) in
      precondition(argumentLabels.count == arguments.count,
                   "Mismatch between arguments/labels")
      return ...
    }
  }
}

```

I like the spirit behind this solution, but I think it lacks polish. Some issues are:

1. I think the desugared "dynamic call" mechanism is better represented as a function, rather than a subscript method. The proposal mentions [using subscript methods](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md#alternatives-considered):

> It was suggested that we use subscripts to represent the call implementations instead of a function call, aligning with `@dynamicMemberLookup`. We think that functions are a better fit here: the reason `@dynamicMemberLookup` uses subscripts is to allow the members to be l-values, but call results are not l-values.

With a subscript method, there must be an extra check that no setter is defined, only a getter. This issue doesn't exist with `func dynamicallyCall` by definition. It'd look like:

```swift
struct RubyObject {
  func dynamicMethod(
    named name: String, argumentLabels: [String?]
  ) -> ([RubyObject]) -> RubyObject {
    return { (arguments: [RubyObject]) in
      precondition(argumentLabels.count == arguments.count,
                   "Mismatch between arguments/labels")
      return ...
    }
  }
}

```

1. Adding a layer of indirection between argument labels and arguments solves the problem of representing "methods whose name includes labels", but it adds complexity.

The dynamic call method is not as easily understandable as before. For many people, it won't be obvious why the method returns a closure or why it's necessary to pass argument labels and arguments separately (especially for Python, where it's not relevant for interop).

The equal number of arguments and argument labels is no longer enforced by the type system, so a runtime precondition check is necessary.

Direct calls to the argument-label-taking dynamic call method are now killer.  
Direct calls are necessary when you want to pass arguments as an `Array` or `Dictionary(Literal)` instead of variadic arguments. You then must write:

```swift
let print = Python.print
// Pretend I read some command-line arguments.
let arguments: [String : PythonObject] = ...

// Current @dynamicCallable implementation.
print.dynamicallyCall(withKeywordArguments: arguments)

// Oh dear. This is Python?
print.dynamicMethod(argumentLabels: arguments.keys)(arguments.values)

```

We can partially mitigate this problem by defining two versions of `dynamicMethod` corresponding to `withArguments:` and `withKeywordArguments:` so calls without keyword arguments don't need to specify `Array(repeating: nil, count: x)` for argument labels.

However, the additional complexity here is undeniable.  
Do you have ideas for simplifying things? I think it's great to continue iterating!

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 16, 2018, 6:19am UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/38 "2018-06-16T06:19:09Z")

</div>

As Review Manager, I'd like to remind the community that this thread is for review feedback on SE-0216, which is about dynamic freestanding calls. The design of a dynamic method call feature is not on-topic here except to the extent that it influences SE-0216. For example, if you believe that dynamic freestanding calls should be treated as a special case of dynamic member calls (taking the base method name as an optional value, maybe), that would be reasonable feedback to leave here. Otherwise, the development of a design for dynamic method calls should be carried out in its own pitch thread.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 16, 2018, 5:34pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/39 "2018-06-16T17:34:58Z")

</div>

An idea about `@dynamicCallable`:

Regarding the `dynamicallyCall(withKeywordArguments: D)` method, should the requirement for `D.Key` change to be an optional `ExpressibleByStringLiteral` type?

Effectively, this means that new function signatures would look like:

```swift
func dynamicallyCall(
  withKeywordArguments: DictionaryLiteral<String?, X>
) -> X

// One positional argument, one keyword argument.
x.dynamicallyCall(withKeywordArguments: [nil: arg1, "label": arg2])
// Problem: what does the empty string label mean?
x.dynamicallyCall(withKeywordArguments: ["": arg3])

```

The downside to using `Optional` for argument labels is that it creates a degenerate case, which is the empty string label. (If `nil` is the proper way to represent "no label", then `""` should not be allowed as a label. However, there's no way to enforce this within the type system anymore.)

Thus, I think the idea of using a non-optional type for labels is more robust.

---

<div class="post-metadata">

**Author:** ![dan-zheng](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dan-zheng/32/2534_2.png) [@dan-zheng](https://forums.swift.org/u/dan-zheng)\
**Post date:** [June 16, 2018, 5:45pm UTC](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615/40 "2018-06-16T17:45:47Z")

</div>

Here's another idea, which isn't core to the proposal.

Better fix-its should be provided when `@dynamicCallable` required methods are not satisfied.

I believe this is only relevant to Xcode and other tools that take advantage of "diagnostics editor mode".  
In Xcode, great fix-its are produced when protocol requirements are missing from a conforming type.

Users are given the ability to add protocol stubs:

 ![42%20AM](https://global.discourse-cdn.com/swift/original/2X/6/68cc32c18b73cbce8337a86e22459c4808644bb2.png)

Clicking the "Fix" button creates a stub with editor placeholders:  
 ![57%20AM](https://global.discourse-cdn.com/swift/original/2X/7/7193b1d76e4b5f69a3346af6ecf72206107813d7.png)

`@dynamicCallable` (and `@dynamicMemberLookup`) should also offer such fix-its: it's especially important because the attribute requirements aren't straightforward. (All that's necessary is to implement a function like [`printProtocolStubFixitString`](https://github.com/apple/swift/blob/27b450180b2c265f266f2569a6b5f45746e65072/lib/Sema/TypeCheckProtocol.cpp#L2350)).

One question is: what should the generated stubs look like?  
The `@dynamicCallable` method requirements are unlike typical protocol requirements.  
For example, in the `func dynamicallyCall(withArguments: A) -> X` method, `A` is required to conform to `ExpressibleByArrayLiteral` but is not required to be any specific type.

Any of the below are valid:

```swift
func dynamicallyCall(withArguments: [Any]) -> Any
func dynamicallyCall(withArguments: Set<AnyHashable>) -> Any
func dynamicallyCall<A : ExpressibleByArrayLiteral>(withArguments: A) -> Any

```

So what template should the generated stub follow?  
Edit: to be clear, the "holes" that need to be filled are the argument type, the return type, and possibly a generic signature.

[Previous page](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615.md?page=1)

[Next page](https://forums.swift.org/t/se-0216-user-defined-dynamically-callable-types/13615.md?page=3)
