<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">I tried to send a response to this earlier today but it apparently didn’t get out. If it did, then I apologize in advance for the dupe:</div><div class=""><br class=""></div><div><blockquote type="cite" class=""><div class="">On Oct 30, 2017, at 10:47 PM, Douglas Gregor <<a href="mailto:dgregor@apple.com" class="">dgregor@apple.com</a>> wrote:</div><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><span 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; float: none; display: inline !important;" class="">// not magic, things like Int, String and many other conform to this.<span class="Apple-converted-space"> </span></span><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=""><span 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; float: none; display: inline !important;" class="">protocol Pythonable {</span><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=""><span 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; float: none; display: inline !important;" class=""> init?(_ : PythonObject)</span><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=""><span 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; float: none; display: inline !important;" class=""> func toPython() -> PythonObject</span><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=""><span 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; float: none; display: inline !important;" class="">}</span><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></blockquote><div class=""><br class=""></div>It’s not magic unless you expect the compiler or runtime to help with conversion between Int/String/etc. and PythonObject, as with _ObjectiveCBridgeable.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Right, as I said above “not magic”. The conformances would be manually implemented in the Python overlay. This provides a free implicit conversion from "T -> Pythonable” for the T’s we care about, and a failable init from Python back to Swift types.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Note that, under this scheme,</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let p: Pythonable = 17</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let i: Int = p as! i</div><div class=""><br class=""></div><div class="">will work if Int is Pythonable, but not when p comes back from Python. </div></div></div></div></blockquote><div><br class=""></div><div>Right. I don't expect users to traffic in the Pythonable type, it would be something that is used internally by the Python overlay. The only time I'd expect them to see it is when they want to make one of their types Pythonable (which would be rare, but needs to be possible).<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class="">I can think of two things that could tip the scale of the discussion:</div><div class=""><br class=""></div><div class="">a) The big question is whether we *want* the ability to write custom rule-of-5 style behavior for structs, or if we want it to only be used in extreme cases (like bridging interop in this proposal). If we *want* to support it someday, then adding proper “safe” support is best (if possible). If we don’t *want* people to use it, then making it Unsafe and ugly is a reasonable way to go.</div><div class=""><br class=""></div><div class="">b) The ownership proposal is likely to add deinit's to structs. If it also adds explicit move initializers, then it is probably the right thing to add copy initializers also (and thus go with approach #2). That said, I’m not sure how the move initializers will be spelled or if that is the likely direction. If it won’t add these, then it is probably better to go with approach #1. John, what do you think?</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space;"><div class=""><div class="">Presumably, binding to Python is going to require some compiler effort—defining how it is that Python objects are initialized/copied/moved/destroyed seems like a reasonable part of that effort.</div></div></div></blockquote><br class=""></div><div class="">Actually no. If we add these three proposals, there is no other python (or perl, etc…) specific support needed. It is all implementable in the overlay.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Support for working with Python objects would be implementable in the overlay, but the result isn’t necessarily ergonomic (e.g., my “as!” case from a Python-generated integer object to Int, shown above). That might be fine! More comments on this below.</div></div></div></div></blockquote><div><br class=""></div><div>Yeah, I don't think that matters. The preferred way to do this is to use:<br class=""><br class=""> Int(somePythonValue)<br class=""><br class="">which would be a failable conversion. somePythonValue would be a PythonObject (the struct) not "Pythonable"<br class=""></div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><span 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; float: none; display: inline !important;" class="">// Magic, allows anyobject-like member lookup on a type when lookup otherwise fails.</span><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=""><span 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; float: none; display: inline !important;" class="">protocol DynamicMemberLookupable {</span><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=""><span 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; float: none; display: inline !important;" class=""> associatedtype MemberLookupResultType</span><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=""><span 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; float: none; display: inline !important;" class=""> func dynamicMemberLookup(_ : String) -> MemberLookupResultType</span><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=""><span 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; float: none; display: inline !important;" class="">}</span><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></blockquote><div class=""><br class=""></div><div class="">AnyObject lookup looks for an actual declaration on any type anywhere. One could extend that mechanism to, say, return all Python methods and assume that you can call any Python method with any PythonObject instance. AnyObject lookup is fairly unprincipled as a language feature, because there’s no natural scope in which to perform name lookup, and involves hacks at many levels that don’t always work (e.g., AnyObject lookup… sometimes… fails across multiple source files for hard-to-explain reasons). You’re taking on that brokenness if you expand AnyObject lookup to another ecosystem.</div></div></div></div></blockquote><div class=""><br class=""></div>Yeah, sorry, that’s not what I meant:</div></div></div></blockquote><div class=""><br class=""></div>(Good)</div></div></div></blockquote><div><br class=""></div><div>Also, for sake of discussion, we’d have to figure out what the type of MemberLookupResultType would be for Python. I can see several choices:</div><div><br class=""></div><div>1) Make it return a "PythonObject!”</div><div>2) Make it strict, returning a PythonObject or trapping.</div><div>3) Make PythonObject itself nullable internally and return a null PythonObject.</div><div><br class=""></div><div>#1 matches Python semantics directly, because it allows clients who care to check, but those who don't can ignore it. The only problem I anticipate is that it will break things like:</div><div><br class="">let x = foo.bar<br class="">let y = x.thing // fails, because x implicitly promoted to PythonObject?<br class=""> </div><div>#3 is gross and cuts against lots of things in Swift (recall when UnsafePointer itself was implicitly nullable, lets not go back to those bad old days). I think that #2 is the least bad tradeoff.</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Right, something like this could definitely work, but keep in mind that the Swift compiler knows nothing about Python declarations. </div><div class=""><br class=""></div><div class="">Perhaps the most straight-forward thing would be to support:</div><div class=""><br class=""></div><div class="">protocol CustomCallable {<br class=""> func call(…arg list as array and kw args...)</div><div class=""> func callMember(_ : String, …otherstuffabove...)<br class="">}</div><div class=""><br class=""></div><div class="">Given this, the compiler could map:</div><div class=""><br class=""></div><div class="">pythonThing(42) -> pythonThing.call([42])</div><div class="">pythonThing.method(a: 42) -> pythonThing.callMember(“method”, kwargs: [“a”: 42])</div><div class=""><br class=""></div><div class="">This is the simplest way to map the Swift semantics (where kw args are part of compound lookups) into the Python world.</div></div></div></blockquote><br class=""></div><div class="">Okay, I agree that this gets Swift syntax into a call to the Python interpreter fairly quickly. Over-architecting for the sake of discussion:</div><div class=""><br class=""></div><div class="">protocol CustomCallable {</div><div class=""> associatedtype CustomArgument</div><div class=""> associatedtype CustomNominal</div><div class=""> associatedtype CustomResult</div><div class=""><br class=""></div><div class=""> func callMember(self: CustomNominal, functionName: String, arguments: [(String, CustomArgument)]) throws -> CustomResult</div><div class=""> // something for class/static members</div><div class="">}</div><div class=""><br class=""></div><div class="">But this is *all* dynamic, even when one could map much of Python’s type information into Swift. For example, let’s take this:</div></div></div></blockquote><div><br class=""></div><div>Yes, something like this is what I had in mind, but I think that functionName should only be required to be StringLiteralConvertible (no need to actually synthesize a real swift string).</div><div><br class=""></div><div><br class=""></div><div>Since you bring it up, Python exceptions will be annoying - As with other languages, Python can throw from an arbitrary expression. Modeling everything as throws in Swift would be super-annoying and unergonomic for the programmer, because we'd require 'try' everywhere. Thoughts on what to do about that are welcome!</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><br class=""></div><div class=""><pre style="overflow-x: auto; overflow-y: hidden; padding: 5px; background-color: rgb(238, 255, 204); color: rgb(51, 51, 51); line-height: 15.600000381469727px; border-top-width: 1px; border-bottom-width: 1px; border-style: solid none; border-top-color: rgb(170, 204, 153); border-bottom-color: rgb(170, 204, 153);" class=""><span class="k" style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span class="nc" style="color: rgb(14, 132, 181); font-weight: bold;">Dog</span><span class="p">:</span>
<span class="k" style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span class="nf" style="color: rgb(6, 40, 126);">__init__</span><span class="p">(</span><span class="bp" style="color: rgb(0, 112, 32);">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp" style="color: rgb(0, 112, 32);">self</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="n">name</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">name</span>
<span class="bp" style="color: rgb(0, 112, 32);">self</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="n">tricks</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="p">[]</span> <span class="c1" style="color: rgb(64, 128, 144); font-style: italic;"># creates a new empty list for each dog</span>
<span class="k" style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span class="nf" style="color: rgb(6, 40, 126);">add_trick</span><span class="p">(</span><span class="bp" style="color: rgb(0, 112, 32);">self</span><span class="p">,</span> <span class="n">trick</span><span class="p">):</span>
<span class="bp" style="color: rgb(0, 112, 32);">self</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="n">tricks</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="n">append</span><span class="p">(</span><span class="n">trick</span><span class="p">)</span></pre><div class=""><br class=""></div></div><div class="">With your don’t-modify-the-compiler approach, how can I create a Dog instance and add a trick? I probably need to look up the class by name, call __init__ manually, etc.</div><div class=""><br class=""></div><div class=""> let dogClass = python_getClassByName(“Dog”) // implemented in the Python “overlay’, I guess</div><div class=""> let dog = python_createInstance(dogClass) // implemented in the Python “overlay’, I guess</div><div class=""> dog.__init__(“Brianna”) // uses CustomCallable’s callMember</div><div class=""> dog.add_trick(“Roll over”) // uses CustomCallable’s callMember</div></div></div></blockquote><div><br class=""></div><div>I-am-not-a-python-expert, but I'd expect this to work:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let dogModule = Python.import("DogModule")<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>dogModule.Dog("jckarter").add_trick("SILGenGen”)</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>let dog = dogModule.Dog(“Brianna”)</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>dog.add_trick(“Roll over)<br class=""><br class="">or equivalently:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>let Dog = Python.import(“DogModule.Dog")<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>Dog("jckarter").add_trick("SILGenGen”)<br class=""><div><span class="Apple-tab-span" style="white-space: pre;">        </span>let dog = Dog(“Brianna”)</div><div><span class="Apple-tab-span" style="white-space: pre;">        </span>dog.add_trick(“Roll over)<br class=""></div></div><div><br class=""></div><div>Seems pretty nice to me, with zero “Swift compiler knowledge of Python” required.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class="">With compiler integration, </div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class Dog : PythonObject {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> init(_ name: Pythonable)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> func add_trick(_ trick: Pythonable)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div></div></blockquote><div><br class=""></div><div>Something like this is possible, but would be substantially more work, be substantially more invasive, and would set the precedent that every other dynamic language would get support hacked directly into Swift. The only reason I can see this being useful is if we wanted to support the optional typing annotations in Python. While this would be "nice to have", I think the cost/benefit tradeoff involved is totally wrong for Swift. </div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class="">With either the true “Python importer” solution or this code-generation solution, you at least get some level of code completion and basic sanity checking “for free”. In other words, you get some of the benefits of having a statically-type-checked language while still working on dynamic Pythonable types.</div></div></blockquote><br class=""></div><div>We don't need to make Swift better at Python than Python itself is :-)</div><div><br class=""></div><div>-Chris</div><div><br class=""></div><br class=""></body></html>