+1 to both proposals. I don't know enough to comment further, but I know this 
would help in the adoption of Swift on my team(s) (we call a lot of Python, 
although currently via exec-ing some bash scripts; it's a mess, but works well 
in the CLI world the stuff was written for).

> On Nov 14, 2017, at 23: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), 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
> 
> 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
>       • Author: Chris Lattner
>       • Review Manager: TBD
>       • Status: Awaiting implementation
> 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 and underlying messaging infrastructure). 
> This sort of functionality is great for implementing dynamic language 
> interoperability, dynamic proxy APIs, and other APIs (e.g. for JSON 
> processing).
> 
> Swift-evolution thread: Discussion thread topic for that proposal
> 
> 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 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 
> 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 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.
> 
> 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.
> 
> 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
> )
>     }
>   }
> }
> 
> Source compatibility
> 
> This is a strictly additive proposal with no source breaking changes.
> 
> Effect on ABI stability
> 
> This is a strictly additive proposal with no ABI breaking changes.
> 
> Effect on API resilience
> 
> This has no impact on API resilience which is not already captured by other 
> language features.
> 
> Alternatives considered
> 
> A few alternatives were considered:
> 
> 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.
> 
> 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


-- 
Rick Mann
rm...@latencyzero.com


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

Reply via email to