Exploring @dynamicMemberLookup
This proposal had some controversy, and one thing I didn’t personally like on this new feature is that it meant that typed annotated with
@dynamicMemberLookup
would not, by design, show any potential compilation error. Which is understandable, as the whole need for that proposal was to be able to call properties which we didn’t know at compile-time.But this also meant that once the type was annotated with
@dynamicMemberLookup
, you wouldn’t be able to choose at call-site if you wanted an expression to allow dynamic member lookup or wanted the expression on that type to be type-checked.[…]
The solution is actually quite short to implement. We’ll create a
^
postfix operator that will wrap the instance it’s applied on into some proxy object that is the one being@dynamicMemberLookup
. That proxy object will just wrap the dictionary we applied^
on, and on dynamic lookup, will search the key in the dictionary to return the corresponding value (if it exists).[…]
In the playground attached below, I’ve explored that idea a bit more, by allowing another syntax. This one looks more like creating a context or scope in which everything we call is dynamically looked up (instead of looking like chaining
^
calls)
Here are some non-obvious things you can do with
@dynamicMemberLookup
:
Provide a setter in addition to the getter. This allows assignments through dynamic member lookup, or passing the subscript expression
inout
. For an example, check out Doug Gregor’s wrapper for reading and writing environment variables.Choose any return type. The subscript must have a single parameter, which can be any type that conforms to
ExpressibleByStringLiteral
but will likely be aString
99 % of the time. The return type, though, can be anything you want, including generic parameters.Provide multiple overloads of the subscript with different return types. If there are multiple dynamic member subscripts, the compiler will pick the best match according to its normal type inference rules, or emit an error if the choice is ambiguous.
Here are some things you can’t do:
“Hide” a declared member of a type. Declared properties always take precedence in the type checker over dynamic member lookup.
Retroactively support dynamic member lookup for a type you don’t control. The attribute only works on the original type declaration.
Nice summary, including a good descriptions of potential caveats. One thing that I haven’t explained yet is that the constexpr work can grow to support static diagnosis of bad members - the member names are constants after all!
Previously: Swift 4.2.
Update (2018-07-18): Jonathan Wight:
Found a great using for Swift 4.2’s @dynamicMemberLookup doing Python “records” (ala @kennethreitz ) style sqlite binding[…]