> On Dec 3, 2017, at 8:41 AM, David Hart via swift-evolution 
> <swift-evolution@swift.org> wrote:
>> On 3 Dec 2017, at 04:11, Matthew Johnson via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> Sent from my iPad
>>> On Dec 2, 2017, at 7:40 PM, Chris Lattner <clatt...@nondot.org> wrote:
>>> On Dec 2, 2017, at 2:13 PM, Matthew Johnson <matt...@anandabits.com> wrote:
>>>>> For all those reasons, we really do need something like AnyObject 
>>>>> dispatch if we care about working with dynamically typed languages.  The 
>>>>> design I’m suggesting carefully cordons this off into its own struct 
>>>>> type, so it doesn’t infect the rest of the type system, and is 
>>>>> non-invasive in the compiler.
>>>> I am quite familiar with dynamic languages and agree that this is 
>>>> necessary if we are going to fully open up access to these languages from 
>>>> Swift.
>>> Ok, then it appears you agree that something like anyobject dispatch is 
>>> necessary for effective dynamic language interop.
>>>>>> I strongly urge you to reconsider the decision of that dynamic members 
>>>>>> must be made available with no indication at usage sites.  An indication 
>>>>>> of dynamic lookup at usage sites aligns very well (IMO) with the rest of 
>>>>>> Swift (AnyObject lookup aside) by calling attention to code that 
>>>>>> requires extra care to get right.
>>>>> I don’t understand this.  The proposal is fully type safe, and this 
>>>>> approach is completely precedented by AnyObject.  Swift’s type system 
>>>>> supports many ways to express fallibility, and keeping those decisions 
>>>>> orthogonal to this proposal is the right thing to do, because it allows 
>>>>> the author of the type to decide what model makes sense for them.
>>>> Allowing the author of the type to choose whether the mechanism is hidden 
>>>> or visible is exactly what I don’t want to allow.  I think you have the 
>>>> right design regarding types and semantics - the author chooses.  But I 
>>>> don’t want these calls to look like ordinary member lookup when I’m 
>>>> reading code.  
>>>> They inherently have a much greater chance of failure than ordinary member 
>>>> lookup.  Further, authors are likely to choose immediate traps or nil IUO 
>>>> as failure modes as forcing users to deal with Optional on every call is 
>>>> likely to be untenable.  I believe this behavior should be represented by 
>>>> some kind of syntax at the usage site.  I don’t believe it is an undue 
>>>> burden.  It would make the dynamic lookup semantic clear to all readers 
>>>> and would help to discourage abuse.
>>> I believe that adding explicit syntax would be counterproductive to your 
>>> goals, and would not make dynamic lookup syntax more clear.  I assume that 
>>> you would also want the same thing for DynamicCallable too, and operator 
>>> overloads, subscripts, and every other operation you perform on these 
>>> values, since they all have the exact same behavior.
>>> If we required some syntax even as minimal as “foo.^bar” and "baz^(42)”, 
>>> that change would turn this (which uses runtime failing or IUO return 
>>> values like AnyObject):
>>>     let np = Python.import("numpy")
>>>     let x = np.array([6, 7, 8])
>>>     let y =  np.arange(24).reshape(2, 3, 4)
>>>     let a = np.ones(3, dtype: np.int32)
>>>     let b = np.linspace(0, pi, 3)
>>>     let c = a+b
>>>     let d = np.exp(c)
>>>     print(d)
>>> into:
>>>     let np = Python.import("numpy")
>>>     let b = np^.array^([6, 7, 8])
>>>     let y =  np^.arange^(24)^.reshape^(2, 3, 4)
>>>     let a = np^.ones^(3, dtype: np^.int32)
>>>     let b = np^.linspace^(0, pi, 3)
>>>     let c = a+^b
>>>     let d = np^.exp^(c)
>>> This does not improve clarity of code, it merely serves to obfuscate logic. 
>>>  It is immediately apparent from the APIs being used, the API style, and 
>>> the static types (in Xcode or through static declarations) that this is all 
>>> Python stuff.  
>> It may be immediately apparent when the types involved are obviously 
>> dynamic, such as in this example where Python.import is explicitly used.  
>> However, my concern is less about the intended use case of dynamic language 
>> interop than I am that this feature will be generally available to all types 
>> in Swift.  
>> This is big change from AnyObject dispatch.  It opens up the dynamism to 
>> types and contexts that are not necessarily obviously using dynamic lookup, 
>> callable, etc.  Maybe this won’t turn out to be a problem in practice but I 
>> still think it’s a legitimate concern.
> If dynamism if restricted to subclasses of a DynamicObject type, like Xiaodi 
> suggested earlier, then we can protect ourselves from this dynamic dispatch 
> being generally available to all types in Swift.

I like the idea of a class type as the marker. 

If we did expose the protocols that DynamicObject conform to, then I would like 
to see an attribute that would be required at the declaration site. 

class MyTypo: SomeDynamicProtocol {...}

I also like the idea of having 
 @dynamic subscript(...)

I’d like the declaration site to be explicit. 

The calling site should just be dot syntax like AnyObject. 

>>> When you start mixing in use of native Swift types like dictionaries 
>>> (something we want to encourage because they are typed!) you end up with an 
>>> inconsistent mismash where people would just try adding syntax or applying 
>>> fixits continuously until the code builds.
>>> Beyond that, it is counterproductive to your goals, because it means that 
>>> people are far less likely to use to use optional returns.  Doing so (which 
>>> produces a safer result) would cause a double tax in syntax, and would be a 
>>> confusing jumble.  I can’t bring myself to do the whole example above, one 
>>> line - just converting member lookup syntax but not callable syntax - would 
>>> end up:
>>>     let y =  np^.arange?^(24)^.reshape^?(2, 3, 4)
>>> If you made DynamicCallable also return optional it would be:
>>>     let y =  np^.arange?^(24)?^.reshape^?(2, 3, 4)!
>>> or something.  This is such madness that no one would do that.
>> Yes, I agree.  The interaction with optional chaining makes it unworkable.  
>> I hadn’t thought that all the way through.  Thank you for indulging in the 
>> discussion about this idea.  
>> I’m uncertain what the right answer is.  I’m still not really comfortable 
>> with opening up dynamic lookup to any user-defined type without some way to 
>> indicate to readers that dynamic lookup is happening in a piece of code.  
>> Maybe there is a less localized annotation that would indicate dynamic 
>> lookup is in effect for a larger chunk of code.
> I think that making dynamic calls syntactically different to readers is going 
> too far in the direction of safety. Plus, it makes the language inconsistent 
> as we already have AnyObject dispatch with exactly the same syntax. But I 
> understand why you would want it if *any* type could end up being conformed 
> to a dynamic lookupable/callable protocol. Like said above, I think that a 
> DynamicObject type is enough protection to not bother making the syntax 
> heavier at the point of use.
>> One idea that hasn’t been explored yet is introducing a dynamic lookup 
>> effect.  That would provide clean syntax at the expression level while still 
>> making it clear that dynamic lookup is happening.  This probably isn’t the 
>> right approach as it would be viral and wouldn’t even indicate that the code 
>> in a specific function body even contains a dynamic lookup.  I mention it 
>> mostly to broaden the conversation and thought space about what kind of 
>> approach might address my concerns without cluttering up expressions.
>> Another, potentially more viable approach would be to use an approach 
>> similar to try and the proposed async of a statement modifier (possibly also 
>> allowed at the expression level as with try and async).  This could be used 
>> in conjunction with an effect as mentioned above but could also be used 
>> independently even though there isn’t a current precedent for that.  Your 
>> example written with this approach might be:
>>      let np = Python.import("numpy")
>>      let b = dynamic np.array([6, 7, 8])
>>      let y =  dynamic np.arange(24).reshape(2, 3, 4)
>>      let a = dynamic np.ones(3, dtype: np.int32)
>>      let b = dynamic np.linspace(0, pi, 3)
>>      let c = dynamic a + b
>>      let d = dynamic np.exp(c)
>> Note: `dynamic` is just a straw man here.  This is obviously a lot of 
>> boilerplate repetition of the same modifier.  I would also be perfectly 
>> happy allowing this annotation to apply to a block of code or even a whole 
>> function body.  Then it might be:
>> dynamic {
>>      let np = Python.import("numpy")
>>      let b = np.array([6, 7, 8])
>>      let y = np.arange(24).reshape(2, 3, 4)
>>      let a = np.ones(3, dtype: np.int32)
>>      let b = np.linspace(0, pi, 3)
>>      let c = a + b
>>      let d = np.exp(c)
>> }
>> The most obvious objection to this is that it introduces nesting and does so 
>> in a way that only very indirectly influences control flow (through the 
>> potential for dynamic failure).
>> My objective certainly isn’t to make code ugly and obfuscate logic.  It is 
>> simply to make the usage of dynamic features that are prone to failure at 
>> runtime immediately clear to a reader.  This should be as lightweight as 
>> possible while still providing valuable information to the reader.
>>>>> Swift already has a dynamic member lookup feature, "AnyObject dispatch" 
>>>>> which does not use additional punctuation, so this would break precedent.
>>>> I would prefer if dynamic lookup were visible with AnyObject as well.  For 
>>>> that reason I don’t believe it makes a good precedent to follow.  In fact, 
>>>> I would prefer to see us go the other direction and perhaps even consider 
>>>> revising dynamic lookup syntax for AnyObject in the future.
>>> This is definitely not going to happen.  The change Doug mentioned is to 
>>> have AnyObject lookup return optional instead of IUO, which forces ? on the 
>>> clients.  Adding other syntax (like you’re suggesting) is certainly not 
>>> going to happen.
>>> The entire point of AnyObject dispatch is to improve syntactic elegance and 
>>> clarity of code using it.  There is no other reason to exist.  Making code 
>>> that uses it syntactically onerous completely defeats the point of having 
>>> it in the first place, as I’ve mentioned before.
>> I agree.  The interaction with optional chaining is significantly more 
>> onerous than I had considered.  I should have worked through an example on 
>> my own.  I do hope you will consider a less localized approach to usage-site 
>> annotation though.
>>> Furthermore, your premise that Swift does not have invisibly failable 
>>> operations is plainly wrong.  Array subscript and even integer addition can 
>>> fail.  
>> I am well aware of these behaviors.  The difference IMO is that programmers 
>> tend to be well aware of these preconditions even if they also often choose 
>> to ignore them.  Dynamic lookup will not be so clear.  This is especially 
>> true if people use it with types that also have an API available through 
>> static lookup.
>>> Even the behavior of AnyObject was carefully designed and considered, and 
>>> were really really good reasons for it returning IUO.
>> I am not trying to call into question the choices made in the past.  Swift 
>> wouldn’t be the great language with a bright future that it is today without 
>> an incredibly successful migration of a large user base from Objective-C to 
>> Swift.  This is a huge accomplishment and couldn’t have happened without 
>> making really good decisions about some really hard tradeoffs.
>>> -Chris
>> _______________________________________________
>> 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

Reply via email to