<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><blockquote type="cite" class="">On Nov 17, 2017, at 12:08 AM, Brent Royal-Gordon <<a href="mailto:brent@architechies.com" class="">brent@architechies.com</a>> wrote:<br class=""></blockquote><div><blockquote type="cite" class=""><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class="">On Nov 16, 2017, at 1:44 PM, Paul Cantrell <<a href="mailto:paul@bustoutsolutions.com" class="">paul@bustoutsolutions.com</a>> wrote:</div></blockquote><blockquote type="cite" class=""><br class=""></blockquote><blockquote type="cite" class=""><div class=""><div class="">In the example you bring up:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class="">you can write `def someMember` and `def someMember= (newValue)`)</div></div></blockquote><br class=""></div><div class="">…there is no overloading. The = is _part of the method name_, i.e. there is a `someMember` method and a `someMember=` method.</div></div></blockquote><div class=""><br class=""></div><div class=""><div class="">You're right—I was speaking imprecisely when I used the word "overloading". Nevertheless, Ruby doesn't quite directly interpret `x.someMember = y` as `x.someMember= (y)`—it supports operators like `+=`, which do a getter-operation-setter dance.</div></div></div></div></div></div></div></blockquote><div><br class=""></div><div>True, `foo.bar += x` is just sugar for `foo.bar = foo.bar + x`, which in turn is sugar for `foo.bar=(foo.bar.+(y))`. But does this pose a problem for a Swift → Ruby bridge? Presumably such a bridge would:</div><div><br class=""></div><div>1. define a Swift += operator on RubyObj that does the same expansion as above, and</div><div><br class=""></div><div>2. forward the property setter for `bar` to Ruby as a `bar=` invocation.</div><div><br class=""></div><div>I don’t think there’s a problem here. But maybe I’m missing it?</div><div><div><div class=""><br class=""></div></div></div><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div class="">The following are equivalent:</div><div class=""><br class=""></div> foo.bar = 3 # just sugar</div><div class=""> foo.bar=(3)<br class=""><div class=""> foo.send("bar=", 3)</div><div class=""><br class=""></div><div class="">Ruby allows ?, !, and = as the last char of method names, and AFAIK other than the special sugar around setters, they are just parts of the method name with no further semantic significance.</div></div></blockquote><div class=""><br class=""></div><div class="">You're correct that, with this design, you could access Ruby accessors from Swift with syntax like:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>myObj.name()</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>myObj.`name=`("Chris")<span class="Apple-tab-span" style="white-space: pre;">                </span>// If we loosened the characters allowed in backticks</div><div class=""><br class=""></div><div class="">My point is simply that this is a poor mapping, for much the same reason `dog["add_trick"].call(…)` is a poor mapping. It's technically correct and exposes the functionality, but it's awkward and doesn't match the user's mental model.</div></div></div></div></div></div></blockquote><div><br class=""></div><div>Well sure, that would be awkward, but doesn’t Chris’s proposed protocol allow idiomatic access to work too?</div><div><br class=""></div><div><div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(88, 126, 168); background-color: rgb(255, 255, 255);" class=""><span style="color: #000000" class=""> </span><span style="color: #323e7d" class="">extension</span><span style="color: #000000" class=""> </span>RubyObj<span style="color: #000000" class="">: </span>DynamicMemberLookupProtocol<span style="color: #000000" class=""> {</span></div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: rgb(50, 62, 125);" class="">subscript</span>(dynamicMember member: <span style="color: rgb(88, 126, 168);" class="">String</span>) -> <span style="color: rgb(88, 126, 168);" class="">RubyObj</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">get</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">return </span><font color="#858884" class="">/* …some deferred callable thing… */</font></div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">set</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> RubyObject_send(rubyObject, method: <span style="color: #853e64" class="">"</span>\<span style="color: #853e64" class="">(</span>member<span style="color: #853e64" class="">)="</span>, args: [newValue])</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div class=""><br class=""></div></div><div class="">That would make `myObj.name = “Chris"` in Swift work as expected. (This is using a made-up RubyObj binding, but I think it makes the point.)</div><div class=""><br class=""></div><div class="">What _is_ a bit nasty, as you pointed out, is that `myObj.name` does _not_ work as expected. Instead of returning the name, it returns a closure that returns the name when called. Yuck.</div><div class=""><br class=""></div><div class="">In Ruby, `myObj.name()` is equivalent to `myObj.name`, and either works. In Swift, I don’t see that it’s possible to make both work with Chris’s proposal.</div></div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><div class="">If we had separate subscripts for methods and properties, then the property subscript could immediately call the appropriate getters and setters, while the method subscript could return a ready-to-call `Method` object.</div></div></div></div></div></blockquote><div><br class=""></div><div>Hmm, yes, I think you’re right. It seems like that would fix the nastiness above. Better yet, why bother with the ready-to-call Method-like object? Just call it! A Ruby binding with separate property and method handling would then look like this:</div><div><br class=""></div><div><div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(88, 126, 168); background-color: rgb(255, 255, 255);" class=""><span style="color: #000000" class=""> </span><span style="color: #323e7d" class="">extension</span><span style="color: #000000" class=""> </span>RubyObj<span style="color: #000000" class="">: </span>DynamicMemberLookupProtocol<span style="color: #000000" class=""> {</span></div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">func</span> callDynamicMethod(dynamicMethod method: <span style="color: #587ea8" class="">String</span>, args: [<span style="color: #587ea8" class="">RubyObject</span>]) -> <span style="color: #587ea8" class="">RubyObj</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> get {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">return</span> RubyObject_send(rubyObject, method: member, args: args)</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><p style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255); min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">subscript</span>(dynamicMember member: <span style="color: #587ea8" class="">String</span>) -> <span style="color: #587ea8" class="">RubyObj</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">get</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">return</span> RubyObject_send(rubyObject, method: member, args: [])</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #323e7d" class="">set</span> {</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> RubyObject_send(rubyObject, method: <span style="color: #853e64" class="">"</span>\<span style="color: #853e64" class="">(</span>member<span style="color: #853e64" class="">)="</span>, args: [newValue])</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-stretch: normal; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div></div></div><div><br class=""></div><div>When Swift sees myObj.name, it uses the getter subscript. When Swift sees myObj.name(), it uses the method invocation. Both work in Swift just as they do in Ruby.</div></div><br class=""><div class="">Note that for this to work, the Swift compiler itself has to tell the Ruby binding whether the member access looks like a property or method access.</div><div class=""><br class=""></div><div class="">• • •</div><div class=""><br class=""></div><div class="">I confess I didn’t follow every detail of your “wall of text,” but I did find this example compelling:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div style="font-family: HelveticaNeue;" class="">That would work for arbitrary fixed sets of properties, but we can extend this to wrapper types. Imagine you want to write the `UIAppearance` class I mentioned previously. (Actually, we'll call it `UIAppearanceProxy` to avoid names already used in UIKit.) Your basic structure looks like this:</div><div style="font-family: HelveticaNeue;" class=""><br class=""></div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>class UIAppearanceProxy<View: UIAppearance> {</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>let containers: [UIAppearanceContainer.Type]</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>let traits: UITraitCollection</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>var properties: [PartialKeyPath<View>: Any] = [:]</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div style="font-family: HelveticaNeue;" class=""><br class=""></div><div style="font-family: HelveticaNeue;" class="">Now, to make all properties of the `View` class settable on this class, you can overload the `additionalProperty` subscript to accept `View` keypaths:</div><div style="font-family: HelveticaNeue;" class=""><br class=""></div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>extension UIAppearanceProxy {</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>subscript<T>(additionalProperty viewKeyPath: KeyPath<View, T>) -> T? {</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>get {</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                                </span>return properties[viewKeyPath] as! T?</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>}</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>set {</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                                </span>properties[viewKeyPath] = newValue</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>}</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>}</div><div style="font-family: HelveticaNeue;" class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div></blockquote></div><div class=""><div style="font-family: HelveticaNeue;" class=""><br class=""></div></div><div style="font-family: HelveticaNeue;" class="">That’s an impressive level of dynamism for a statically type-checked idea. Awful lot of complexity to bite off, though!</div><div class=""><br class=""></div><div class="">Cheers,</div><div class=""><br class=""></div><div class="">Paul</div><div class=""><br class=""></div></body></html>