{"id":21983,"date":"2018-06-29T16:02:31","date_gmt":"2018-06-29T20:02:31","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=21983"},"modified":"2018-07-18T14:48:34","modified_gmt":"2018-07-18T18:48:34","slug":"exploring-dynamicmemberlookup","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2018\/06\/29\/exploring-dynamicmemberlookup\/","title":{"rendered":"Exploring @dynamicMemberLookup"},"content":{"rendered":"<p><a href=\"http:\/\/alisoftware.github.io\/swift\/exploration\/2018\/06\/14\/exploring-dynamicmemberlookup\/\">Olivier Halligon<\/a>:<\/p>\n<blockquote cite=\"http:\/\/alisoftware.github.io\/swift\/exploration\/2018\/06\/14\/exploring-dynamicmemberlookup\/\"><p>This proposal had some controversy, and one thing I didn&rsquo;t personally like on this new feature is that it meant that typed annotated with <code>@dynamicMemberLookup<\/code> 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&rsquo;t know at compile-time.<\/p><p>But this also meant that once the type was annotated with <code>@dynamicMemberLookup<\/code>, you wouldn&rsquo;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.<\/p><p>[&#8230;]<\/p><p>The solution is actually quite short to implement. We&rsquo;ll create a <code>^<\/code> postfix operator that will wrap the instance it&rsquo;s applied on into some proxy object that is the one being <code>@dynamicMemberLookup<\/code>. That proxy object will just wrap the dictionary we applied <code>^<\/code> on, and on dynamic lookup, will search the key in the dictionary to return the corresponding value (if it exists).<\/p><p>[&#8230;]<\/p><p>In the playground attached below, I&rsquo;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 <code>^<\/code> calls)<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/oleb.net\/blog\/2018\/06\/dynamic-member-lookup\/\">Ole Begemann<\/a> (<a href=\"https:\/\/twitter.com\/olebegemann\/status\/1010565507135819777\">tweet<\/a>):<\/p>\n<blockquote cite=\"https:\/\/oleb.net\/blog\/2018\/06\/dynamic-member-lookup\/\"><p>Here are some non-obvious things you can do with <code>@dynamicMemberLookup<\/code>:<\/p>\n<ul>\n  <li>\n    <p><strong>Provide a setter in addition to the getter.<\/strong> This allows assignments through dynamic member lookup, or passing the subscript expression <a href=\"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Functions.html#ID173\"><code>inout<\/code><\/a>. For an example, check out <a href=\"https:\/\/twitter.com\/dgregor79\">Doug Gregor&rsquo;s<\/a> wrapper for <a href=\"https:\/\/gist.github.com\/DougGregor\/68259dd47d9711b27cbbfde3e89604e8\">reading and writing environment variables<\/a>.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Choose any return type.<\/strong> The subscript must have a single parameter, which can be any type that conforms to <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/expressiblebystringliteral\"><code>ExpressibleByStringLiteral<\/code><\/a> but will likely be a <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/string\"><code>String<\/code><\/a> 99&#x202F;% of the time. The return type, though, can be anything you want, including generic parameters.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Provide multiple overloads of the subscript with different return types.<\/strong> 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.<\/p>\n  <\/li>\n<\/ul>\n<p>Here are some things you can&rsquo;t do:<\/p>\n<ul>\n  <li>\n    <p><strong>&ldquo;Hide&rdquo; a declared member of a type.<\/strong> Declared properties always take precedence in the type checker over dynamic member lookup.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Retroactively support dynamic member lookup for a type you don&rsquo;t control.<\/strong> The attribute only works on the original type declaration.<\/p>\n  <\/li>\n<\/ul>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/clattner_llvm\/status\/1010556675713949702\">Chris Lattner<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/clattner_llvm\/status\/1010556675713949702\">\n<p>Nice summary, including a good descriptions of potential caveats.  One thing that I haven&rsquo;t explained yet is that the constexpr work can grow to support static diagnosis of bad members - the member names are constants after all!<\/p>\n<\/blockquote>\n\n<p>Previously: <a href=\"https:\/\/mjtsai.com\/blog\/2018\/06\/07\/swift-4-2\/\">Swift 4.2<\/a>.<\/p>\n\n<p>Update (2018-07-18): <a href=\"https:\/\/twitter.com\/schwa\/status\/1019454064839385089\">Jonathan Wight<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/schwa\/status\/1019454064839385089\">\n<p>Found a great using for Swift 4.2&rsquo;s @dynamicMemberLookup doing Python &ldquo;records&rdquo; (ala @kennethreitz ) style sqlite binding[&#8230;]<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Olivier Halligon: This proposal had some controversy, and one thing I didn&rsquo;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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2018-07-18T18:48:37Z","apple_news_api_id":"adff36d8-98d6-42eb-adff-a9878ce621d9","apple_news_api_modified_at":"2018-07-18T18:48:38Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAQ==","apple_news_api_share_url":"https:\/\/apple.news\/Arf822JjWQuut_6mHjOYh2Q","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[],"tags":[46,71,901],"class_list":["post-21983","post","type-post","status-publish","format-standard","hentry","tag-languagedesign","tag-programming","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/21983","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=21983"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/21983\/revisions"}],"predecessor-version":[{"id":22158,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/21983\/revisions\/22158"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=21983"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=21983"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=21983"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}