Those two proposals are growing on me and they have the potential to make Swift 
even better at DSLs.

> On 15 Nov 2017, at 08:29, Chris Lattner via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Hi All,
> 
> As a peer to the DynamicCallable proposal 
> (https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d 
> <https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d>), I’d like 
> to get your feedback on making member lookup dynamically extensible.  My 
> primary motivation is to improve interoperability with dynamic languages like 
> Python, Perl, Ruby, Javascript, etc, but there are other use cases (e.g. when 
> working with untyped JSON).
> 
> In addition to being a high impact on expressivity of Swift, I believe an 
> implementation can be done in a way with changes that are localized, and thus 
> not have significant impact on the maintainability of the compiler as a 
> whole.  Once the pitch phase of this proposal helps refine the details, I’ll 
> be happy to prepare an implementation for consideration.
> 
> In case it is useful, I’m working on cleaning up my current prototype Python 
> bindings.  I’ll share them in the next day or two in case they are useful to 
> provide context.  It is amazingly simple: less than 500 lines of Swift code 
> (plus some small additional C header glue to work around clang importer 
> limitations) enables impressive interoperability.  The only problems are the 
> verbosity addressed by this proposal and the peer DynamicCallable proposal.
> 
> 
> Here is the canonical proposal URL:
> https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438 
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438>
> 
> A snapshot of the proposal is included below in case it is useful.  Thanks in 
> advance for help improving the proposal!
> 
> -Chris
> 
> 
> Introduce User-defined "Dynamic Member Lookup" Types
> 
> Proposal: SE-NNNN 
> <https://gist.github.com/lattner/NNNN-DynamicMemberLookup.md>
> Author: Chris Lattner <https://github.com/lattner>
> Review Manager: TBD
> Status: Awaiting implementation
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#introduction>Introduction
> 
> This proposal introduces a new DynamicMemberLookupProtocol type to the 
> standard library. Types that conform to it provide "dot" syntax for arbitrary 
> names which are resolved at runtime. It is simple syntactic sugar which 
> allows the user to write:
> 
>     a = someValue.someMember
>     someValue.someMember = a
> and have it be interpreted by the compiler as:
> 
>   a = someValue[dynamicMember: "someMember"]
>   someValue[dynamicMember: "someMember"] = a
> Many other languages have analogous features (e.g. the composition of 
> Objective-C's explicit properties 
> <https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/DeclaredProperty.html>
>  and underlying messaging infrastructure 
> <https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html>).
>  This sort of functionality is great for implementing dynamic language 
> interoperability, dynamic proxy APIs 
> <https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html>,
>  and other APIs (e.g. for JSON processing).
> 
> Swift-evolution thread: Discussion thread topic for that proposal 
> <https://lists.swift.org/pipermail/swift-evolution/>
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#motivation-and-context>Motivation
>  and Context
> 
> Swift is well known for being exceptional at interworking with existing C and 
> Objective-C APIs, but its support for calling APIs written in scripting 
> languages like Python, Perl, and Ruby is quite lacking.
> 
> C and Objective-C are integrated into Swift by expending a heroic amount of 
> effort into integrating Clang ASTs, remapping existing APIs in an attempt to 
> feel "Swifty", and by providing a large number of attributes and 
> customization points for changing the behavior of this integration when 
> writing an Objective-C header. The end result of this massive investment of 
> effort is that Swift provides a better experience when programming against 
> these legacy APIs than Objective-C itself did.
> 
> When considering the space of dynamic languages, three things are clear: 1) 
> there are several different languages of interest, and they each have 
> significant interest in different quarters: for example, Python is big in 
> data science and machine learning, Ruby is popular for building server side 
> apps, and even Perl is in still widely used. 2) These languages have decades 
> of library building behind them, sometimes with significant communities 
> <https://pandas.pydata.org/> and 3) there are one or two orders of magnitude 
> more users of these libraries than there are people currently using Swift.
> 
> While it is theoretically possible to expend the same level of effort on each 
> of these languages and communities as has been spent on Objective-C, it is 
> quite clear that this would both ineffective as well as bad for Swift: It 
> would be ineffective, because the Swift community has not leverage over these 
> communities to force auditing and annotation of their APIs. It would be bad 
> for Swift because it would require a ton of language-specific support (and a 
> number of third-party dependencies) onto the compiler and runtime, each of 
> which makes the implementation significantly more complex, difficult to 
> reason about, difficult to maintain, and difficult to test the supported 
> permutations. In short, we'd end up with a mess.
> 
> Fortunately for us, these scripting languages provide an extremely dynamic 
> programming model where almost everything is discovered at runtime, and many 
> of them are explicitly designed to be embedded into other languages and 
> applications. This aspect allows us to embed APIs from these languages 
> directly into Swift with no language support at all - without not the level 
> of effort, integration, and invasiveness that Objective-C has benefited from. 
> Instead of invasive importer work, we can write some language-specific Swift 
> APIs, and leave the interop details to that library.
> 
> This offers a significant opportunity for us - the Swift community can 
> "embrace" these dynamic language APIs (making them directly available in 
> Swift) which reduces the pain of someone moving from one of those languages 
> into Swift. It is true that the APIs thus provided will not feel "Swifty", 
> but if that becomes a significant problem for any one API, then the community 
> behind it can evaluate the problem and come up with a solution (either a 
> Swift wrapper for the dynamic language, or a from-scratch Swift 
> reimplementation of the desired API). In any case, if/when we face this 
> challenge, it will be a good thing: we'll know that we've won a significant 
> new community of Swift developers.
> 
> While it is possible today to import (nearly) arbitrary dynamic language APIs 
> into Swift today, the resultant API is unusable for two major reasons: member 
> lookup is too verbose to be acceptable, and calling behavior is similarly too 
> verbose to be acceptable. As such, we seek to provide two "syntactic sugar" 
> features that solve this problem. These sugars are specifically designed to 
> be dynamic language independent and, indeed, independent of dynamic languages 
> at all: we can imagine other usage for the same primitive capabilities.
> 
> The two proposals in question are the introduction of the DynamicCallable 
> <https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d> protocol 
> and a related DynamicMemberLookupProtocol proposal (this proposal). With 
> these two extensions, we think we can eliminate the need for invasive 
> importer magic by making interoperability with dynamic languages ergonomic 
> enough to be acceptable.
> 
> For example, consider this Python code:
> 
> class Dog:
>     def __init__(self, name):
>         self.name = name
>         self.tricks = []    # creates a new empty list for each dog
>         
>     def add_trick(self, trick):
>         self.tricks.append(trick)
> we would like to be able to use this from Swift like this (the comments show 
> the corresponding syntax you would use in Python):
> 
>   // import DogModule
>   // import DogModule.Dog as Dog    // an alternate
>   let Dog = Python.import(“DogModule.Dog")
> 
>   // dog = Dog("Brianna")
>   let dog = Dog("Brianna")
> 
>   // dog.add_trick("Roll over")
>   dog.add_trick("Roll over")
> 
>   // dog2 = Dog("Kaylee").add_trick("snore")
>   let dog2 = Dog("Kaylee").add_trick("snore")
> Of course, this would also apply to standard Python APIs as well. Here is an 
> example working with the Python pickleAPI and the builtin Python function 
> open:
> 
>   // import pickle
>   let pickle = Python.import("pickle")
> 
>   // file = open(filename)
>   let file = Python.open(filename)
> 
>   // blob = file.read()
>   let blob = file.read()
> 
>   // result = pickle.loads(blob)
>   let result = pickle.loads(blob)
> This can all be expressed today as library functionality written in Swift, 
> but without this proposal, the code required is unnecessarily verbose and 
> gross. Without it (but with the related DynamicCallable proposal 
> <https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d> the code 
> would have explicit member lookups all over the place:
> 
>   // import pickle
>   let pickle = Python.get(member: "import")("pickle")
> 
>   // file = open(filename)
>   let file = Python.get(member: "open")(filename)
> 
>   // blob = file.read()
>   let blob = file.get(member: "read")()
> 
>   // result = pickle.loads(blob)
>   let result = pickle.get(member: "loads")(blob)
> 
>   // dog2 = Dog("Kaylee").add_trick("snore")
>   let dog2 = Dog("Kaylee").get(member: "add_trick")("snore")
> While this is a syntactic sugar proposal, we believe that this expands Swift 
> to be usable in important new domains. In addition to dynamic language 
> interoperability, this sort of functionality is useful for other APIs, e.g. 
> when working with dynamically typed unstructured data like JSON, which could 
> provide an API like jsonValue?.jsonField1?.jsonField2where each field is 
> dynamically looked up.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#proposed-solution>Proposed
>  solution
> 
> We propose introducing this protocol to the standard library:
> 
> protocol DynamicMemberLookupProtocol {
>   associatedtype DynamicMemberLookupValue
> 
>   subscript(dynamicMember name: String) -> DynamicMemberLookupValue { get set 
> }
> }
> It also extends the language such that member lookup syntax (x.y) - when it 
> otherwise fails (because there is no member y defined on the type of x) and 
> when applied to a value which conforms to DynamicMemberLookupProtocol- is 
> accepted and transformed into a call to the subscript in the protocol. This 
> ensures that no member lookup on such a type ever fails.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#example-usage>Example
>  Usage
> 
> While there are many potential uses of this sort of API (e.g. resolving JSON 
> members to named results, producing optional bindings) a motivating example 
> comes from a prototype Python interoperability layer. There are many ways to 
> implement this, and the details are not particularly important, but it is 
> perhaps useful to know that this is directly useful to address the motivation 
> section described above. Given a currency type of PyVal (and a conforming 
> implementation named PyRef), an implementation may look like:
> 
> extension PyVal {
>   subscript(dynamicMember member: String) -> PyVal {
>     get {
>       let result = PyObject_GetAttrString(borrowedPyObject, member)!
>       return PyRef(owned: result)  // PyObject_GetAttrString returns +1 
> result.
>     }
>     set {
>       PyObject_SetAttrString(borrowedPyObject, member,
>                              newValue.toPython().borrowedPyObject)
>     }
>   }
> }
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#source-compatibility>Source
>  compatibility
> 
> This is a strictly additive proposal with no source breaking changes.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#effect-on-abi-stability>Effect
>  on ABI stability
> 
> This is a strictly additive proposal with no ABI breaking changes.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#effect-on-api-resilience>Effect
>  on API resilience
> 
> This has no impact on API resilience which is not already captured by other 
> language features.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#alternatives-considered>Alternatives
>  considered
> 
> A few alternatives were considered:
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#add-ability-to-provide-read-only-members>Add
>  ability to provide read-only members
> 
> The implementation above does not allow an implementation to statically 
> reject members that are read-only. If this was important to add, we could add 
> another protocol to model this, along the lines of:
> 
> protocol DynamicMemberLookupGettableProtocol {
>   associatedtype DynamicMemberLookupValue
> 
>   // gettable only
>   subscript(dynamicMember name: String) -> DynamicMemberLookupValue { get }
> }
> 
> protocol DynamicMemberLookupProtocol : DynamicMemberLookupGettableProtocol {
>   // gettable and settable.
>   subscript(dynamicMember name: String) -> DynamicMemberLookupValue { get set 
> }
>   }
> This would allow a type to implement one or the other based on their 
> capabilities. This proposal starts with a very simple design based on the 
> requirements of dynamic languages (which have no apparent immutability 
> model), but if there is demand for this (e.g. because we want input JSON 
> values to be gettable but not settalbe), the author is happy to switch to 
> this more general model.
> 
>  
> <https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438#naming>Naming
> 
> There is a lot of grounds to debate naming of the protocol and methods in 
> this type. Suggestions (along with rationale to support them) are more than 
> welcome.
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to