[swift-evolution] [Pitch] Introduce user-defined dynamically "callable" types
Joe Groff
jgroff at apple.com
Fri Nov 10 17:36:05 CST 2017
> On Nov 10, 2017, at 3:27 PM, Charles Srstka <cocoadev at charlessoft.com> wrote:
>
>> On Nov 10, 2017, at 2:57 PM, Joe Groff <jgroff at apple.com> wrote:
>>
>>> On Nov 10, 2017, at 12:37 PM, Charles Srstka <cocoadev at charlessoft.com> wrote:
>>>
>>>> On Nov 10, 2017, at 12:04 PM, Joe Groff via swift-evolution <swift-evolution at 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- at objc, @objc, and @objc dynamic calls?
>>
>> The language semantics aren't any different for non- at 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”)
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
More information about the swift-evolution
mailing list