قالب وردپرس درنا توس
Home / IOS Development / Thoughts on @dynamicMemberLookup

Thoughts on @dynamicMemberLookup



Possibly the most controversial new feature in Swift 4.2 is Dynamic Membership introduced by Swift Evolution Proposal SE-0195.

@dynamicMemberLookup is a new attribute that can be used for a class, structure, enum or protocol declaration. 1 Cases of a @dynamic member search type can be called with any property access access dot notation) - the compiler will not issue an error if a property with the specified name is not exists.

Runtime Lookup for Properties

Instead, the compiler translates such access to a special subscription (DynamicMember :) subscription that passes the "non-existent" member name as a string. Within the signature of the signature, the author of the type can do what they want to look up to the correct return value at runtime.

Let's look at a simple, meaningless example. This type returns any member as a capital letter:

  @dynamicMemberLookup 
  struct    Uppercaser    {
      subscription   (  dynamicMember    input :    string )    ->    string    {. 
          return    entry    uppercased   () 
    } 
} 

  ]     hey    // → "HELLO" 
  // You can write everything as long as Swift accepts it as an identifier. 
  Uppercaser   .   käsesoße    // → "KÄSESOSSE" 

The rules

Here are some unsettled things you can do with @dynamicMemberLookup :

  • Give a set besides the goats. This allows assignments through dynamic member postings, or transfers the subscription term inout . For example, check out Doug Gregor's wrapper for reading and writing environment variables.

  • Select any return type. The sub script must have a single parameter, which may be any type that corresponds to ExpressibleByStringLiteral but will probably be a String 99% of the time. However, the return type can be anything you want, including generic parameters.

  • Provide multiple overloads of subscription with different return types. If there are multiple dynamic member subscriptions, the compiler will choose the best match according to the usual settlement rules, or make an error if the choice is ambiguous.

Here are some things you can not do:

  • "Hide" a declared member of a type. Declared properties always prevail in the Type Controller over Dynamic Member Upload.

  • Support for dynamic members demand for a type you do not control. The attribute only works on the original type statement.

Dynamic Language Interoperability

The proposal and implementation of dynamic member listings was largely driven by the Swift for TensorFlow team on Google. Their main motivation is to facilitate interoperability between Swift and dynamic languages, especially (but not exclusive) Python. The goal is to make it possible to call the Swift Python Code with a well-known syntax.

The proposal @Dynamic Membership covers only "half of the distance" against this goal. The other half is a feature called @dynamic Callable that allows developers to declare types as dynamic "callable", ie to answer driving time to a "call function" with any arbitrary argument list (optional with marked arguments). @dynamic Callable did not make Swift 4.2, but the SE-0216 proposal was recently accepted and the feature will be included in a future Swift edition.

The Swift for Tensorflow team has written a Swift-Python Bridge that will benefit from these features when they ship. You can try the Python Bridge today by installing a Swift for TensorFlow toolchain.

(It is worth noting that since Python has a C API and Swift can call C, neither @dynamic Member Lookup or @dynamic Callable required to implement it the actual Swift-Python bridge, but they make the resulting Swift syntax much better. Without them there is no chance anyone would use the bridge because the syntax would be unacceptable verbose for something more than a few isolated code lines.)

Although Python has been the main focus of the team who worked with the proposal, will interop teams with other dynamic languages ​​like Ruby or JavaScript could also benefit from it.

Other Usage Cases

And dynamic member notifications are also not limited to this usage. Any type currently having a string-based subscriber style API can be converted to dynamic member lookup style.

Example: Highly Written JSON

SE-0195 uses a heavily written JSON type as an example that I would like to repeat here with minor changes. Suppose we have one enum to represent JSON data:

  enum    JSON    {
      case    number   (  Double ) 
      case [19659056] string   (  String ) 
      case    series   ([  JSON- ]) 
      case    dictionary   (   String :    JSON ]) 
} 

(We only handle a subset of valid JSON for the example, please add support for Boolean values ​​and null. )

To make it easier to extract data in a JSON value, we can provide some accessors and two subscriptions - one that takes an Int for indexing in a matrix and one who takes a

 ] String 

  for drilling into a dictionary: 

  extension    JSON    {
      var    numberValue :    Double    Double 

  ?    {
         ] guard    case  .             n )    =          Otherwise    {
              Return    nil 
        } 
          ] return    n 
    } 

      var    stringValue :?.     [19659000] 
          cover    case      string   ([19659098] la    s )    =         
              return    nil 
        } 
          return    s 
    } 

      lowered   (  index :    ] Int )?    ->    JSON-      {
          cover    case  .  ]   (  With    arr )    =     
             arr .   indexes [19659022].   contains   (  index )    otherwise 
         {
              return    nil 
        } 
          return    ank   [  ] index ] 
    } 

      lowered   (  key :    String )?    ->    JSON-      {
          cover    case [19659017].               dict )    =    self    other    {
              return    nil 
        } 
      ] return    dict   [  key ] 
    } 
} 

Note that your subscriptions return optional JSON values. This allows us to drill down to nested JSON data using optional chain:

  // [
  // {
  // "Name": {
  // "First": ". .. "19659032] //" last ":" ... "
  //} 
  //}, 
  // {
  // ... 
  //} [19659029] //] [19659219] json   [  0 ] [  "Name" ] [  "First" ] .   stringValue 

Now let's use @dynamicMemberLookup of our type. Implementation is exactly the same as for the strict subscription above:

  @dynamicMemberLookup 
  enum    JSON    {
      ... 

      subscription   (  dynamicMember    key :?.    String )    ->    JSON      {
          guard    case      dictionary [19659011]       dict )    =    even    otherwise    {
              return    nil 
        } 
          return    dict 
      19659011] [  key ] 
    } 
} 

This clears the syntax to drill into a JSON dictionary very nicely into this:

  json   [  ] ] .     .   first ? .   stringValue 

Choosing the Right Return Type

Please note that as writers of the type JSON we have full flexibility over the type of (s) of our dynamic member subscription (s). This gives us full freedom 2 how to handle failed spreads. Do you want your type to behave like a Dictionary back zero when a key can not be found? Make your return type optional. Do you prefer to capture invalid entry? Make the return type non-select and call fatalError in the implementation.

This is why the proposal emphasizes that dynamic member postings are completely safe despite the fact that name resolution occurs at runtime and can clearly fail - the point is that (a) the author of one type has full control over driving time behavior, and (b) the user of the type can derive how to handle the return value from the subscriber's return type.

As we saw, returns of the same type (as optional or not) will be a popular choice because it allows chaining. The Python Bridge also uses this pattern: each return type is again a PythonObject .

It's only syntactic sugar

It's important to understand that @dynamic Member Lookup isn It's not a kind of compiler magic. It only gives syntactic sugar, removes some square brackets and quotes from your code. The summons Uppercaser () .hello corresponds to Uppercaser () [dynamicMember: "hello"] in every way.

Is it worth it?

It brings me to the central question. Is Synthetic Sugar Worth It? Are you going to replace all your existing subscriptions with @Dynamic Memberships ? I do not think so.

Yes, the syntax is cleaner and more readable, but I would argue that this is at the expense of clarity in most cases. By concealing a fundamentally "uncertain" 3 string suggestion behind the apparently "secure" membership of membership by way of bulleted note, you can give the readers of the code the wrong impression that the compiler has been able to check your code for styles etc. 4

Syntax highlighting can help someone: Xcode can color declared members differently than dynamic lookup. But not everybody can use an editor with good syntax highlighting, and even Xcode can not help you if you accidentally enter a typing error in a dynamic member's lookup. (The same applies if you put a typo error in a normal string-based subscription, of course. But at least it is clear to readers of the code that this is a slightly risky operation.)

Does json [0]] have? .name. .first does it read much better for you than json [0]? ["name"]? ["first"] to be worth this disagreement? It does not matter to me.

Conclusion

Dynamic member postings are an indispensable feature for a small but important usage case where a pure syntax can create or destroy an API - namely dynamic language branching and possibly dynamic proxy / message forwarding.

With dynamic member posts, the Python community may not embrace Swift (or Swift Commmunity, Python). Without that, Swift would not even have a chance.

For all other applications, ask yourself if pure syntax is really much more readable than a normal subscription to be worth downsides. Keep in mind that dynamic membership is just syntactic sugar. It does not enable new functionality. In most situations, I think the answer should be "no".

If misused, dynamic member postings (and the associated "dynamic callable" proposal have the potential to fundamentally change what constitutes Swift code in the developer's mind. It also has the potential to open Swift up to other communities and let it grow to fields where there is no player at the moment. Let's work to make the latter a reality.

  1. Protocols Annotated with Dynamic Member Search Compile at the moment only if you specify a standard implementation for the dynamic membership subscription ↩︎ I use quotes for "unsafe" because as we saw, dynamic member notifications are not uncertain in Swift's definition of the word. However, it remains fundamentally a string-based lookup and prohibits refactoring and compiler control. ↩︎

  2. This argument has also been made under the proposal s review, most convincing of Matthew Johnson.

    Others suggested adding a seal (eg ^ as in value ^ ) to distinguish dynamic lookup from compiler-controlled lookup in the code. This was eventually rejected as obtrusive. Olivier Halligon explores how to recreate this behavior within the framework of the design provided with Swift 4.2, using a custom postfix operator. ↩︎


If you liked this article, I suggest you also like the Advanced Swift book I wrote with Chris Eidhof and Airspeed Velocity. The third edition is completely updated for Swift 4.

Advanced Swift is available as a DRM-free e-book and in print.




Source link