<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Feb 10, 2016 at 2:00 PM, Douglas Gregor via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)">Hello Swift community,</p><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)">The review of SE-0030 &quot;Property Behaviors&quot; begins now and runs through February, 2016. The proposal is available here:</p><blockquote style="margin:0px 0px 16px;padding:0px 15px;color:rgb(119,119,119);border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><div style="margin-top:0px;margin-bottom:0px"><a href="https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-proposal.md" style="color:rgb(64,120,192);text-decoration:none;background-color:transparent" target="_blank">https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md</a></div></blockquote><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)">Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at</p><blockquote style="margin:0px 0px 16px;padding:0px 15px;color:rgb(119,119,119);border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><div style="margin-top:0px;margin-bottom:0px"><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="color:rgb(64,120,192);text-decoration:none;background-color:transparent" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)">or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:</p><blockquote style="margin:0px 0px 16px;padding:0px 15px;color:rgb(119,119,119);border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><p style="margin-top:0px;margin-bottom:16px">Proposal link:</p><blockquote style="margin:0px 0px 16px;padding:0px 15px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221)"><div style="margin-top:0px;margin-bottom:0px"><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md" target="_blank">https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md</a></div></blockquote><p style="margin-top:0px;margin-bottom:16px">Reply text</p><blockquote style="margin:0px;padding:0px 15px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221)"><div style="margin-top:0px;margin-bottom:0px">Other replies</div></blockquote></blockquote><h5 style="margin-top:1em;margin-bottom:16px;line-height:1.4;font-size:1em;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;background-color:rgb(255,255,255)"><a href="https://github.com/apple/swift-evolution#what-goes-into-a-review-1" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1.1;background-color:transparent" target="_blank"><u></u><u></u><u></u><u></u></a>What goes into a review?</h5><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)">The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:</p><ul style="padding:0px 0px 0px 2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><li>What is your evaluation of the proposal?</li></ul></div></blockquote><div>Would it be out-of-scope to propose extending this to functions?  I can think of several use-cases:</div><div><br></div><div>1.) Providing both static &amp; singleton accessors, for the convenience of users of your library.  In a library I&#39;ve been writing, I&#39;ve found that all my configuration calls take this form:</div><div><br></div><div>public static func useCoreData() -&gt; Self { return sharedInstance().useCoreData() }</div><div>public func useCoreData() { /* Code here; */ return self }</div><div><br></div><div>public static func useNotifications() -&gt; Self { return sharedInstance().useNotifications() }</div><div>public func useNotifications() { /* Code here; */ return self }</div><div><br></div><div>I&#39;d love to be able to do (using the ... syntax for the proposed splat operator being discussed in another thread):</div><div><br></div><div>public behavior func [StaticChainable] name(...ArgumentType) -&gt; ResultType {</div><div>  public static func name(...args) -&gt; Self { return sharedInstance().name(...args) }</div><div>  public func name() { body; return self }</div><div>}</div><div><br></div><div>public func [StaticChainable] useCoreData() { /* Code here */ }</div><div>public func [StaticChainable] useNotifications() { /* Code here */ }</div><div><br></div><div>2.) Providing both &quot;whole&quot; and &quot;parts&quot; versions of Composite Pattern methods.  Very often, you might have a tree structure where each element has the same type (or conforms to the same protocol).  You want a method with an optional first arg to specify which element you&#39;re operating on:</div><div><br></div><div>public func move(toElement: DOMElement, edge: EdgeType) { /* Code here */ }</div><div>public func move(childWithID id: String, toElement elem: DOMElement, edge: EdgeType {</div><div>  children[id].move(toElement: elem, edge: edge)</div><div>}</div><div><br></div><div>public func hide() { /* Code here */ }</div><div>public func hide(childWithID id: String) { children[id].hide() }</div><div><br></div><div>public func show() { /* Code here */}</div><div>public func show(childWithID id: String) { children[id].show() }</div><div><br></div><div>vs.</div><div><br></div><div>public behavior func [Composite] name(...ArgumentType) -&gt; ResultType where Self: HasChildren {</div><div>  public func name(...args) { body }</div><div>  public func name(childWithID id: String, ...args) { self.children[id].name(...args) }</div><div>}</div><div><br></div><div><div>public func [Composite] move(toElement: DOMElement, edge: EdgeType) { /* Code here */ }</div></div><div>public func [Composite] show() { /* Code here */ }</div><div><div>public func [Composite] hide() { /* Code here */ }</div></div><div><br></div><div>3.) Or you have a number of convenience functions that differ only in what helpers they invoke:</div><div><br></div><div>public func remove(elementAtIndex i: Index) { /* Code here */ }</div><div>public func remove(element: Generator.Element) { remove(elementAtIndex: indexOf(element)) }</div><div>public func remove(firstMatchingPredicate pred: Generator.Element throws -&gt; Bool) {</div><div>   remove(elementAtIndex: indexOf(pred))</div><div>}</div><div><br></div><div><div>public func insertAfter(elementAtIndex i: Index, newElement: Generator.Element) { /* Code here */ }</div><div>public func insertAfter(element: Generator.Element, newElement: Generator.Element) {</div><div>   remove(elementAtIndex: indexOf(element))</div><div>}</div><div>public func insertAfter(firstMatchingPredicate pred: Generator.Element throws -&gt; Bool, newElement: Generator.Element) {</div><div>   remove(elementAtIndex: indexOf(pred))</div><div>}</div></div><div><br></div><div><div><div>public func insertBefore(elementAtIndex i: Index, newElement: Generator.Element) { /* Code here */ }</div><div>public func insertBefore(element: Generator.Element, newElement: Generator.Element) {</div><div>   remove(elementAtIndex: indexOf(element))</div><div>}</div><div>public func insertBefore(firstMatchingPredicate pred: Generator.Element throws -&gt; Bool, newElement: Generator.Element) {</div><div>   remove(elementAtIndex: indexOf(pred))</div><div>}</div></div></div><div><br></div><div>4.) Sometimes you need to define a combination of functions and related properties, like in the Builder Pattern.  (Note the usage of &quot;return self&quot; here - that&#39;s why this is not done with straight settable properties, it&#39;s so the user can chain set calls together and avoid repeating the object name):<br></div><div><br></div><div>private var foo : String</div><div>public func setFoo(newValue: String) -&gt; Self { foo = newValue; return self }</div><div><br></div><div>private var bar : String</div><div>public func setBar(newValue: String) -&gt; Self { bar = newValue; return self }</div><div><br></div><div>private var baz : String</div><div>public func setBaz(newValue: String) -&gt; Self { baz = newValue; return self }</div><div><br></div><div>(Also this is a good opportunity to use the &quot;set once&quot; behavior mentioned in the proposal, but I won&#39;t repeat that code here.)</div><div><br></div><div>public behavior var [Builder] name: Type = initialValue {</div><div>  private var name: Type = initialValue</div><div>  public func set##name(newValue: Type) -&gt; Self { name = newValue; return self }</div><div>}</div><div><br></div><div>public var [Builder] foo: String</div><div>public var [Builder] bar: String</div><div>public var [Builder] baz: String</div><div><br></div><div>Notice that this is a &quot;var&quot; behavior rather than a &quot;func&quot; behavior...I&#39;d envision the keyword matching the type of entity you *decorate*, not the type of entity generated.  The declarations are public because the access modifier of the declaration should set a *maximum* visibility, not a minimum (i.e. you can always make individual instances of a behavior more restrictive).  ## is a token-pasting operation similar to the C preprocessor; it lets you construct a new name out of identifiers.  I&#39;d assume it would do automatic capitalization of the second argument to preserve camelCasing.</div><div><br></div><div>5.) Sometimes you need a whole suite of classes, functions, and variables.  For example, think of event handlers:</div><div><br></div><div>public behavior class [Event] name {</div><div>  public class name##Event : Event {</div><div>    body</div><div><br></div><div>    func preventDefault() { ... }</div><div>  }</div><div><br></div><div>  public protocol name##Listener {</div><div>    public func on##name(event: name##Event)</div><div>  }</div><div><br></div><div>  private var listeners = [name##Listener]()</div><div>  public func add##name##Listener(listener: name##Listener) {</div><div>    listeners.append(listener)</div><div>  }</div><div>  public func remove##name##Listener(listener: name##Listener) {</div><div>    listeners.remove(atIndex: listeners.indexOf(listener))</div><div>  }</div><div><br></div><div>  func fire##name##Event(event: name##Event) {</div><div>    for listener in listeners {</div><div>      listener(event)</div><div>    }</div><div>  }</div><div>}</div><div><br></div><div>class [Event] Click {</div><div>  let windowCoordinates: Point</div><div>  let viewCoordinates: Point</div><div>}<br></div><div><br></div><div>class [Event] TouchDown {</div><div>  struct Touch {</div><div>    let coordinates: Point</div><div>    let force: Double</div><div>  }</div><div>  let touches: [Touch]</div><div>}<br></div><div><br></div><div><br></div><div>The way I&#39;d envision this extension working is that the &quot;behavior&quot; keyword could be followed by any top-level entity: let/var, func, class, struct, enum.  The behavior then forms a template for entities to be generated and expanded in place.  &#39;name&#39; is bound to the name of the entity, &#39;body&#39; to the statement body from the grammar.  Functions can define individual arguments, types, and a result type, or can use whatever the outcome of the splatting discussion is as &quot;all remaining arguments, taken from the instantiating func declaration.&quot;  Classes can also bind the supertype and associated types.  I don&#39;t think it&#39;s necessary to be able to reach into the body and parse it for individual statements; at least, none of the use-cases I thought of require this.</div><div><br></div><div>All the scoping &amp; storage concerns are as per the original proposal.  Code within the body is not allowed to access code inside the behavior (beyond - possibly? - calling public member functions or properties that are defined by the behavior).  When generating names from behavior expansion, the compiler checks for name conflicts with any other symbols.  Code within the behavior is not allowed to inspect or destructure the body beyond the bindings already described, but may call other methods either within the behavior or within the hosting class if the class conforms to a protocol (as per the existing proposal).  (Perhaps some form of limited destructuring could be allowed as a generalization of accessor requirements; allow binding names to functions with the &#39;func&#39; keyword in classes, binding to variables with &#39;let&#39; in function bodies, etc.  Haven&#39;t really thought this through, may end up being too complicated.)</div><div><br></div><div>A couple features of the existing proposal can be removed under this generalization.  The syntax for calling a method of a property isn&#39;t necessary; instead of x.[lazy].clear, define &quot;func clear##name&quot; inside the behavior and call it as clearX().  (This also eliminates the need to have debates over what that syntax should be. :-))  It also lifts the restriction on &#39;let&#39; parameters, as these are just another top-level entity that could be expanded to a set of other entities.  They wouldn&#39;t allow accessors or get/set blocks, however; the purpose of this would not be to allow computed &#39;let&#39; properties, it would be to fold in additional functionality into these declarations.  One could imagine libraries that allow clients to auto-delegate all method calls in a protocol to a sub-object, for example:</div><div><br></div><div>public behavior let [DelegateCNContactPicker] name: Type = initialValue</div><div>      where Type: CNContactPickerDelegate, Self: CNContactPickerDelegate,  {</div>  public func contactPickerDidCancel(...args) { name.contactPickerDidCancel(...args) }<br>  public func contactPicker(...args) { name.contactPicker(...args)</div><div class="gmail_quote">  // Should it include overloads implicitly with ...args?  Or should each be overridden explicitly?<br><div>}</div><div><br></div><div>and then its use:</div><div><br></div><div>class MyViewController: CNContactPickerDelegate {</div><div>  private let [DelegateCNContactPicker] myObjectThatReallyHandlesIt = MyContactPickerDelegate()</div><div>  // No forwarding methods needed, and no need to expose myObjectThatReallyHandlesIt to the outside world.</div><div>}</div><div><br></div><div>I think this mechanism may also be able to subsume default methods on protocols - instead of using an extension, you could use a behavior with a protocol constraint.  I haven&#39;t really thought through the implications of this, though, in particular the possibility of protocols with storage.  May not be possible, in which case we wouldn&#39;t want to allow behaviors on protocols.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><ul style="padding:0px 0px 0px 2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><li>Is the problem being addressed significant enough to warrant a change to Swift?</li></ul></div></blockquote><div>Yes.  There are a number of use-cases here, and it generalizes several special-case features that are currently baked into the compiler.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><ul style="padding:0px 0px 0px 2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><li>Does this proposal fit well with the feel and direction of Swift?</li></ul></div></blockquote><div>I think so, but I&#39;ll let people with a better sense of the feel and direction of Swift make that decision.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><ul style="padding:0px 0px 0px 2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><li>If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?</li></ul></div></blockquote><div>It reminds me a lot of Lisp macros, and in particular the ability to define many entities at once or intercept the definition of those entities (used to good effect in CLOS).  This is one of the best features of Lisp; it lets you eliminate much of the boilerplate that shows up in production-quality libraries for Java, C#, C++, etc.</div><div><br></div><div>Behaviors let you side-step many of the hygiene/gensym issues of Lisp because they can&#39;t appear in unrestricted contexts the way Lisp macros can.  Even generalized to allow function &amp; class bodies, the set of symbols that may be visible inside the behavior body is limited to keywords &amp; argument lists in the language definition itself; you don&#39;t need to build a whole symbol table to see which symbols are visible inside the behavior, and you don&#39;t need to worry about inserting arbitrary blocks of code into arbitrary insertion points.  This limits the flexibility of behaviors a bit (eg. you could never do something like loop or destructuring-bind as a behavior), but it still seems like they cover a lot of the most annoying boilerplate cases.</div><div><br></div><div>It&#39;s also somewhat similar to aspect-oriented programming, and to Python decorators.  Seems to be both more and less powerful than these - more powerful in that you can define multiple get/set/accessors at once and scope any code required by them, and less powerful because as it currently stands you can&#39;t intercept functions.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><ul style="padding:0px 0px 0px 2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:&#39;Helvetica Neue&#39;,Helvetica,&#39;Segoe UI&#39;,Arial,freesans,sans-serif,&#39;Apple Color Emoji&#39;,&#39;Segoe UI Emoji&#39;,&#39;Segoe UI Symbol&#39;;font-size:16px;background-color:rgb(255,255,255)"><li>How much effort did you put into your review? A glance, a quick reading, or an in-depth study?</li></ul></div></blockquote><div><br></div><div>Read through the proposal fully, skimmed some of the responses, wrote up a lot of examples.</div></div></div></div>