<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div><blockquote type="cite" class=""><div class="">On Jan 19, 2016, at 2:46 PM, John McCall via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class="">On Jan 13, 2016, at 2:07 PM, Joe Groff via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><div class=""><div class="">Thanks everyone for the first round of feedback on my behaviors proposal. I've revised it with the following changes:<br class=""><br class="">- Instead of relying on mapping behaviors to function or type member lookup, I've introduced a new purpose-built 'var behavior' declaration, which declares the accessor and initializer requirements and provides the storage and behavior methods of the property. I think this gives a clearer design for authoring behaviors, and allows for a more efficient and flexible implementation model.<br class="">- I've backed off from trying to include 'let' behaviors. As many of you noted, it's better to tackle immutable computed properties more holistically than to try to backdoor them in.<br class="">- I suggest changing the declaration syntax to use a behavior to square brackets—'var [behavior] foo'—which avoids ambiguity with destructuring 'var' bindings, and also works with future candidates for behavior decoration, particularly `subscript`.<br class=""></div></div></blockquote><div class=""><br class=""></div></div>Syntax comments:<div class=""><br class=""></div><div class="">I still think these feel attribute-like to me, but if we’re not just going to use @lazy — and I agree that that does have some problems —I’m fine with [lazy].<br class=""><div class=""><br class=""></div><div class="">"var behavior" is really weird to me, and the <T> doesn’t seem to fit and is pretty redundant in the common case. How about this:</div></div><div class=""><br class=""></div><div class=""><font face="Menlo" class=""> "behavior" var-or-let "[" identifier-list "]" (identifier | </font><span style="font-family: Menlo;" class="">"</span><font face="Menlo" class="">_</font><span style="font-family: Menlo;" class="">")</span><span style="font-family: Menlo;" class=""> ":" identifier (</span><span style="font-family: Menlo;" class="">"="</span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">identifier)? </span><span style="font-family: Menlo;" class="">("where" generic-requirement-list)?</span></div><div class=""><br class=""></div><div class="">So, for example,</div><div class=""><font face="Menlo" class=""> behavior var [lazy] _ : T where T : IntegerLiteralConvertible { … }</font></div><div class=""><br class=""></div><div class="">This is definitely taking the idea of “this is basically a macro” and running with it. Think of the stuff between “behavior” and the optional “where” as being a pattern for the declaration. So this pattern would match:</div><div class=""><font face="Menlo" class=""> var [lazy] x: Int</font></div><div class="">but not:</div><div class=""><font face="Menlo" class=""> let [lazy] x: Int</font></div><div class="">or:</div><div class=""><font face="Menlo" class=""> var [lazy] x : Int = foo()</font></div><div class=""><br class=""></div><div class="">The behavior list has to match exactly (or maybe as sets?).</div><div class=""><br class=""></div><div class="">The property name, if bound, expands to a string literal within the behavior.</div><div class=""><br class=""></div><div class="">The type name is always a generic parameter. This interferes with the ability to make a pattern that only matches a concrete type, but I think that’s okay.</div><div class=""><br class=""></div><div class="">The initializer name, if bound, expands to the original expression within the behavior. Maybe it should be coerced to type T first? Not sure.</div></div></div></blockquote><div><br class=""></div>I feel like there are two possible implementation models here: type instantiation and member instantiation. The type instantiation model has the advantage of allowing generic programming over the complete lazy type, but probably means we end up creating a lot of redundant stuff, some of which we’d have trouble eliminating. The member instantiation model is tricky and more limiting in terms of what you can do polymorphically directly with behavior types, but (1) I don’t think that’s crucial and (2) it does seem implementable.<br class=""><div><br class=""></div></div>The member-instantiation model looks like this:<div class=""><br class=""></div><div class="">Conceptually, a behavior declaration creates a generic behavior value type:</div><div class=""> struct [weak,lazy]<T> {</div><div class=""> ...</div><div class=""> }</div><div class=""><br class=""></div><div class="">That behavior declaration always contains an implicit “value” member:</div><div class=""> var value : T {</div><div class=""> ...</div><div class=""> }</div><div class=""><br class=""></div><div class="">Any accessors in the behavior body are considered to be accessors of the value member. Everything else is just a member of the behavior type.</div><div class=""><br class=""></div><div class="">An expression within the main behavior declaration is “instantiated” if it refers to any of the bound macro parameters (other than the type T). A function body within the main behavior declaration is instantiated if:</div><div class=""> - it contains an instantiated expression,</div><div class=""> - it is an initializer, and the initializer expression of a stored property contains an instantiated expression,</div><div class=""> - it uses a function within the main behavior declaration that is instantiated, or</div><div class=""> - it uses an accessor within the main behavior declaration that is instantiated.</div><div class=""><br class=""></div><div class="">An instantiated member or accessor of the behavior type may not be used outside of the behavior body except as directly applied to a particular behavior object.</div><div class=""><br class=""></div><div class="">We can still separately type-check and even generate SIL for instantiated members. Type-checking works by saying that the property name is an opaque string literal and the initializer is an opaque r-value of type T; the type-checker would have to do the latter coercion for each use, but that seems better semantically and for type inference anyway. When an instantiated function was used from an uninstantiated context, SILGen would just create SIL builtins that fetch the name / initializer; instantiating would clone the body and replace those operations. The type checker would require that instantiated functions can only be used from appropriate contexts, and the SIL verifier and IRGen would do the same.</div><div class=""><br class=""></div><div class="">The behavior type must be nullary-initializable (possibly with an instantiated initializer). At the use site, the behavior is always expanded as:</div><div class=""> var _foo_behavior = [weak,lazy]<T>()</div><div class=""><br class=""></div><div class="">I’m not quite sure how extensions should work with initializers, because what I described above naturally allows behaviors to be overloaded based on the presence/absence of the initializer. Either that’s disallowed, or you have to write:</div><div class=""> extension var [weak,lazy] _ : T = _ { … }</div><div class="">to clarify that (1) you mean the behavior that allows an initializer but (2) you can’t actually use the initializer expression in your implementation, because the member-instantiation model definitely doesn’t support doing that.</div><div class=""><br class=""></div><div class="">John.</div></body></html>