[Pitch] Introduce User-defined "Dynamic Member Lookup" Types

Hi Chris,

There are only a few examples in the proposal that demonstrate using dynamic member lookup.

I suppose that some examples will look like this:

let foo: PyRef = ...
foo.bar // translates into foo[dynamic: “bar”]
foo.bar.baz // translates into foo[dynamic: “bar”][dynamic: “baz”]

and if we have:

extension PyRef {
    var one: Int {
        return 1
    }
}

foo.bar.one // returns 1, because it translates into foo[dynamic: “bar”].one

This to me is hinting that dynamic member lookup will only be applied to one property at a time. In order for it to work on multiple levels, the type being returned by the subscript operation must still conform to DynamicMemberLookup. Is this how you envisioned it to work?

Cheers,
Andrew

···

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

The author of the type gets to make that choice. In the case of the Python interop, yes, it will return another PyRef. In the case of a JSON library, that should also return "Self?" so that you can optional chain through an access path.

That said, there are other potential use cases where you may only want a single level of dynamism. The proposal allows the API author to specify the element type, which means they have flexibility to do what is right for their usecase.

-Chris

···

On Nov 15, 2017, at 1:38 AM, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:

Hi Chris,

There are only a few examples in the proposal that demonstrate using dynamic member lookup.

I suppose that some examples will look like this:

let foo: PyRef = ...
foo.bar // translates into foo[dynamic: “bar”]
foo.bar.baz // translates into foo[dynamic: “bar”][dynamic: “baz”]

and if we have:

extension PyRef {
    var one: Int {
        return 1
    }
}

foo.bar.one // returns 1, because it translates into foo[dynamic: “bar”].one

This to me is hinting that dynamic member lookup will only be applied to one property at a time. In order for it to work on multiple levels, the type being returned by the subscript operation must still conform to DynamicMemberLookup. Is this how you envisioned it to work?