> On Dec 3, 2017, at 8:41 AM, David Hart via swift-evolution
> <[email protected]> wrote:
>
>
>
>> On 3 Dec 2017, at 04:11, Matthew Johnson via swift-evolution
>> <[email protected]> wrote:
>>
>>
>>
>> Sent from my iPad
>>
>>> On Dec 2, 2017, at 7:40 PM, Chris Lattner <[email protected]> wrote:
>>>
>>> On Dec 2, 2017, at 2:13 PM, Matthew Johnson <[email protected]> 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.
@dynamic
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
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution