[swift-evolution] Proposal: Introduce User-defined "Dynamic Member Lookup" Types

Xiaodi Wu xiaodi.wu at gmail.com
Sun Dec 3 16:44:49 CST 2017


On Sun, Dec 3, 2017 at 2:16 PM, Matthew Johnson via swift-evolution <
swift-evolution at swift.org> wrote:

>
> On Dec 3, 2017, at 11:36 AM, Chris Lattner <clattner at nondot.org> wrote:
>
> On Dec 2, 2017, at 7:11 PM, Matthew Johnson <matthew at anandabits.com>
> wrote:
>
>
> 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.
>
>
> Sure, it is a legit concern, but it is also nothing new.  This is the
> standard concern with type inference.
>
>
> The concern for me is orthogonal to type inference.  The name of a type
> supporting dynamic lookup will not necessarily provide any indication that
> the type supports dynamic lookup.
>
>
> While there are weird cases, in practice, values do not get magicked out
> of no-where.  They most commonly are either root values like:
>
> let np = Python.import(“foo”)
> let pyInt = PyVal(42)
>
> or they come for parameters:
>
> func f(x : PyVal) {
>
> The place that is potentially surprising is when the type gets buried
> because you’re working with some API that returns a [String, PyVal]
> dictionary or something:
>
>
> let x = foo()[“someKey”]
>
> and you don’t realize that PyVal’s are involved.  However, if you are
> actively writing the code, you have access to code completion and other
> things that tell you these types, and if it is important for the clarity of
> the code, you write this instead:
>
> let x :PyVal = foo()[“someKey”]
>
> There is nothing specific to this proposal about this issue.
>
>
> See above.  In the case of PyVal specifically the concern is somewhat
> mitigated by the name of the type.  That won’t necessarily always be the
> case.
>

If that's the concern, then it would be pretty straightforward to restrict
dynamic protocols for stdlib internal use only and expose only PyVal. The
trade-off is that all such bridging code would have to be shipped with
Swift itself.


> 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.
>
>
> You seem to be extremely concerned that people will adopt
> DynamicMemberLookup for types where it doesn’t make sense and abuse the
> feature.  I am having a real problem understanding what your concern is, so
> I’d really love for you to explain some theoretical examples of the bad
> thing that happens: why someone (non-maliciously) adopts the protocol, what
> code gets written, and what harm actually comes from it.
>
> Let me use a made up tale from a parallel universe to illustrate why I
> don’t understand your concern.  Imagine if Swift didn’t already
> interoperate with C, and did not already have IUOs.  Someone who cared
> about C language interoperability would quickly realize that the ergonomics
> of importing everything as strong optionals is a non-starter, jeopardizing
> the usability of C interop, and would propose IUOs as a feature.
>
> We’d then have a long and drawn out conversation about the various options
> on how to model this, the pros and cons of each, and would settle on IUO as
> the least bad design (as an aside, in our universe, when we went through
> the design process that led to IUOs, this is exactly what happened, we even
> considered syntaxing them as interobangs :-).
>
> At that point, there would be a general uproar because IUOs have high
> potential for abuse: Swift is “all about” strong types and safety, which
> IUOs undermine.  Strong optionals are considered a pain to use by some
> people and widely misunderstood (I think they are the biggest challenge in
> learning Swift in practice), and so it is a reasonable feature that people
> could pervasively adopt IUOs, leading to a much worse world all around.
>
>
> This made up parallel universe is exactly analogous to what is happening
> now.  DynamicMemberLookup is no more dangerous and harmful than IUOs are.
> They will be one more tool in the toolbox.  While it is possible that
> someone will abuse it, this will not be widespread.  People who are
> particularly worried will build a single new rule into their linters (which
> already flag uses of x!), and the world will keep revolving.
>
>
> There is an important difference between IUOs and dynamic lookup in my
> mind.  In the case of dynamic lookup a runtime value (the member name) is
> masquerading as something that is statically verified everywhere in Swift
> *except* in the case of dynamic lookup.  I would strongly prefer to not
> need to consider the possibility that a member name is actually a runtime
> value outside of contexts that make this possibility known to me when I am
> reading code.  Spelling a member name right is a precondition that is
> trivial to violate both by accident and by code evolution.
>
> That is also why I am not concerned about the potential for failure of the
> longhand way of writing this: x.get(“foo”).get(“bar”).  It is clear that a
> runtime value (which may violate a precondition) is being provided.  That
> is not at all the case when the dynamic lookup syntax mirrors static lookup
> syntax with no contextual hint that it may be in effect.
>
> An example of the kind of thing I am concerned about is people using
> dynamic lookup to avoid more structured forms of polymorphism.  I can
> imagine this happening for a number of reasons that are not intentionally
> abusive or malicious.
>
> Some people are big fans of dynamic behavior and this feature will make it
> much easier to write code in that style.  They will do it without feeling
> malicious or considering this to be abusive, considering it to be a
> legitimate style preference.  I wouldn’t be surprised to see people develop
> mixins that implement the subscript using mirror and other future
> reflection capabilities without considering that to be abusive (I would
> almost be surprised if this *didn’t* happen).  Requiring some kind of
> usage site annotation would both discourage this and help anyone who walks
> into a such a code base to understand what is going on.
>
>
>
> 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.
>
>
> You miss my point.  My point is that AnyObject lookup was carefully
> considered, has stood the test of time, and is the *right* answer.  Swift 1
> would not have been nearly as successful without it.
>
>
> I don’t think I do.  I was trying to agree with exactly the point that it
> was the right answer in the early days of Swift and getting it right then
> was essential to Swift’s success.
>
> Aside from the historical necessity of AnyObject, it is also a very
> specific and widely know type that doesn’t have *any* statically
> available members at all and only looks up @objc members.  These properties
> help to reduce the risk that somebody misunderstands what is going on.
>

I imagine we could restrict all such dynamic types from having any
statically available members without losing much.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171203/65c13c9a/attachment.html>


More information about the swift-evolution mailing list