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

Thanks for taking the time to respond. I think my confusion started from a
misinterpretation that each method you're exposing from python would need a
new implementation of the protocol. I've worked out my mistake, and you
helped clarified with:

The system I’ve sketched out does not incorporate Python declarations,

everything is dynamic

You've addressed the other things that didn't stem from that :)

Thanks,
Andrew

···

On Sun, Nov 12, 2017 at 12:27 PM, Chris Lattner <sabre@nondot.org> wrote:

On Nov 11, 2017, at 4:15 PM, Andrew Bennett <cacoyi@gmail.com> wrote:

HI, this proposal looks really interesting!

Thanks!

*Clarity on the proposal's intent*
*Nice cheap bridges, or lowering barriers to bridging?*

The later: I’m not overly concerned about constant time performance:
dynamic languages are often slow (though exceptions exist, e.g.
Javascript). So if there is a beautiful approach that works great but is a
bit slower, that is fine with me.

My desire is to enable access to “any” API in Python directly from Swift
with natural syntax. This doesn’t mean it has to match Python of course:
Python ranges and Swift ranges are syntactically very different, and
Swift-in-Python should use Swift syntax. That said, there are a LOT of
expression level similarities between Swift and Python, so things that can
be the same should be.

This proposal isn’t the only way to achieve that, of course. I’m open to
other suggestions.

I can see this providing a nice quick interface to Python from Swift, but
I'm not sure if the exposed interface will be very Swifty (you probably
have a much better idea of what is Swifty ;) than I do though). It seems
you want it to be possible for everything to be dynamically exposed

Right: Python APIs are not “Swifty”. They do not use optionals or
generics, use lowercase snake names, etc. IMO, that’s perfectly fine.
Unlike the Objective-C interop in Swift - which aims to make legacy ObjC
APIs feel Swifty, it is perfectly acceptable (and far less impact on the
compiler) to just import Python directly into Swift. This is actually much
better for the target audience anyway.

Is it common for the the argument labels in other languages to be open
ended, or are labels typically finite? If the answer is finite, why not use
a Swift method as the wrapper?

Python keyword arguments are open ended. They get passed to the Python
API as a dictionary.

Do you want duck typing, and would it be better to expose this via a
protocol?

I’m not sure how that can work, can you elaborate?

It seems like in almost every case you could do something like this:

func myMethod<X: PythonConvertible & CanQuack, Y: PythonConvertible>(a: X?
= nil, b: Y) {
    pythonBridge.call("myMethod", arguments: ["a": X, "b": Y])
}

The currency type is PyVal (name TBD of course), just like AnyObject for
Objective-C. I’m not sure what the code above is getting at.

It might be good to add some *use-cases* (a popular Python library
perhaps) to the proposal where this type of bridge would be insufficient :).

There are lots of examples. In data science, NumPy and Pandas are two
examples. The Python community is 1 or 2 orders of magnitude larger than
Swift’s community and there is 25 years of code out there to interop with.
The point of this is to make it all accessible.

It seems like this proposal pushes the responsibility of Swifty-ness and
type-safety to the caller.

Yes, welcome to dynamic languages :-)

At some point you'll have to write a type-safe bridging layer, or write
your entire program in non-Swifty code ("The most obvious way to write code
should also behave in a safe manner"). Is the main goal to *lower the
barrier to Python* and other dynamic languages? or is it to provide a
cheap nice Swifty bridge? I have the above concerns about the latter.

It’s the later. Many Python APIs are already wrappers around C code, so
if someone cares about investing a bunch of effort into making a great
Swift API, it would generally make sense to wrap the C API in Swift, not
wrap the Python API in Swift.

*Alternative sugar*

Ruby has Keyword Arguments for similar sugar:

*def* foo(regular, hash={})
    *puts* "the hash: #{hash}"

I'm sure you're aware of it, but I'll explain for completeness, any
trailing argument labels are stored and passed as a hash:

foo(regular, bar: "hello", bas: 123) *# outputs 'the hash: [**bar: "hello", bas: 123]’*

Python is similar, but allows them to be intermixed with positional
arguments.

Have you considered an alternative like this? For example:

func myMethod(regular: Int, store: @argcapture [String: PythonConvertible])
-> PythonConvertible

I'm sure you have good reasons, it might make the implementation bleed out
into other parts of the codebase. It would be good to include it in the
proposal *alternatives* section though. At the moment most of the
"alternatives" in the proposal just seem to be extensions to the same
solution :)

I’m not sure what you’re getting at here. The system I’ve sketched out
does not incorporate Python declarations, everything is dynamic. It is
extremely simple. This is a huge feature :-) I think it is entirely
non-scalable to do something like the ObjC importer for every language that
Swift wants to interop with.

*Clarity*
*Perhaps just that things are more clear to me now*

If my extrapolation is correct a user will implement a single type that
will allow a subset of a good portion of another language to be exposed
(object method and property bridging). I'm guessing that the dynamic member
proposal you're planning will not work with methods, it will require a
property, I think this helps explain some of the motivations. It might be
nice to have a more complete example that includes dynamic members. I
didn't find it clear from the proposal that it would only be necessary to
implement this protocol once per language.

I’m sorry, I don’t understand what you mean.

-Chris

It would only be ambiguous if a metatype conformed to the DynamicCallable protocol specifically. This can’t happen today, and if it happened, we could easily define disambiguation rules.

-Chris

···

On Nov 13, 2017, at 1:45 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Hello Chris, I have some questions about this passage:

Before this proposal, the Swift language has two types that participate in call syntax: functions and metatypes (for initialization). Neither of those may conform to protocols at the moment, so this introduces no possible ambiguity into the language.
Can you shortly describe why it would be ambiguous if metatypes would conform to protocols?

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already.

This isn’t particularly useful feedback. Can you elaborate more on what your concern is, and how calls are unlike anything else in Swift that would have this potential problem?

Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

Similarly, I’d love for you to elaborate on how the potential for abuse of this feature is different than anything else in Swift (e.g. operator overloading). Potential for abuse hasn’t been a particularly guiding force in the design on Swift, and for good reasons.

I also don’t understand what you mean by a static Callable protocol. I specifically address what I think you might mean in the “alternatives” part of the proposal, did you read that?

People have reasonably asked for the ability to make their own function-like types in the past, such that "myvalue(...)" behaves like sugar for "myvalue.call(...)" or something like that. In most cases, they still want to have type system control over what arguments and results their call operation produces. They don't really get that with this proposal; they lose all control over the arity and argument types. That may be what you want for calling into a dynamically-typed black hole, but it's not what you want in general. I fear that people would be willing to compromise their designs in order to get the sugar they want by contorting to fit this design.

I think a much better general solution to the problem of "make dynamic systems interact with type systems" is something like F#'s type providers which lets you write your own importers that look at dynamic information from a database, dynamic language VM, or some other system and generate type information usable by the compiler.

Thanks! I wasn’t aware of type providers, I’ll investigate them.

Integration at the importer level could let you produce more well-typed Swift declarations by looking at the runtime information you get by importing a Python module.

This is also addressed directly in the proposal. I don’t want to add Python specific support to Swift. The motivations are explained in the proposal.

I'm not suggesting a Python-specific feature, I'm suggesting that type providers could be a general framework for the kinds of problems you encounter trying to make dynamic systems work with Swift, including Python.

-Joe

···

On Nov 10, 2017, at 10:41 AM, Chris Lattner <sabre@nondot.org> wrote:
On Nov 10, 2017, at 10:03 AM, Joe Groff <jgroff@apple.com> wrote:

The language semantics aren't any different for non-@objc or @objc calls. The dispatch mechanism is an implementation detail. `dynamic` admits the possibility of late binding to change the method implementation dynamically, but doesn't change the type system behavior of the method, or radically change the implementation mechanism; it's still ultimately an indirect call, it doesn't turn your argument list into a dictionary that can be arbitrarily interpreted by user code.

-Joe

···

On Nov 10, 2017, at 12:37 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Nov 10, 2017, at 12:04 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already. Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

We already have that though, with the Objective-C bridge. How is the proposed behavior here more wildly different than the semantics of non-@objc, @objc, and @objc dynamic calls?

You sure about that? ;-)

MyObject.h:

import <Foundation/Foundation.h>

@interface MyObject : NSObject

@property (nonatomic, copy) void (^callback)(NSDictionary *);

@end

@interface MyObject (MyCategory)

- (void)foo:(NSString *)foo bar:(NSString *)bar;

@end

MyObject.m:

import "MyObject.h"

@implementation MyObject

- (void)baz:(NSString *)baz qux:(NSString *)qux {}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    if (selector == @selector(foo:bar:)) {
        return [super methodSignatureForSelector:@selector(baz:qux:)];
    } else {
        return [super methodSignatureForSelector:selector];
    }
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSString *name = NSStringFromSelector(invocation.selector);
    NSMutableArray *args = [NSMutableArray new];
    
    for (NSUInteger i = 2; i < invocation.methodSignature.numberOfArguments; i++) {
        __unsafe_unretained id obj = nil;
        
        [invocation getArgument:&obj atIndex:i];
        
        [args addObject:obj];
    }
    
    self.callback(@{ @"name" : name, @"arguments" : args });
}

@end

main.swift:

import Foundation

let obj = MyObject()

obj.callback = { dict in
    print("got this dictionary: \(dict as? [String : Any] ?? [:])")
}

obj.foo("Foo", bar: "Baz”)

Charles

···

On Nov 10, 2017, at 2:57 PM, Joe Groff <jgroff@apple.com> wrote:

On Nov 10, 2017, at 12:37 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Nov 10, 2017, at 12:04 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already. Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

We already have that though, with the Objective-C bridge. How is the proposed behavior here more wildly different than the semantics of non-@objc, @objc, and @objc dynamic calls?

The language semantics aren't any different for non-@objc or @objc calls. The dispatch mechanism is an implementation detail. `dynamic` admits the possibility of late binding to change the method implementation dynamically, but doesn't change the type system behavior of the method, or radically change the implementation mechanism; it's still ultimately an indirect call, it doesn't turn your argument list into a dictionary that can be arbitrarily interpreted by user code.

-Joe

It is. It is strictly sugar, as mentioned in the proposal.

All new code has a cost.

I do not expect any SIL or IRGen changes associated with this proposal, just type checker changes. The type checker changes should be straight-forward, but you can evaluate that when there is a patch.

Like I said, the type checker’s code for solving member constraints and applying the solution is already a rats-nest of special cases to deal with the rich semantics of method calls in Swift. We’ve been trying to simplify it over time and fix bugs, and adding new special cases is a step in the wrong direction.

Our goal is for Swift to be awesome, not comparable to “other statically typed languages”.

Ok, but being awesome is not the same as adding every possible feature to the language.

swift-evolution isn’t about priorities. I’m not asking someone else to implement this, and you don’t tell me how I spend my engineering time :-) :-)

Again, my point here is that implementing a new feature is just the first step. New code also has to be maintained going forward.

I’m open to requiring these to be the same type if there is a benefit to doing so. What benefit do you see?

Can you think of a case where they have to be different?

Slava

···

On Nov 10, 2017, at 6:01 PM, Chris Lattner <sabre@nondot.org> wrote:

This seems a really interesting solution Chris.
Similar to what Joe mentions I think this would also be appreciated by
the community to make even nicer DSLs in Swift, which may or may not
be a good side effect of the proposal.
Also, I'm just wondering, how much complication adds this to the
compiler itself that would have to be maintained in the future? I've
seen some threads lately about removing some "debt" with the
Objective-C interoperability, so I guess it would be nice to analyse
that also (not a compiler engineer here but I guess is a thing that
affects everyone so I'm just raising it :P)
And following up on Joe idea of type providers is that is something
that the web community has been using to interop JS with more typed
compile-to-JS langs, it also helps them give autocomplition on IDEs so
it may be worth investigating that.
Sorry for not having more direct feedback on the proposal. For what
can understand it looks reasonable and a good solution for the issue
in hand.
Cheers.

···

On Fri, Nov 10, 2017 at 6:41 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 10, 2017, at 10:03 AM, Joe Groff <jgroff@apple.com> wrote:

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already.

This isn’t particularly useful feedback. Can you elaborate more on what your concern is, and how calls are unlike anything else in Swift that would have this potential problem?

Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

Similarly, I’d love for you to elaborate on how the potential for abuse of this feature is different than anything else in Swift (e.g. operator overloading). Potential for abuse hasn’t been a particularly guiding force in the design on Swift, and for good reasons.

I also don’t understand what you mean by a static Callable protocol. I specifically address what I think you might mean in the “alternatives” part of the proposal, did you read that?

I think a much better general solution to the problem of "make dynamic systems interact with type systems" is something like F#'s type providers which lets you write your own importers that look at dynamic information from a database, dynamic language VM, or some other system and generate type information usable by the compiler.

Thanks! I wasn’t aware of type providers, I’ll investigate them.

Integration at the importer level could let you produce more well-typed Swift declarations by looking at the runtime information you get by importing a Python module.

This is also addressed directly in the proposal. I don’t want to add Python specific support to Swift. The motivations are explained in the proposal.

Do you have any actual feedback on the proposal?

-Chris

-Joe

On Nov 10, 2017, at 9:37 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello all,

I have a couple of proposals cooking in a quest to make Swift interoperate with dynamically typed languages like Python better. Instead of baking in hard coded support for one language or the other, I’m preferring to add a few small but general purpose capabilities to Swift. This is the first, which allows a Swift type to become “callable”.

The proposal is here:
https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d

I’ve also attached a snapshot below, but it will drift out of date as the proposal is refined. Feedback and thoughts are appreciated, thanks!

-Chris

Introduce user-defined dynamically "callable" types

     • Proposal: SE-NNNN
     • Author: Chris Lattner
     • Review Manager: TBD
     • Status: Awaiting implementation
Introduction

This proposal introduces a new DynamicCallable protocol to the standard library. Types that conform to it are "callable" with the function call syntax. It is simple syntactic sugar which allows the user to write:

   a = someValue(keyword1: 42, "foo", keyword2: 19)
and have it be interpreted by the compiler as:

a = someValue.dynamicCall(arguments
: [
   (
"keyword1", 42), ("", "foo"), ("keyword2", 19
)
])

Other languages have analogous features (e.g. Python "callables"), but the primary motivation of this proposal is to allow elegant and natural interoperation with dynamic languages in Swift.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Swift is well known for being exceptional at interworking with existing C and Objective-C APIs, but its support for calling APIs written in scripting langauges like Python, Perl, and Ruby is quite lacking. These languages provide an extremely dynamic programming model where almost everything is discovered at runtime.

Through the introduction of this proposal, and the related DynamicMemberLookupProtocol proposal, we seek to fix this problem. We believe we can make many common APIs feel very natural to use directly from Swift without all the complexity of implementing something like the Clang importer. For example, consider this Python code:

class Dog
:

def __init__(self, name
):

self.name =
name

self.tricks = # creates a new empty list for each dog

def add_trick(self, trick
):

self.tricks.append(trick)
we would like to be able to use this from Swift like this (the comments show the corresponding syntax you would use in Python):

// import DogModule
// import DogModule.Dog as Dog // an alternate
let Dog = Python.import(“DogModule.Dog")

// dog = Dog("Brianna")
let dog = Dog("Brianna")

// dog.add_trick("Roll over")
dog.add_trick("Roll over")

// dog2 = Dog("Kaylee").add_trick("snore")
let dog2 = Dog("Kaylee").add_trick("snore")
Of course, this would also apply to standard Python APIs as well. Here is an example working with the Python pickleAPI and the builtin Python function open:

// import pickle
let pickle = Python.import("pickle"
)

// file = open(filename)
let file = Python.open
(filename)

// blob = file.read()
let blob = file.read
()

// result = pickle.loads(blob)
let result = pickle.loads(blob)
This can all be expressed today as library functionality written in Swift, but without this proposal, the code required is unnecessarily verbose and gross. Without it (but with the related dynamic member lookup proposal) the code would have a method name (like call) all over the code:

// import pickle
let pickle = Python.import("pickle") // normal method in Swift, no change.

// file = open(filename)
let file = Python.open.call
(filename)

// blob = file.read()
let blob = file.read.call
()

// result = pickle.loads(blob)
let result = pickle.loads.call
(blob)

// dog2 = Dog("Kaylee").add_trick("snore")
let dog2 = Dog.call("Kaylee").add_trick.call("snore")
While this is a syntactic sugar proposal, we believe that this expands Swift to be usable in important new domains. This sort of capability is also highly precedented in other languages, and is a generally useful language feature that could be used for other purposes as well.

Proposed solution

We propose introducing this protocol to the standard library:

protocol DynamicCallable
{

associatedtype DynamicCallableArgument

associatedtype DynamicCallableResult

func dynamicCall(arguments: [(String, DynamicCallableArgument)]) throws ->
DynamicCallableResult
}

It also extends the language such that function call syntax - when applied to a value of DynamicCallable type - is accepted and transformed into a call to the dynamicCall member. The dynamicCall method takes a list of tuples: the first element is the keyword label (or an empty string if absent) and the second value is the formal parameter specified at the call site.

Before this proposal, the Swift language has two types that participate in call syntax: functions and metatypes (for initialization). Neither of those may conform to protocols at the moment, so this introduces no possible ambiguity into the language.

It is worth noting that this does not introduce the ability to provide dynamicly callable static/class members. We don't believe that this is important given the goal of supporting dynamic languages like Python, but if there is a usecase discovered in the future, it could be explored as future work. Such future work should keep in mind that call syntax on metatypes is already meaningful, and that ambiguity would have to be resolved somehow.

Discussion

While the signature for dynamicCall is highly general we expect the most common use will be clients who are programming against concrete types that implement this proposal. One very nice aspect of this is that, as a result of Swift's existing subtyping mechanics, implementations of this type can choose whether they can actually throw an error or not. For example, consider this silly implementation:

struct ParameterSummer : DynamicCallable
{

func dynamicCall(arguments: [(String, Int)]) -> Int
{

return arguments.reduce(0) { $0+$1
.1 }
}
}

let x = ParameterSummer
()

print(x(1, 7, 12)) // prints 20
Because ParameterSummer's implementation of dynamicCall does not throw, the call site is known not to throw either, so the print doesn't need to be marked with try.

Example Usage

A more realistic (and motivating) example comes from a prototype Python interop layer. While the concrete details of this use case are subject to change and not important for this proposal, it is perhaps useful to have a concrete example to see how this comes together.

That prototype currently has two types which model Python values, one of which handles Python exceptions and one of which does not. Their conformances would look like this, enabling the use cases described in the Motivation section above:

extension ThrowingPyRef: DynamicCallable
{

func dynamicCall(arguments: [(String, PythonConvertible)]) throws

->
PythonConvertible {

// Make sure state errors are not around.
   assert(PyErr_Occurred() == nil, "Python threw an error but wasn't handled"
)

// Count how many keyword arguments are in the list.
   let numKeywords = arguments.reduce(0
) {

$0 + ($1.0.isEmpty ? 0 : 1
)
   }

let kwdict = numKeywords != 0 ? PyDict_New() : nil

// Non-keyword arguments are passed as a tuple of values.
   let argTuple = PyTuple_New(arguments.count-numKeywords)!

var nonKeywordIndex = 0

for (keyword, argValue) in
arguments {

if keyword.isEmpty
{

PyTuple_SetItem(argTuple, nonKeywordIndex, argValue.toPython
())
       nonKeywordIndex
+= 1

     }
else
{

PyDict_SetItem(kwdict!, keyword.toPython(), argValue.toPython
())
     }
   }

// Python calls always return a non-null value when successful. If the
   // Python function produces the equivalent of C "void", it returns the None
   // value. A null result of PyObjectCall happens when there is an error,
   // like 'self' not being a Python callable.
   guard let resultPtr = PyObject_Call(state, argTuple, kwdict) else
{

throw PythonError.invalidCall(self
)
   }

let result = PyRef(owned
: resultPtr)

// Translate a Python exception into a Swift error if one was thrown.
   if let exception = PyErr_Occurred
() {

PyErr_Clear
()

throw PythonError.exception(PyRef(borrowed
: exception))
   }

return
result
}
}

extension PyRef: DynamicCallable
{

func dynamicCall(arguments: [(String
, PythonConvertible)])

->
PythonConvertible {

// Same as above, but internally aborts instead of throwing Swift
   // errors.
}
}

Source compatibility

This is a strictly additive proposal with no source breaking changes.

Effect on ABI stability

This is a strictly additive proposal with no ABI breaking changes.

Effect on API resilience

This has no impact on API resilience which is not already captured by other language features.

Alternatives considered

A few alternatives were considered:

Add ability to reject parameter labels

The implementation above does not allow an implementation to staticly reject argument labels. If this was important to add, we could add another protocol to model this, along the lines of:

/// A type conforming just to this protocol would not accept parameter
/// labels in its calls.
protocol DynamicCallable
{

associatedtype DynamicCallableArgument

associatedtype DynamicCallableResult

func dynamicCall(arguments: [DynamicCallableArgument]) throws ->
DynamicCallableResult
}

/// A type conforming to this protocol does allow optional parameter
/// labels.
protocol DynamicCallableWithKeywordsToo : DynamicCallable
{

func dynamicCall(arguments: [(String, DynamicCallableArgument)]) throws ->
DynamicCallableResult
}

This would allow a type to implement one or the other based on their capabilities. This proposal is going with a very simple design, but if there is demand for this, the author is happy to switch.

Staticly checking for exact signatures

This protocol does not allow a type to specify an exact signature for the callable - a specific number of parameters with specific types. If we went down that route, the best approach would be to introduce a new declaration kind (which would end up being very similar to get-only subscripts) since, in general, a type could want multiple concrete callable signatures, and those signatures should participate in overload resolution.

While such a feature could be interesting for some use cases, it is almost entirely orthogonal from this proposal: it addresses different use cases and does not solve the needs of this proposal. It does not address our needs because even a variadic callable declaration would not provide access to the keyword argument labels we need.

Direct language support for Python

We considered implementing something analogous to the Clang importer for Python, which would add a first class Python specific type(s) to Swift language or standard library. We rejected this option because it would be significantly more invasive in the compiler, would set the precedent for all other dynamic languages to get first class language support, and because that first class support doesn't substantially improve the experience of working with Python over existing Swift with a couple small "generally useful" extensions like this one.

Naming

The most fertile ground for bikeshedding is the naming of the protocol and the members. We welcome other ideas and suggestions for naming, but here are some thoughts on obvious options to consider:

We considered but rejected the name CustomCallable, because the existing Custom* protocols in the standard library (CustomStringConvertible, CustomReflectable, etc) provide a way to override and custom existing builtin abilities of Swift. In contrast, this feature grants a new capability to a type.

We considered but rejected a name like ExpressibleByCalling to fit with the ExpressibleBy* family of protocols (like ExpressibleByFloatLiteral, ExpressibleByStringLiteral, etc). This name family is specifically used by literal syntax, and calls are not literals. Additionally the type itself is not "expressible by calling" - instead, instances of the type may be called.

On member and associated type naming, we intentionally gave these long and verbose names so they stay out of the way of user code completion. The members of this protocol are really just compiler interoperability glue. If there was a Swift attribute to disable the members from showing up in code completion, we would use it (such an attribute would also be useful for the LiteralConvertible and other compiler magic protocols).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Alejandro Martinez

That'd be great, but Swift violates Gilad Bracha's golden rule by having static overloading, and throws bidirectional type inference on top, so our static name resolution can't be treated as a specialization of a dynamic name lookup mechanism in all cases.

-Joe

···

On Nov 11, 2017, at 4:21 AM, Marcel Weiher <marcel.weiher@gmail.com> wrote:

On Nov 10, 2017, at 19:04 , Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I don't like the idea of some calls having wildly different semantics from others;

The obvious solution then is to make this proposal the general case and current calls a specialization of that.

They're called Blocks. Objective C has them. They're objects. They're actually Objective C objects.

···

On Nov 10, 2017, at 10:26 AM, Florent Vilmart via swift-evolution <swift-evolution@swift.org> wrote:

Object that are functions too, Amazing! I wanted that in Javascript for a while!

On Nov 10, 2017, 1:04 PM -0500, Joe Groff via swift-evolution <swift-evolution@swift.org>, wrote:

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already. Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

I think a much better general solution to the problem of "make dynamic systems interact with type systems" is something like F#'s type providers which lets you write your own importers that look at dynamic information from a database, dynamic language VM, or some other system and generate type information usable by the compiler. Integration at the importer level could let you produce more well-typed Swift declarations by looking at the runtime information you get by importing a Python module.

-Joe

On Nov 10, 2017, at 9:37 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello all,

I have a couple of proposals cooking in a quest to make Swift interoperate with dynamically typed languages like Python better. Instead of baking in hard coded support for one language or the other, I’m preferring to add a few small but general purpose capabilities to Swift. This is the first, which allows a Swift type to become “callable”.

The proposal is here:
https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d

I’ve also attached a snapshot below, but it will drift out of date as the proposal is refined. Feedback and thoughts are appreciated, thanks!

-Chris

Introduce user-defined dynamically "callable" types

• Proposal: SE-NNNN
• Author: Chris Lattner
• Review Manager: TBD
• Status: Awaiting implementation
Introduction

This proposal introduces a new DynamicCallable protocol to the standard library. Types that conform to it are "callable" with the function call syntax. It is simple syntactic sugar which allows the user to write:

a = someValue(keyword1: 42, "foo", keyword2: 19)
and have it be interpreted by the compiler as:

a = someValue.dynamicCall(arguments
: [
(
"keyword1", 42), ("", "foo"), ("keyword2", 19
)
])

Other languages have analogous features (e.g. Python "callables"), but the primary motivation of this proposal is to allow elegant and natural interoperation with dynamic languages in Swift.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Swift is well known for being exceptional at interworking with existing C and Objective-C APIs, but its support for calling APIs written in scripting langauges like Python, Perl, and Ruby is quite lacking. These languages provide an extremely dynamic programming model where almost everything is discovered at runtime.

Through the introduction of this proposal, and the related DynamicMemberLookupProtocol proposal, we seek to fix this problem. We believe we can make many common APIs feel very natural to use directly from Swift without all the complexity of implementing something like the Clang importer. For example, consider this Python code:

class Dog
:

def __init__(self, name
):

self.name =
name

self.tricks = # creates a new empty list for each dog

def add_trick(self, trick
):

self.tricks.append(trick)
we would like to be able to use this from Swift like this (the comments show the corresponding syntax you would use in Python):

// import DogModule
// import DogModule.Dog as Dog // an alternate
let Dog = Python.import(“DogModule.Dog")

// dog = Dog("Brianna")
let dog = Dog("Brianna")

// dog.add_trick("Roll over")
dog.add_trick("Roll over")

// dog2 = Dog("Kaylee").add_trick("snore")
let dog2 = Dog("Kaylee").add_trick("snore")
Of course, this would also apply to standard Python APIs as well. Here is an example working with the Python pickleAPI and the builtin Python function open:

// import pickle
let pickle = Python.import("pickle"
)

// file = open(filename)
let file = Python.open
(filename)

// blob = file.read()
let blob = file.read
()

// result = pickle.loads(blob)
let result = pickle.loads(blob)
This can all be expressed today as library functionality written in Swift, but without this proposal, the code required is unnecessarily verbose and gross. Without it (but with the related dynamic member lookup proposal) the code would have a method name (like call) all over the code:

// import pickle
let pickle = Python.import("pickle") // normal method in Swift, no change.

// file = open(filename)
let file = Python.open.call
(filename)

// blob = file.read()
let blob = file.read.call
()

// result = pickle.loads(blob)
let result = pickle.loads.call
(blob)

// dog2 = Dog("Kaylee").add_trick("snore")
let dog2 = Dog.call("Kaylee").add_trick.call("snore")
While this is a syntactic sugar proposal, we believe that this expands Swift to be usable in important new domains. This sort of capability is also highly precedented in other languages, and is a generally useful language feature that could be used for other purposes as well.

Proposed solution

We propose introducing this protocol to the standard library:

protocol DynamicCallable
{

associatedtype DynamicCallableArgument

associatedtype DynamicCallableResult

func dynamicCall(arguments: [(String, DynamicCallableArgument)]) throws -
DynamicCallableResult
}

It also extends the language such that function call syntax - when applied to a value of DynamicCallable type - is accepted and transformed into a call to the dynamicCall member. The dynamicCall method takes a list of tuples: the first element is the keyword label (or an empty string if absent) and the second value is the formal parameter specified at the call site.

Before this proposal, the Swift language has two types that participate in call syntax: functions and metatypes (for initialization). Neither of those may conform to protocols at the moment, so this introduces no possible ambiguity into the language.

It is worth noting that this does not introduce the ability to provide dynamicly callable static/class members. We don't believe that this is important given the goal of supporting dynamic languages like Python, but if there is a usecase discovered in the future, it could be explored as future work. Such future work should keep in mind that call syntax on metatypes is already meaningful, and that ambiguity would have to be resolved somehow.

Discussion

While the signature for dynamicCall is highly general we expect the most common use will be clients who are programming against concrete types that implement this proposal. One very nice aspect of this is that, as a result of Swift's existing subtyping mechanics, implementations of this type can choose whether they can actually throw an error or not. For example, consider this silly implementation:

struct ParameterSummer : DynamicCallable
{

func dynamicCall(arguments: [(String, Int)]) -> Int
{

return arguments.reduce(0) { $0+$1
.1 }
}
}

let x = ParameterSummer
()

print(x(1, 7, 12)) // prints 20
Because ParameterSummer's implementation of dynamicCall does not throw, the call site is known not to throw either, so the print doesn't need to be marked with try.

Example Usage

A more realistic (and motivating) example comes from a prototype Python interop layer. While the concrete details of this use case are subject to change and not important for this proposal, it is perhaps useful to have a concrete example to see how this comes together.

That prototype currently has two types which model Python values, one of which handles Python exceptions and one of which does not. Their conformances would look like this, enabling the use cases described in the Motivation section above:

extension ThrowingPyRef: DynamicCallable
{

func dynamicCall(arguments: [(String, PythonConvertible)]) throws

-
PythonConvertible {

// Make sure state errors are not around.
assert(PyErr_Occurred() == nil, "Python threw an error but wasn't handled"
)

// Count how many keyword arguments are in the list.
let numKeywords = arguments.reduce(0
) {

$0 + ($1.0.isEmpty ? 0 : 1
)
}

let kwdict = numKeywords != 0 ? PyDict_New() : nil

// Non-keyword arguments are passed as a tuple of values.
let argTuple = PyTuple_New(arguments.count-numKeywords)!

var nonKeywordIndex = 0

for (keyword, argValue) in
arguments {

if keyword.isEmpty
{

PyTuple_SetItem(argTuple, nonKeywordIndex, argValue.toPython
())
nonKeywordIndex
+= 1

}
else
{

PyDict_SetItem(kwdict!, keyword.toPython(), argValue.toPython
())
}
}

// Python calls always return a non-null value when successful. If the
// Python function produces the equivalent of C "void", it returns the None
// value. A null result of PyObjectCall happens when there is an error,
// like 'self' not being a Python callable.
guard let resultPtr = PyObject_Call(state, argTuple, kwdict) else
{

throw PythonError.invalidCall(self
)
}

let result = PyRef(owned
: resultPtr)

// Translate a Python exception into a Swift error if one was thrown.
if let exception = PyErr_Occurred
() {

PyErr_Clear
()

throw PythonError.exception(PyRef(borrowed
: exception))
}

return
result
}
}

extension PyRef: DynamicCallable
{

func dynamicCall(arguments: [(String
, PythonConvertible)])

-
PythonConvertible {

// Same as above, but internally aborts instead of throwing Swift
// errors.
}
}

Source compatibility

This is a strictly additive proposal with no source breaking changes.

Effect on ABI stability

This is a strictly additive proposal with no ABI breaking changes.

Effect on API resilience

This has no impact on API resilience which is not already captured by other language features.

Alternatives considered

A few alternatives were considered:

Add ability to reject parameter labels

The implementation above does not allow an implementation to staticly reject argument labels. If this was important to add, we could add another protocol to model this, along the lines of:

/// A type conforming just to this protocol would not accept parameter
/// labels in its calls.
protocol DynamicCallable
{

associatedtype DynamicCallableArgument

associatedtype DynamicCallableResult

func dynamicCall(arguments: [DynamicCallableArgument]) throws -
DynamicCallableResult
}

/// A type conforming to this protocol does allow optional parameter
/// labels.
protocol DynamicCallableWithKeywordsToo : DynamicCallable
{

func dynamicCall(arguments: [(String, DynamicCallableArgument)]) throws -
DynamicCallableResult
}

This would allow a type to implement one or the other based on their capabilities. This proposal is going with a very simple design, but if there is demand for this, the author is happy to switch.

Staticly checking for exact signatures

This protocol does not allow a type to specify an exact signature for the callable - a specific number of parameters with specific types. If we went down that route, the best approach would be to introduce a new declaration kind (which would end up being very similar to get-only subscripts) since, in general, a type could want multiple concrete callable signatures, and those signatures should participate in overload resolution.

While such a feature could be interesting for some use cases, it is almost entirely orthogonal from this proposal: it addresses different use cases and does not solve the needs of this proposal. It does not address our needs because even a variadic callable declaration would not provide access to the keyword argument labels we need.

Direct language support for Python

We considered implementing something analogous to the Clang importer for Python, which would add a first class Python specific type(s) to Swift language or standard library. We rejected this option because it would be significantly more invasive in the compiler, would set the precedent for all other dynamic languages to get first class language support, and because that first class support doesn't substantially improve the experience of working with Python over existing Swift with a couple small "generally useful" extensions like this one.

Naming

The most fertile ground for bikeshedding is the naming of the protocol and the members. We welcome other ideas and suggestions for naming, but here are some thoughts on obvious options to consider:

We considered but rejected the name CustomCallable, because the existing Custom* protocols in the standard library (CustomStringConvertible, CustomReflectable, etc) provide a way to override and custom existing builtin abilities of Swift. In contrast, this feature grants a new capability to a type.

We considered but rejected a name like ExpressibleByCalling to fit with the ExpressibleBy* family of protocols (like ExpressibleByFloatLiteral, ExpressibleByStringLiteral, etc). This name family is specifically used by literal syntax, and calls are not literals. Additionally the type itself is not "expressible by calling" - instead, instances of the type may be called.

On member and associated type naming, we intentionally gave these long and verbose names so they stay out of the way of user code completion. The members of this protocol are really just compiler interoperability glue. If there was a Swift attribute to disable the members from showing up in code completion, we would use it (such an attribute would also be useful for the LiteralConvertible and other compiler magic protocols).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I didn't know of Gilad Bracha, so you made me curious.

I guess that the "golden rule" you refer to is here, for anyone curious: Room 101: Systemic Overload

Gwendal Roué

···

Le 11 nov. 2017 à 16:48, Joe Groff via swift-evolution <swift-evolution@swift.org> a écrit :

That'd be great, but Swift violates Gilad Bracha's golden rule by having static overloading, and throws bidirectional type inference on top, so our static name resolution can't be treated as a specialization of a dynamic name lookup mechanism in all cases.

1 Like

This would be an error. It is perfectly fine to do something like this:

  func foo(a : PyRef) -> PyRef {
     let b = a.foo // produces a PyRef
     let c = b(“string”) // produces a PyRef
     return c
  }

The idea of this proposal is that in Python “everything is a python object”, but that some of the objects are callable. This approach is modeling a python object with some type (PyRef in the example above). It is totally possible to produce sugar on top of that, or to go farther. For example, with no language extensions, it is possible to do:

  let closure: (String) -> Int = { arg in Int(pythonThing.call(args: arg))! }

Or with this proposal, you could do something like:

  let closure: (String) -> Int = { arg in Int(pythonThing(arg))! }

-Chris

···

On Nov 12, 2017, at 3:33 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

Am 11.11.2017 um 19:03 schrieb Chris Lattner <sabre@nondot.org <mailto:sabre@nondot.org>>:

Swift is quite flexible on what can act as a closure — would it be allowed to use a dynamic callable in that context?
I guess forwarding of

let closure: ([(String, Int)]) -> Int = DynamicCallableType()

to the dynamicCall method of DynamicCallableType isn’t that hard, but wouldn’t it be odd if the value of closure changes when you leave out its type?

I’m not sure I understand what you’re getting at. Can you show how this would work with the example in the motivation section?

I’m just asking if this should work ;-) — I didn’t see anything regarding it in the proposal.
But to take something from the document:

  // file = open(filename)
  let file = Python.open.call(filename)
let openFile: (String) -> FileHandle? = Python.open // is this be possible, or an error?

Hello Chris, I have some questions about this passage:

Before this proposal, the Swift language has two types that participate in call syntax: functions and metatypes (for initialization). Neither of those may conform to protocols at the moment, so this introduces no possible ambiguity into the language.
Can you shortly describe why it would be ambiguous if metatypes would conform to protocols?

It would only be ambiguous if a metatype conformed to the DynamicCallable protocol specifically. This can’t happen today, and if it happened, we could easily define disambiguation rules.

FWIW it looks like we don’t allow calling a value of metatype type as of Swift 3 — you have to add ‘.init’:

t.swift:6:4: error: initializing from a metatype value must reference 'init' explicitly
  m()
   ^
   .init

Slava

···

On Nov 13, 2017, at 10:27 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 13, 2017, at 1:45 AM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

As I mentioned, this is directly addressed in the writeup. Here’s the link:
https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d#staticly-checking-for-exact-signatures

-Chris

···

On Nov 10, 2017, at 10:51 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

Similarly, I’d love for you to elaborate on how the potential for abuse of this feature is different than anything else in Swift (e.g. operator overloading). Potential for abuse hasn’t been a particularly guiding force in the design on Swift, and for good reasons.

I also don’t understand what you mean by a static Callable protocol. I specifically address what I think you might mean in the “alternatives” part of the proposal, did you read that?

People have reasonably asked for the ability to make their own function-like types in the past, such that "myvalue(...)" behaves like sugar for "myvalue.call(...)" or something like that. In most cases, they still want to have type system control over what arguments and results their call operation produces. They don't really get that with this proposal; they lose all control over the arity and argument types.

This is a very localized and simple change to the compiler. Assuming the pitch process goes well, I will provide an implementation.

-Chris

···

On Nov 10, 2017, at 10:57 AM, Alejandro Martinez via swift-evolution <swift-evolution@swift.org> wrote:

This seems a really interesting solution Chris.
Similar to what Joe mentions I think this would also be appreciated by
the community to make even nicer DSLs in Swift, which may or may not
be a good side effect of the proposal.
Also, I'm just wondering, how much complication adds this to the
compiler itself that would have to be maintained in the future?

I don't think it's that localized. It's going to make call resolution massively more complicated. Through great pain and community anguish, we pushed ourselves to a model where argument labels are parts of the declaration name, not part of the call argument, and this would throw us straight back into the kinds of complications of dealing with overloaded name- and type-dependent behavior we're trying to get away from in the language.

Swift is a dynamic language too, and tuples have enough metadata in them that you could get almost everything you want with an infix operator. Mirror doesn't expose the reflection information particularly elegantly, but you can do something like this to decompose a Swift tuple into positional and keyword arguments to fit the Python model:

infix operator ∫ // a "snake". get it

protocol Pythonic {
  func call(positionalArgs: [Pythonic], kwArgs: [(String, Pythonic)]) -> Pythonic
}

func ∫(function: Pythonic, args: Any) -> Pythonic {
  var positionalArgs: [Pythonic] =
  var kwArgs: [(String, Pythonic)] =
  
  let argMirror = Mirror(reflecting: args)
  
  if argMirror.displayStyle == .tuple {
    // Tuple argument
    for child in argMirror.children {
      if let label = child.label, let first = label.first, first != "." {
        kwArgs.append((label, child.value as! Pythonic))
      } else {
        positionalArgs.append(child.value as! Pythonic)
      }
    }
  } else {
    // One argument
    positionalArgs = [args as! Pythonic]
  }
  
  return function.call(positionalArgs: positionalArgs, kwArgs: kwArgs)
}

x∫(0, "foo", bar: "bas")

-Joe

···

On Nov 10, 2017, at 11:20 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 10, 2017, at 10:57 AM, Alejandro Martinez via swift-evolution <swift-evolution@swift.org> wrote:

This seems a really interesting solution Chris.
Similar to what Joe mentions I think this would also be appreciated by
the community to make even nicer DSLs in Swift, which may or may not
be a good side effect of the proposal.
Also, I'm just wondering, how much complication adds this to the
compiler itself that would have to be maintained in the future?

This is a very localized and simple change to the compiler. Assuming the pitch process goes well, I will provide an implementation.

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already.

This isn’t particularly useful feedback. Can you elaborate more on what your concern is, and how calls are unlike anything else in Swift that would have this potential problem?

Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

Similarly, I’d love for you to elaborate on how the potential for abuse of this feature is different than anything else in Swift (e.g. operator overloading). Potential for abuse hasn’t been a particularly guiding force in the design on Swift, and for good reasons.

I also don’t understand what you mean by a static Callable protocol. I specifically address what I think you might mean in the “alternatives” part of the proposal, did you read that?

People have reasonably asked for the ability to make their own function-like types in the past, such that "myvalue(...)" behaves like sugar for "myvalue.call(...)" or something like that. In most cases, they still want to have type system control over what arguments and results their call operation produces. They don't really get that with this proposal; they lose all control over the arity and argument types. That may be what you want for calling into a dynamically-typed black hole, but it's not what you want in general. I fear that people would be willing to compromise their designs in order to get the sugar they want by contorting to fit this design.

+1. This exact concern is the first thing that came to mind while reading this proposal.

The general problem of interop with dynamic languages is an important one but a solution shouldn’t come at the cost of introducing syntactic sugar that is sub-optimal for native Swift code.

···

Sent from my iPad

On Nov 10, 2017, at 12:51 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 10, 2017, at 10:41 AM, Chris Lattner <sabre@nondot.org> wrote:
On Nov 10, 2017, at 10:03 AM, Joe Groff <jgroff@apple.com> wrote:

I think a much better general solution to the problem of "make dynamic systems interact with type systems" is something like F#'s type providers which lets you write your own importers that look at dynamic information from a database, dynamic language VM, or some other system and generate type information usable by the compiler.

Thanks! I wasn’t aware of type providers, I’ll investigate them.

Integration at the importer level could let you produce more well-typed Swift declarations by looking at the runtime information you get by importing a Python module.

This is also addressed directly in the proposal. I don’t want to add Python specific support to Swift. The motivations are explained in the proposal.

I'm not suggesting a Python-specific feature, I'm suggesting that type providers could be a general framework for the kinds of problems you encounter trying to make dynamic systems work with Swift, including Python.

-Joe

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

How `MyObject.foo(_:bar:)` gets implemented is its own business, as far as the compiler is concerned. The compile-time name resolution for the method isn't impacted.

-Joe

···

On Nov 10, 2017, at 3:27 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Nov 10, 2017, at 2:57 PM, Joe Groff <jgroff@apple.com> wrote:

On Nov 10, 2017, at 12:37 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Nov 10, 2017, at 12:04 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I don't like the idea of some calls having wildly different semantics from others; it's difficult enough to tell what exactly a call might be doing already. Since we also lack the more obvious static "Callable" protocol idea to give even well-typed call syntax to user-defined types, this also seems like it'd be easily abused for that purpose too.

We already have that though, with the Objective-C bridge. How is the proposed behavior here more wildly different than the semantics of non-@objc, @objc, and @objc dynamic calls?

The language semantics aren't any different for non-@objc or @objc calls. The dispatch mechanism is an implementation detail. `dynamic` admits the possibility of late binding to change the method implementation dynamically, but doesn't change the type system behavior of the method, or radically change the implementation mechanism; it's still ultimately an indirect call, it doesn't turn your argument list into a dictionary that can be arbitrarily interpreted by user code.

-Joe

You sure about that? ;-)

MyObject.h:

import <Foundation/Foundation.h>

@interface MyObject : NSObject

@property (nonatomic, copy) void (^callback)(NSDictionary *);

@end

@interface MyObject (MyCategory)

- (void)foo:(NSString *)foo bar:(NSString *)bar;

@end

MyObject.m:

import "MyObject.h"

@implementation MyObject

- (void)baz:(NSString *)baz qux:(NSString *)qux {}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    if (selector == @selector(foo:bar:)) {
        return [super methodSignatureForSelector:@selector(baz:qux:)];
    } else {
        return [super methodSignatureForSelector:selector];
    }
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSString *name = NSStringFromSelector(invocation.selector);
    NSMutableArray *args = [NSMutableArray new];
    
    for (NSUInteger i = 2; i < invocation.methodSignature.numberOfArguments; i++) {
        __unsafe_unretained id obj = nil;
        
        [invocation getArgument:&obj atIndex:i];
        
        [args addObject:obj];
    }
    
    self.callback(@{ @"name" : name, @"arguments" : args });
}

@end

main.swift:

import Foundation

let obj = MyObject()

obj.callback = { dict in
    print("got this dictionary: \(dict as? [String : Any] ?? [:])")
}

obj.foo("Foo", bar: "Baz”)

This seems a really interesting solution Chris.
Similar to what Joe mentions I think this would also be appreciated by
the community to make even nicer DSLs in Swift, which may or may not
be a good side effect of the proposal.
Also, I'm just wondering, how much complication adds this to the
compiler itself that would have to be maintained in the future?

This is a very localized and simple change to the compiler. Assuming the pitch process goes well, I will provide an implementation.

I don't think it's that localized. It's going to make call resolution massively more complicated. Through great pain and community anguish, we pushed ourselves to a model where argument labels are parts of the declaration name, not part of the call argument, and this would throw us straight back into the kinds of complications of dealing with overloaded name- and type-dependent behavior we're trying to get away from in the language.

I’m pretty sure it doesn’t, but good news: since Swift requires an implementation before the proposal can be officially reviewed, you will be able to look at the actual patch to make this decision.

Swift is a dynamic language too, and tuples have enough metadata in them that you could get almost everything you want with an infix operator.

This is really gross. The goal here is to make Python feel natural. If you oppose this proposal then we’ll have to find some other way to provide an elegant experience:

x∫(0, "foo", bar: "bas”)

This is not going to be acceptable to users.

-Chris

···

On Nov 10, 2017, at 11:42 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 10, 2017, at 10:57 AM, Alejandro Martinez via swift-evolution <swift-evolution@swift.org> wrote:

The compile-time name resolution for the method doesn’t happen *at all.* If the method is dynamic, it’s resolved at runtime. The method may or may not even exist; all you know is that the object address, the method name, and a list of the arguments will be passed to a function called objc_msgSend(), which conceptually is remarkably similar to Chris’s dynamicCall().

Charles

···

On Nov 10, 2017, at 5:36 PM, Joe Groff <jgroff@apple.com> wrote:

How `MyObject.foo(_:bar:)` gets implemented is its own business, as far as the compiler is concerned. The compile-time name resolution for the method isn't impacted.

-Joe

Through great pain and community anguish, we pushed ourselves to a model where argument labels are parts of the declaration name, not part of the call argument

Hey Joe, does this mean it is likely there is little to no chance to have argument labels for closures and stored functions back? Sorry to bring noise in here, but your words put a grim outlook on something the core team said it would be revisited last year in the next Swift iteration.

···

Sent from my iPhone

On 10 Nov 2017, at 19:42, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote: