<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=""><br class=""><div><blockquote type="cite" class=""><div class="">On Nov 27, 2017, at 3:27 PM, Tony Allevato &lt;<a href="mailto:tony.allevato@gmail.com" class="">tony.allevato@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div dir="ltr" class="">On Mon, Nov 27, 2017 at 12:49 PM Matthew Johnson &lt;<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.com</a>&gt; wrote:<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><blockquote type="cite" class=""><div class="">On Nov 27, 2017, at 1:24 PM, Tony Allevato &lt;<a href="mailto:tony.allevato@gmail.com" target="_blank" class="">tony.allevato@gmail.com</a>&gt; wrote:</div><br class="m_-7899327579131506277Apple-interchange-newline"><div class=""><div dir="ltr" class=""><br class=""><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Mon, Nov 27, 2017 at 11:12 AM Matthew Johnson via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><blockquote type="cite" class=""><div class="">On Nov 27, 2017, at 12:50 PM, Douglas Gregor &lt;<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>&gt; wrote:</div><br class="m_-7899327579131506277m_-2480070432859288515Apple-interchange-newline"><div class=""><div style="word-wrap:break-word;line-break:after-white-space" class=""><div dir="auto" style="word-wrap:break-word;line-break:after-white-space" class=""><br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class="">On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_-7899327579131506277m_-2480070432859288515Apple-interchange-newline"><div class=""><div style="word-wrap:break-word" class=""><div class="">As mentioned in my prior message, I currently have a PR open to update the generics manifesto (<a href="https://github.com/apple/swift/pull/13012" target="_blank" class="">https://github.com/apple/swift/pull/13012</a>).&nbsp; I removed one topic from that update at Doug Gregor’s request that it be discussed on the list first. &nbsp;</div><div class=""><br class=""></div><div class="">The idea is to add the ability to make default arguments conditional (i.e. depend on generic constraints).&nbsp; It is currently possible to emulate conditional default arguments using an overload set.&nbsp; This is verbose, especially when several arguments are involved.&nbsp; Here is an example use case using the overload method to emulate this feature:</div><div class=""><br class=""></div><div class="">```swift</div><div class="">protocol Resource {</div><div class="">&nbsp; associatedtype Configuration</div><div class="">&nbsp; associatedtype Action</div><div class="">}</div><div class="">struct ResourceDescription&lt;R: Resource&gt; {</div><div class="">&nbsp; func makeResource(with configuration: R.Configuration, actionHandler: @escaping (R.Action) -&gt; Void) -&gt; R {</div><div class="">&nbsp; &nbsp; // create a resource using the provided configuration</div><div class="">&nbsp; &nbsp; // connect the action handler</div><div class="">&nbsp; &nbsp; // return the resource</div><div class="">&nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension ResourceDescription where R.Configuration == Void {</div><div class="">&nbsp; func makeResource(actionHandler: @escaping (R.Action) -&gt; Void) -&gt; R {</div><div class="">&nbsp; &nbsp; return makeResource(with: (), actionHandler: actionHandler)</div><div class="">&nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension ResourceDescription where R.Action == Never {</div><div class="">&nbsp; func makeResource(with configuration: R.Configuration) -&gt; R {</div><div class="">&nbsp; &nbsp; return makeResource(with: configuration, actionHandler: { _ in })</div><div class="">&nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension ResourceDescription where R.Configuration == Void, R.Action == Never {</div><div class="">&nbsp; func makeResource() -&gt; R {</div><div class="">&nbsp; &nbsp; return makeResource(with: (), actionHandler: { _ in })</div><div class="">&nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">```</div><div class=""><br class=""></div><div class="">Adding language support for defining these more directly would eliminate a lot of boilerplate and reduce the need for overloads. </div></div></div></blockquote><div class=""><br class=""></div><div class="">If one could refer to `self` in a default argument (which is not a big problem), you could turn the default into a requirement itself… although it doesn’t *quite* work with your example as written because it would always need to be implemented somehow:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div style="word-wrap:break-word;line-break:after-white-space" class=""><div class="">protocol Resource {</div><div class="">&nbsp; associatedtype Configuration</div><div class="">&nbsp; associatedtype Action</div></div></blockquote>&nbsp; &nbsp; func defaultConfiguration() -&gt; Configuration</div><div class="">&nbsp; &nbsp; func defaultHandler() -&gt; ((R.Action) -&gt; Void)<br class=""><blockquote type="cite" class=""><div style="word-wrap:break-word;line-break:after-white-space" class=""><div class="">}</div></div></blockquote></div></div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap:break-word" class=""><div class=""><div class="">This won’t work on its own for this use case because there is only a valid default in relatively narrow (but common) cases.&nbsp; For most values of Configuration and Action an argument must be provided by the caller.&nbsp; Xiaodi’s proposed syntax is the best fit (so far) for the use case I had in mind. &nbsp;</div></div></div></blockquote><div class=""><br class=""></div><div class="">Out of curiosity, what part of my proposed syntax misses the mark for your use case?</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap:break-word" class=""><div class=""><div class="">I think it’s important that we be clear about when a default is and is not available.&nbsp; Your syntax makes the availability of a default non-local.</div></div></div></blockquote><div class=""><br class=""></div><div class="">That's certainly true. It can be mitigated somewhat by placing the definitions as close to the declaration as possible, but it's still up to the user to do the right thing here:</div><div class=""><br class=""></div><div class="">```</div><div class="">struct ResourceDescription&lt;R: Resource&gt; {</div><div class="">&nbsp; func defaultConfiguration() -&gt; Void { return () }</div><div class="">&nbsp; func defaultActionHandler() -&gt; (Never) -&gt; Void { return _ in }</div><div class="">&nbsp; func makeResource(...) {</div><div class="">&nbsp; &nbsp; ...</div><div class="">&nbsp; }</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">It's worth noting that this problem does exist somewhat in the language today, if you replace "makes the availability of a default non-local" with "makes the value of a default non-local”.</div></div></div></div></blockquote><div><br class=""></div><div>Non-locality of the value doesn’t bother me. &nbsp;The availability of the default is part of the API contract, the specific value need not be.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">&nbsp;<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">The parts that I think are somewhat unfortunate are losing a small bit of reference locality and the introduction of a new operator to distinguish between "default argument not present if no match found" vs. “com pile time error", definitely. However, one unfortunate trend I've noticed when new features are proposed is that there's a tendency to end up with "let's invent @yet_another_attribute" and that just doesn't seem scalable or clean if we can strive to better integrate it into the syntax of the language.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Xiaodi acknowledged that his suggestion is a bit clunky but I’m not sure we can do better while preserving clarity and making the defaults part of the function declaration.&nbsp; If we added this feature it would be relatively advanced and usually used by library developers.&nbsp; While I would be pleased with more elegant syntax I’m not sure there is a better solution.&nbsp; I prefer semantic clarity to syntactic elegance.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Why not both? Hopefully we can get some more input from folks who might have other ideas about how to bring the two ideas together.</div></div></div></div></blockquote><div><br class=""></div><div>Of course I agree! &nbsp;It would be wonderful if someone figured out how to accomplish that.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">For me, grafting on a second completely different default value syntax for functions is hard to swallow, especially if folks feel that it's clunky right out of the gate. (Especially if we're talking about parsing entire expressions—and closure expressions—out of an attribute body. How deep does that need to go?)</div></div></div></div></blockquote><div><br class=""></div><div>I can sympathize with that point of view. &nbsp;The syntactic problem is that we don’t have an obvious way to attach constraints to default arguments inline, especially if different defaults were available for the same parameter under different constraints. &nbsp;We could place an attribute on the default itself like this:</div><div><br class=""></div><div>`configuration: Configuration = @where((), Configuration == Void), @where(R.defaultConfiguration, R: DefaultConfigurable)`</div><div><br class=""></div><div>However, this puts a lot of noise inline. &nbsp;I think Xiaodi’s suggestion of attaching an attribute to the function itself would lead to more readable declarations.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">My thoughts on this particular problem are either that it's a relatively advanced problem that comes up rarely enough that using extensions isn't a significant problem and adding a special annotation isn't worth the increase in compiler maintenance/complexity, or it's more common than we think and thus warrants a syntax better integrated with the rest of the language. It's probably hard to say for sure where this problem falls among those two, but my guess it's that it's closer to the former than the latter. If that's the case, I'd rather stick with what we have today rather than create an ad hoc annotation for a rare use case.</div></div></div></div></blockquote><div><br class=""></div><div>That’s a reasonable point of view. &nbsp;I agree that it’s hard to say which case this falls under in general. &nbsp;</div><div><br class=""></div><div>It’s worth noting that any parameter of an unconstrained generic type which will often be Void in practice is a parameter for which this feature leads to cleaner call-site code. &nbsp;It is often reasonable to provide nil as a default for Optional parameters. &nbsp;I don’t know of a way to identify whether an unconstrained T is an Optional but if that was possible this would be another relatively common use case for the feature.&nbsp;</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">I'm particularly interested in this because I hit a use case that's similar to yours in my own code base. I wanted a generic type that could be instantiated with a disjoint type set—either a RawRepresentable whose RawValue is Int, or an Int itself. This could have been solved by having Int retroactively conform to RawRepresentable, but doing that to a fundamental built-in type Feels Dirty<span id="cid:2122@goomoji.gmail">&lt;emoji_u2122.png&gt;</span>, so I made it work by not constraining the generic type at all and moving the public initializers to constrained extensions so that the only way you could *instantiate* the type is if you have a correct type argument, and those initializers pass through to an internal one, sending along a closure that does the correct transformation:&nbsp;<a href="https://github.com/allevato/icu-swift/blob/master/Sources/ICU/RuleBasedBreakCursor.swift#L208" target="_blank" class="">https://github.com/allevato/icu-swift/blob/master/Sources/ICU/RuleBasedBreakCursor.swift#L208</a></div><div class=""><br class=""></div><div class="">With your proposed addition, I could hoist those closures into default arguments based on the constraints instead of adding extensions.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap:break-word" class=""><div class=""><div class="">IIUC you would still need the extensions because you only want to expose an initializer for two specific type arguments.&nbsp; Conditional default arguments would only work if you didn’t mind exposing an initializer for other type arguments (while requiring an explicit ruleStatusFactory for other type arguments).</div></div></div></blockquote><div class=""><br class=""></div><div class="">True, and I wouldn't mind that—the Int and RawRepresentable versions would essentially be conveniences in that case.</div></div></div></div></blockquote><div><br class=""></div><div>You can have that today if you expose the initializer you're forwarding to.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">That being said, I don't find the extension-based approach *that* restrictive, and it kind of makes sense from the point of view of "this overload only exists for this type under a particular constraint". I wonder how often this comes up that combinatorial explosion is truly harmful.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap:break-word" class=""><div class=""><div class="">The extension based approach is workable, it’s just verbose and clunky and I suspect it results in useful default arguments being omitted in some cases.&nbsp; I’m sure the combinatorial problem is relatively rare but it leads to an overload set that is difficult to maintain properly when it happens.&nbsp; That said, if the community decides this is a niche problem that isn’t worth language support to solve I would understand that decision.</div><div class=""><br class=""></div><div class="">To be honest, this thread has received more positive feedback than I expected (I expected the generalized supertype thread to get more traffic).&nbsp; That indicates to me that the feature might not be quite as niche as I suspected.&nbsp; That is one of the things I hoped to learn from the thread.</div></div></div><div style="word-wrap:break-word" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><div class=""><br class=""></div><div class="">That said, the ability to refer to self in default arguments is complementary as it would expand the cases where conditional default arguments could be provided.&nbsp; For example, in the example above it would allow a resource to provide a nontrivial default configuration.</div></div></div><div style="word-wrap:break-word" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap:break-word;line-break:after-white-space" class=""><div dir="auto" style="word-wrap:break-word;line-break:after-white-space" class=""><div class=""><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div style="word-wrap:break-word" class=""><div class="">&nbsp;Doug mentioned that it may also help simplify associated type inference (<a href="https://github.com/apple/swift/pull/13012#discussion_r152124535" target="_blank" class="">https://github.com/apple/swift/pull/13012#discussion_r152124535</a>).</div></div></div></blockquote><br class=""></div><div class="">Oh, I thought this was something related to choosing a defaults for associated types, which might have helped with my current associated-type-inference quandary. The topic you actually wanted to discuss is disjoint (sorry).</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap:break-word" class=""><div class=""><div class="">I was wondering how it was related and wondered if it was somehow due to reduction in the size of the overload set.&nbsp; If you have any ideas on changes that might help guide the inference algorithm somehow please start a new thread.&nbsp; Even if you’re only able to describe the challenges it might be worth a thread if it’s possible others might have useful ideas.&nbsp; Improving the reliability and predictability of inference is a very important topic!</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap:break-word;line-break:after-white-space" class=""><div dir="auto" style="word-wrap:break-word;line-break:after-white-space" class=""><div class=""><br class=""></div><div class=""><span class="m_-7899327579131506277m_-2480070432859288515Apple-tab-span" style="white-space:pre-wrap">        </span>- Doug</div><div class=""><br class=""></div><br class=""></div></div></div></blockquote></div><br class=""></div>_______________________________________________<br class="">
swift-evolution mailing list<br class="">
<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a><br class="">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="">
</blockquote></div></div>
</div></blockquote></div></div></blockquote></div></div>
</div></blockquote></div><br class=""></body></html>