> On Oct 30, 2017, at 2:54 PM, David Kopecky <dkope...@apple.com> wrote: > > To Chris’s use case point, I see Python -> Swift interop as a very common use > case in data science / ML for speeding up certain functions/modules by > embedding Swift into Python, like you can with, say, ctypes or python > extensions in dynamic C libraries. This is very common practice in the Python > universe, it just could be a lot better with Swift instead. > > Things like this *sort of* exist with Swift > (https://gist.github.com/jiaaro/e111f0f64d0cdb8aca38 > <https://gist.github.com/jiaaro/e111f0f64d0cdb8aca38>), but it’s not really > very mature or functional. > > I would LOVE to be able to speed up a lot of python code by implementing > swift libraries and importing into python.
There are a bunch of ways one could export Swift functionality to Python, from a library solution like the C++ Boost.Python (http://www.boost.org/doc/libs/1_65_1/libs/python/doc/html/index.html <http://www.boost.org/doc/libs/1_65_1/libs/python/doc/html/index.html>), to code generators like SWIG or, with some future expansion of metadata, directly at runtime via reflection facilities. Python’s dynamic nature makes this direction easier. The harder challenge (IMO!) is to get a Python library into Swift, because there isn’t a lot of type information in Python, so you have to show the Python in the much-more-strongly-typed Swift somehow (and keep it usable). - Doug > > - David > >> On Oct 30, 2017, at 13:25, Douglas Gregor via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> >> >>> On Oct 29, 2017, at 1:34 PM, Chris Lattner via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>> >>>> >>>> On Oct 29, 2017, at 8:23 AM, Chris Lattner via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>> >>>> >>>>> On Oct 29, 2017, at 4:04 AM, Lukas Stabe <lu...@stabe.de >>>>> <mailto:lu...@stabe.de>> wrote: >>>>> >>>>>> On 28. Oct 2017, at 23:10, Chris Lattner via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> … which is to say, exactly identical to the Python version except that >>>>>> new variables need to be declared with let/var. This can be done by >>>>>> blessing Python.Object (which is identical to “PyObject*” at the machine >>>>>> level) with some special dynamic name lookup behavior: Dot syntax turns >>>>>> into a call to PyObject_GetAttrString, subscripts turn into >>>>>> PyObject_GetItem, calls turn into PyObject_Call, etc. ARC would be >>>>>> implemented with INCREF etc. >>>>> >>>>> That sounds like a very interesting prospect. Do you think it would make >>>>> sense to make the language features that facilitate this (dynamic >>>>> dispatch of method calls, property accesses, subscript and ARC) available >>>>> to Swift classes annotated in some way, so that interop like this can be >>>>> implemented as a library without special treatment by the Swift compiler? >>>>> This could also enable more dynamic DSL like features. >>>> >>>> I haven’t explored enough of the design space to be sure, but I’d want to >>>> make sure that a library version of this could be done without giving up >>>> ergonomics of the result. If you were interested in being able to interop >>>> with other languages that are dynamically typed and reference counted, >>>> then something like this could be possible in principle: >>> >>> Thinking about the Perl case makes it clear to me that this should not be >>> built into the compiler as a monolithic thing. Perl supports several >>> different types (SV/AV/HV) which represent different concepts (scalars, >>> arrays, hashes) so baking it all together into one thing would be the wrong >>> way to map it. In fact, the magic we need is pretty small, and seems >>> generally useful for other things. Consider a design like this: >>> >>> >>> // not magic, things like Int, String and many other conform to this. >>> protocol Pythonable { >>> init?(_ : PythonObject) >>> func toPython() -> PythonObject >>> } >> >> It’s not magic unless you expect the compiler or runtime to help with >> conversion between Int/String/etc. and PythonObject, as with >> _ObjectiveCBridgeable. >> >>> >>> // Not magic. >>> struct PythonObject : /*protocols below*/ { >>> var state : UnsafePointer<PyObject> >>> >>> subscript(_ : Pythonable…) -> PythonObject { >>> ... >>> } >>> } >>> >>> // Magic, must be on the struct definition. >>> // Could alternatively allow custom copy/move/… ctors like C++. >>> protocol CustomValueWitnessTable { >>> static func init(..) >>> static func copy(..) >>> static func move(..) >>> static func destroy(..) >>> } >> >> Swift’s implementation model supports this. As a surface-level construct >> it’s going to be mired in UnsafeMutablePointers, and it’s not at all clear >> to me that we want this level of control there. 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. >> >>> // Magic, allows anyobject-like member lookup on a type when lookup >>> otherwise fails. >>> protocol DynamicMemberLookupable { >>> associatedtype MemberLookupResultType >>> func dynamicMemberLookup(_ : String) -> MemberLookupResultType >>> } >> >> 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. >> >> Although it doesn’t really seem like AnyObject lookup is the thing you’re >> asking for here. It seems more like you want dynamicMemberLookup(_:) to >> capture “self” and the method name, and then be a callable thing as below... >> >>> >>> // Magic, allows “overloaded/sugared postfix ()”. >>> protocol CustomCallable { >>> func call( …) >>> } >>> >>> The only tricky thing about this is the call part of things. At least in >>> the case of python, we want something like this: >>> >>> foo.bar(1, 2, a: x, b: y) >>> >>> to turn into: >>> foo.dynamicMemberLookup(“bar”).call(1, 2, kwargs: [“a”:x, “b”:y]) >>> >>> We don’t want this to be a memberlookup of a value that has “bar” as a >>> basename and “a:” and “b:” as parameter labels. >> >> Well, I think the MemberLookupResult is going to get the name “bar”, >> argument labels “_:_:a:b:”, and arguments “1”, “2”, “x”, “y”, because that’s >> the Swift model of argument labels. It can then reshuffle them however it >> needs to for the underlying interaction with the Python interpreter. >> >> There are definite design trade-offs here. With AnyObject lookup, it’s a >> known-broken feature but because it depends on synthesized Swift method >> declarations, it’ll behave mostly the same way as other Swift method >> declarations—static overloading, known (albeit weak) type signatures, etc. >> But, it might require more of Python’s model to be grafted onto those method >> declarations. With dynamic member lookup, you’re throwing away all type >> safety (even for a motivated Python developer who might be willing to >> annotate APIs with types) and creating a general language mechanism for >> doing that. >> >> - Doug >> >>> >>> -Chris >>> >>>> >>>> protocol DynamicDispatchable { // Protocol is “magic" known by the >>>> compiler. >>>> func retain() >>>> func release() >>>> func memberLookup(_ : String) -> Self >>>> func subscript<T>(_ : T) -> Self >>>> func call(_ args: [Self]) -> Self >>>> } >>>> >>>> module Python { >>>> struct Object : DynamicDispatchable { >>>> var state : UnsafePointer<PyObject> >>>> >>>> func retain() { >>>> INCREF(self) >>>> } >>>> >>>> func memberLookup(_ : String) -> Object { >>>> PyObject_GetAttrString(…) >>>> } >>>> etc >>>> } >>>> >>>> module Perl5 { >>>> struct Object : DynamicDispatchable { >>>> var state : UnsafePointer<SV> >>>> >>>> func retain() { >>>> SvREFCNT_inc(self) >>>> } >>>> …. >>>> >>>> >>>> >>>> Are there other uses for such a thing? >>>> >>>> -Chris >>>> >>>> >>>> _______________________________________________ >>>> swift-evolution mailing list >>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution