<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Nov 30, 2017, at 3:24 AM, Benjamin G <<a href="mailto:benjamin.garrigues@gmail.com" class="">benjamin.garrigues@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><br class="Apple-interchange-newline"><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;">On Thu, Nov 30, 2017 at 9:24 AM, Douglas Gregor via swift-evolution<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word; line-break: after-white-space;" class=""><span class=""><br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class="">On Nov 26, 2017, at 10:04 PM, Chris Lattner via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>> wrote:</div><br class="m_8995992571770886749Apple-interchange-newline"><div class=""><div class="">I’d like to formally propose the inclusion of user-defined dynamic member lookup types.<br class=""><br class="">Here is my latest draft of the proposal:<br class=""><a href="https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438" target="_blank" class="">https://gist.github.com/<wbr class="">lattner/<wbr class="">b016e1cf86c43732c8d82f90e5ae54<wbr class="">38</a><br class=""><a href="https://github.com/apple/swift-evolution/pull/768" target="_blank" class="">https://github.com/apple/<wbr class="">swift-evolution/pull/768</a><br class=""><br class="">An implementation of this design is available here:<br class=""><a href="https://github.com/apple/swift/pull/13076" target="_blank" class="">https://github.com/apple/<wbr class="">swift/pull/13076</a><br class=""><br class="">The implementation is straight-forward and (IMO) non-invasive in the compiler.<br class=""></div></div></blockquote></div><br class=""><div class=""><br class=""></div></span><div class="">I think better interoperability with Python (and other OO languages in widespread use) is a good goal, and I agree that the implementation of the feature described is straight-forward and not terribly invasive in the compiler.</div><div class=""><br class=""></div><div class="">However, I do not think this proposal is going in the right direction for Swift. I have objections on several different grounds.</div><div class=""><br class=""></div><div class=""><b class="">Philosophy</b></div><div class="">Swift is, unabashedly, a strong statically-typed language. We don’t allow implicit down casting, we require “as?” so you have to cope with the possibility of failure (or use “as!” and think hard about the “!”). Even the gaping hole that is AnyObject dispatch still requires the existence of an @objc declaration and produces an optional lookup result, so the user must contend with the potential for dynamic failure. Whenever we discuss adding more dynamic features to Swift, there’s a strong focus on maintaining that strong static type system.</div><div class=""><br class=""></div><div class="">IMO, this proposal is a significant departure from the fundamental character of Swift, because it allows access to possibly-nonexistent members (as well as calls with incorrect arguments, in the related proposal) without any indication that the operation might fail. It’s easy to fall through these cracks for any type that supports DynamicMemberLookupProtocol—a single-character typo when using a DynamicMemberLookupProtocol-<wbr class="">capable type means you’ve fallen out of the safety that Swift provides. I think that’s a poor experience for the Python interoperability case, but more on that in the Tooling section below.</div><div class=""><br class=""></div><div class="">While we shouldn’t necessarily avoid a feature simply because it can be used distastefully, consider something like this:</div><div class=""><br class=""></div><div class=""><span class="m_8995992571770886749Apple-tab-span" style="white-space: pre-wrap;">        </span>public extension NSObject : DynamicMemberLookupProtocol, DynamicCallableProtocol { … }</div><div class=""><br class=""></div><div class="">that goes directly to the Objective-C runtime to resolve member lookups and calls—avoiding @objc, bridging headers, and so on. It’s almost frighteningly convenient, and one could imagine some mixed Objective-C/Swift code bases where this would save a lot of typing (of code)… at the cost of losing static typing in the language. The presence of that one extension means I can no longer rely on the safety guarantees Swift normally provides, for any project that imports that extension and uses a subclass of NSObject. At best, we as a community decide “don’t do that”; at worse, some nontrivial fraction of the community decides that the benefits outweigh the costs (for this type or some other), and we can no longer say that Swift is a strong statically-typed language without adding “unless you’re using something that adopts DynamicMemberLookupProtocol”.</div><div class=""><br class=""></div><div class=""><b class="">Tooling</b></div><div class="">The Python interoperability enabled by this proposal *does* look fairly nice when you look at a small, correctly-written example. However, absolutely none of the tooling assistance we rely on when writing such code will work for Python interoperability. Examples:</div><div class=""><br class=""></div><div class="">* As noted earlier, if you typo’d a name of a Python entity or passed the wrong number of arguments to it, the compiler will not tell you: it’ll be a runtime failure in the Python interpreter. I guess that’s what you’d get if you were writing the code in Python, but Swift is supposed to be *better* than Python if we’re to convince a community to use Swift instead.</div><div class="">* Code completion won’t work, because Swift has no visibility into declarations written in Python</div><div class="">* Indexing/jump-to-definition/<wbr class="">lookup documentation/generated interface won’t ever work. None of the IDE features supported by SourceKit will work, which will be a significant regression for users coming from a Python-capable IDE.</div><div class=""><br class=""></div><div class="">Statically-typed languages should be a boon for tooling, but if a user coming from Python to Swift *because* it’s supposed to be a better development experience actually sees a significantly worse development experience, we’re not going to win them over. It’ll just feel inconsistent.</div><div class=""><br class=""></div><div class=""><b class="">Dynamic Typing Features</b></div><div class="">It’s possible that the right evolutionary path for Swift involves some notion of dynamic typing, which would have a lot of the properties sought by this proposal (and the DynamicCallableProtocol one). If that is true—and I’m not at all convinced that it is—we shouldn’t accidentally fall into a suboptimal design by taking small, easy, steps. If we’re to include dynamic-typing facilities, we should look at more existing practice—C# ‘dynamic' is one such approach, but more promising would be some form of gradual typing a la TypeScript that let’s one more smoothly (and probably explicitly) shift between strong and weak typing.</div><div class=""><br class=""></div><div class=""><b class="">How Should Python Interoperability Work?</b></div><div class="">Going back to the central motivator for this proposal, I think that providing something akin to the Clang Importer provides the best interoperability experience: it would turn Python declarations into *real* Swift declarations, so that we get the various tooling benefits of having a strong statically-typed language. Sure, the argument types will all by PyObject or PyVal, but the names are there for code completion (and indexing, etc.) to work, and one could certainly imagine growing the importer to support Python’s <a href="https://docs.python.org/3/library/typing.html" target="_blank" class="">typing annotations</a>. But the important part here is that it doesn’t change the language model at all—it’s a compiler feature, separate from the language. Yes, the Clang importer is a big gnarly beast—but if the goal is to support N such importers, we can refactor and share common infrastructure to make them similar, perhaps introducing some kind of type provider infrastructure to allow one to write new importers as Swift modules.</div></div></blockquote><div class=""><br class=""></div>Thanks for expressing those concerns. I think a lot of people like me don't feel legitimate enough to argue with Chris on what Swift philosophy is about, so it's good to see someone qualified saying it. </div></div></blockquote><div><br class=""></div><div>If you have an opinion, please speak up. The core team will continue to articulate our views (both as individuals and collectively as the core team) and make the final decisions for the Swift project, but we don’t have the breadth of experience of the larger Swift community, so we value discussion with everyone here.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;">One question though : I'm not familiar with Clang importer, so there's something I don't understand : in your ideal scenario, would the syntax for calling those Python imported functions look like regular swift function calls ? Because i'm pretty sure some python function signature can't be expressed with the current swift syntax, unless we had those "dynamic" types that you mentioned above ( in which case, are you suggesting adding a new keyword to the language rather than a protocol ?)<br class=""></div></div></blockquote><div><br class=""></div><div><br class=""></div><div>Those imported functions would be regular Swift declarations, with implementations that call into the Python runtime. They’ll necessarily have very weakly-specified types, e.g.,</div><div><pre style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.600000381469727px; margin-top: 0px; margin-bottom: 0px; word-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(36, 41, 46);" class="">
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">def</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">add_trick</span>(<span class="pl-smi" style="box-sizing: border-box;">self</span>, <span class="pl-smi" style="box-sizing: border-box;">trick</span>):
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">self</span>.tricks.append(trick)</pre><div class=""><br class=""></div></div><div><br class=""></div><div><br class=""></div><div>might turn into</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>extension PyVal {</div><div> <span class="Apple-tab-span" style="white-space:pre">        </span> func add_trick(_ trick: PyVal) { /* … */ }</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><br class=""></div><div>Python allows positional arguments, so it works to use the underscore. Python 3’s <a href="https://www.python.org/dev/peps/pep-3102/" class="">keyword-only arguments</a> could be mapped to proper Swift argument labels. Python’s **kwargs would be mapped to a dictionary, e.g.,</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>func do_something(_ kwargs: [String: PyVal]) { … }</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space: pre;">        </span>- Doug</div></div><div><br class=""><blockquote type="cite" class=""><div class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><br class=""></div><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><br class=""></div><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word; line-break: after-white-space;" class=""><div class=""><br class=""></div><div class="">In truth, you don’t even need the compiler to be involved. The dynamic “subscript” operation could be implemented in a Swift library, and one could write a Python program to process a Python module and emit Swift wrappers that call into that subscript operation. You’ll get all of the tooling benefits with no compiler changes, and can tweak the wrapper generation however much you want, using typing annotations or other Python-specific information to create better wrappers over time.</div><div class=""><br class=""></div><div class=""><span class="m_8995992571770886749Apple-tab-span" style="white-space: pre-wrap;">        </span>- Doug</div><div class=""><br class=""></div></div><br class="">______________________________<wbr class="">_________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">evolution</a></blockquote></div></div></blockquote></div><br class=""></body></html>