Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 4:11 PM, Slava Pestov  wrote:
> 
> 
> 
>> On Nov 28, 2017, at 1:35 PM, Matthew Johnson > > wrote:
>> 
 * the compiler doesn’t have to reason about an overload set which might 
 improve build times, etc
>>> 
>>> They’re effectively equivalent, because we still have to decide which 
>>> subset of the default arguments apply at a given call site by checking all 
>>> combinations of constraints.
>> 
>> Interesting.  Are there no advantages to the compiler that would be possible 
>> if an overload set was replaced with constrained default arguments?
>> 
> 
> Probably not. In general I’m wary of designing language features specifically 
> to speed up the type checker, since they make not have the intended effect or 
> even the opposite effect. We know the type checker implementation is not the 
> best possible implementation of a type checker — there is a lot we can 
> improve without changing the language.

That isn’t the motivation here.  I thought it might be an incidental benefit.  
If it isn’t the motivating use case still stands.  Of course it may or may not 
be sufficient to justify the feature.  :)

> 
> Slava

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Slava Pestov via swift-evolution


> On Nov 28, 2017, at 1:35 PM, Matthew Johnson  wrote:
> 
>>> * the compiler doesn’t have to reason about an overload set which might 
>>> improve build times, etc
>> 
>> They’re effectively equivalent, because we still have to decide which subset 
>> of the default arguments apply at a given call site by checking all 
>> combinations of constraints.
> 
> Interesting.  Are there no advantages to the compiler that would be possible 
> if an overload set was replaced with constrained default arguments?
> 

Probably not. In general I’m wary of designing language features specifically 
to speed up the type checker, since they make not have the intended effect or 
even the opposite effect. We know the type checker implementation is not the 
best possible implementation of a type checker — there is a lot we can improve 
without changing the language.

Slava___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 3:28 PM, Slava Pestov  wrote:
> 
> 
> 
>> On Nov 28, 2017, at 1:25 PM, Matthew Johnson > > wrote:
>> 
>> 
>>> On Nov 28, 2017, at 3:18 PM, Slava Pestov >> > wrote:
>>> 
>>> 
>>> 
 On Nov 28, 2017, at 8:44 AM, Matthew Johnson >>> > wrote:
 
 func makeResource(
 with configuration: Configuration = () where Configuration == Void, 
 actionHandler: @escaping (Action) -> Void = { _ in } where Action == 
 Never
 )
>>> 
>>> Similar question to the one I posed earlier — what happens if I’m using 
>>> makeResource() from a generic context? Is the conditional default argument 
>>> simply not available?
>> 
>> Right.  If the constraints are not met at the call site the default is not 
>> available.  I think you understood, but looking at the example above I 
>> omitted the resource type parameter.  It should read:
>> 
>> func makeResource(
>> with configuration: R.Configuration = () where R.Configuration == Void, 
>> actionHandler: @escaping (R.Action) -> Void = { _ in } where R.Action == 
>> Never
>> )
>> 
>>> 
>>> In this case, how is it different from defining some static overloads of 
>>> makeResource(), some of which have default arguments and some of which are 
>>> generic?
>> 
>> From the point of view of the call site it is not different.  The 
>> differences are that:
>> 
>> * the user is presented with a single API rather than several overloads
> 
> Is this less confusing than encountering a new ‘where’ clause on default 
> arguments, which is probably rare enough that many users will spend 
> months/years using Swift without seeing it?

I think so.  The where clause is used for constraints consistently in the 
language so the meaning of seeing one attached to a default argument should be 
intuitive for anyone familiar with the generics system.  In the motivating use 
case I have there would still be an overload set available but it would be much 
smaller with this feature and therefore the available signatures would be much 
more clear.

> 
>> * the compiler doesn’t have to reason about an overload set which might 
>> improve build times, etc
> 
> They’re effectively equivalent, because we still have to decide which subset 
> of the default arguments apply at a given call site by checking all 
> combinations of constraints.

Interesting.  Are there no advantages to the compiler that would be possible if 
an overload set was replaced with constrained default arguments?

> 
> Slava
> 
>> 
>>> 
>>> Slava
>> 
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Slava Pestov via swift-evolution


> On Nov 28, 2017, at 1:25 PM, Matthew Johnson  wrote:
> 
> 
>> On Nov 28, 2017, at 3:18 PM, Slava Pestov > > wrote:
>> 
>> 
>> 
>>> On Nov 28, 2017, at 8:44 AM, Matthew Johnson >> > wrote:
>>> 
>>> func makeResource(
>>> with configuration: Configuration = () where Configuration == Void, 
>>> actionHandler: @escaping (Action) -> Void = { _ in } where Action == 
>>> Never
>>> )
>> 
>> Similar question to the one I posed earlier — what happens if I’m using 
>> makeResource() from a generic context? Is the conditional default argument 
>> simply not available?
> 
> Right.  If the constraints are not met at the call site the default is not 
> available.  I think you understood, but looking at the example above I 
> omitted the resource type parameter.  It should read:
> 
> func makeResource(
> with configuration: R.Configuration = () where R.Configuration == Void, 
> actionHandler: @escaping (R.Action) -> Void = { _ in } where R.Action == 
> Never
> )
> 
>> 
>> In this case, how is it different from defining some static overloads of 
>> makeResource(), some of which have default arguments and some of which are 
>> generic?
> 
> From the point of view of the call site it is not different.  The differences 
> are that:
> 
> * the user is presented with a single API rather than several overloads

Is this less confusing than encountering a new ‘where’ clause on default 
arguments, which is probably rare enough that many users will spend 
months/years using Swift without seeing it?

> * the compiler doesn’t have to reason about an overload set which might 
> improve build times, etc

They’re effectively equivalent, because we still have to decide which subset of 
the default arguments apply at a given call site by checking all combinations 
of constraints.

Slava

> 
>> 
>> Slava
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 3:18 PM, Slava Pestov  wrote:
> 
> 
> 
>> On Nov 28, 2017, at 8:44 AM, Matthew Johnson > > wrote:
>> 
>> func makeResource(
>> with configuration: Configuration = () where Configuration == Void, 
>> actionHandler: @escaping (Action) -> Void = { _ in } where Action == 
>> Never
>> )
> 
> Similar question to the one I posed earlier — what happens if I’m using 
> makeResource() from a generic context? Is the conditional default argument 
> simply not available?

Right.  If the constraints are not met at the call site the default is not 
available.  I think you understood, but looking at the example above I omitted 
the resource type parameter.  It should read:

func makeResource(
with configuration: R.Configuration = () where R.Configuration == Void, 
actionHandler: @escaping (R.Action) -> Void = { _ in } where R.Action == 
Never
)

> 
> In this case, how is it different from defining some static overloads of 
> makeResource(), some of which have default arguments and some of which are 
> generic?

>From the point of view of the call site it is not different.  The differences 
>are that:

* the user is presented with a single API rather than several overloads
* the library author isn’t required to maintain an overload set
* the compiler doesn’t have to reason about an overload set which might improve 
build times, etc

> 
> Slava

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Slava Pestov via swift-evolution


> On Nov 28, 2017, at 8:44 AM, Matthew Johnson  wrote:
> 
> func makeResource(
> with configuration: Configuration = () where Configuration == Void, 
> actionHandler: @escaping (Action) -> Void = { _ in } where Action == Never
> )

Similar question to the one I posed earlier — what happens if I’m using 
makeResource() from a generic context? Is the conditional default argument 
simply not available?

In this case, how is it different from defining some static overloads of 
makeResource(), some of which have default arguments and some of which are 
generic?

Slava___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 11:40 AM, Tony Allevato  wrote:
> 
> 
> 
> On Tue, Nov 28, 2017 at 9:16 AM Matthew Johnson  > wrote:
>> On Nov 28, 2017, at 11:01 AM, Tony Allevato > > wrote:
>> 
>> 
>> 
>> On Tue, Nov 28, 2017 at 8:45 AM Matthew Johnson > > wrote:
>>> On Nov 28, 2017, at 10:06 AM, Tony Allevato >> > wrote:
>>> 
>>> 
>>> 
>>> On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov >> > wrote:
>>> Hi Tony,
>>> 
>>> So if my understanding is correct, the basic proposal is the following:
>>> 
>>> func id(t: T ?= T.defaultValue) { return t }
>>> 
>>> extension Int { static var defaultValue = 0 }
>>> 
>>> extension String { static var defaultValue = “” }
>>> 
>>> id() as Int // returns 0
>>> id() as String // returns “”
>>> id() as SomeRandomType // fails to type check — no default argument
>>> 
>>> I don’t understand what would happen if the caller is itself generic 
>>> though, for example:
>>> 
>>> callsID(_ t: T) {
>>>   _ = id() as T
>>> }
>>> 
>>> It appears that body of callsID() itself cannot type check without 
>>> knowledge of the concrete T that will be used with this function.
>>> 
>>> Thanks for bringing up this example, Slava.
>>> 
>>> Unless I'm misunderstanding, the issue you're describing is inherent to the 
>>> *problem* described in the original post, not to any specific hypothetical 
>>> syntax for adding the default arguments, correct? In other words, if this 
>>> was written using extensions as can be done today:
>>> 
>>> ```
>>> struct Foo {
>>>   func id(t: T) -> T { return t }
>>> }
>>> 
>>> extension Foo where T == Void {
>>>   func id() -> T { return () }
>>> }
>>> 
>>> extension Foo where T == Int {
>>>   func id() -> T { return 0 }
>>> }
>>> 
>>> callsID(_ t: T) {
>>>   _ = Foo().id() as T// mark
>>> }
>>> ```
>>> 
>>> The compiler would still reject the marked line because there's no 
>>> guarantee that T is one of the types that has the necessary overload.
>>> 
>>> But now that you've mentioned it, it does have me thinking that this 
>>> problem might be better left to extensions. In one sense, default arguments 
>>> are a kind of "overload synthesis”,
>> 
>> They appear to callers as if they were overloads but I think it’s important 
>> that they actually do so *without* introducing an overload.  Reducing the 
>> size of an overload set is good for users, library authors and the compiler. 
>>  The benefits that come to all parties when the size of an overload set is 
>> reduced is the primary reason I started this thread.
>> 
>>> but on the other hand, there's an expectation that the default value 
>>> expression is of a single type (or set of related types) known at compile 
>>> time. Even if it's generic, it still must be expressed in terms of whatever 
>>> constraints are present on that generic type—you can't use a disjunction of 
>>> types, but instead have to have a common protocol that would provide some 
>>> operation.
>> 
>> This should not change for any given default value expression.  This thread 
>> doesn’t discuss changing that.  I discusses the ability to constrain the 
>> presence of a default value expression.
>> 
>> But that's exactly the distinction that I'm driving at—the more I think 
>> about it, the more I have difficulty expressing it cleanly and I think 
>> that's because these aren't the same as default arguments—they really are 
>> distinct overloads because you're talking about the presence or absence of 
>> an argument based on specific constraints instead of something that applies 
>> to the function uniformly for all possible types that satisfy the 
>> constraint. So what I'm suggesting is that maybe conflating this concept 
>> with default arguments is the wrong approach after all.
>> 
>>  
>>  While it would be useful to allow multiple default value expressions for 
>> different constraints the most common case will be a single constrained 
>> default value expression for any given argument.  We could just allow a 
>> trailing where clause on the default value expression itself like this:
>> 
>> func makeResource(
>> with configuration: Configuration = () where Configuration == Void, 
>> actionHandler: @escaping (Action) -> Void = { _ in } where Action == 
>> Never
>> )
>> 
>> That addresses the most common cases for this feature with a fairly obvious 
>> and direct syntax.
>> 
>> Allowing only one constraint seems arbitrary and I imagine that the 
>> immediately following question would be "what if I want more than one?" I 
>> wouldn't want to address only part of the problem. My ICU example further up 
>> in the thread shows a scenario where I would want defaults for two distinct 
>> types (if I also wanted to publicize the general initializer to everyone).
> 
> Did you really mean “only one constraint” here?  Or did you mean “only one 
> default value expression”?  These are very differ

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Tony Allevato via swift-evolution
On Tue, Nov 28, 2017 at 9:16 AM Matthew Johnson 
wrote:

> On Nov 28, 2017, at 11:01 AM, Tony Allevato 
> wrote:
>
>
>
> On Tue, Nov 28, 2017 at 8:45 AM Matthew Johnson 
> wrote:
>
>> On Nov 28, 2017, at 10:06 AM, Tony Allevato 
>> wrote:
>>
>>
>>
>> On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov  wrote:
>>
>>> Hi Tony,
>>>
>>> So if my understanding is correct, the basic proposal is the following:
>>>
>>> func id(t: T ?= T.defaultValue) { return t }
>>>
>>> extension Int { static var defaultValue = 0 }
>>>
>>> extension String { static var defaultValue = “” }
>>>
>>> id() as Int // returns 0
>>> id() as String // returns “”
>>> id() as SomeRandomType // fails to type check — no default argument
>>>
>>> I don’t understand what would happen if the caller is itself generic
>>> though, for example:
>>>
>>> callsID(_ t: T) {
>>>   _ = id() as T
>>> }
>>>
>>> It appears that body of callsID() itself cannot type check without
>>> knowledge of the concrete T that will be used with this function.
>>>
>>
>> Thanks for bringing up this example, Slava.
>>
>> Unless I'm misunderstanding, the issue you're describing is inherent to
>> the *problem* described in the original post, not to any specific
>> hypothetical syntax for adding the default arguments, correct? In other
>> words, if this was written using extensions as can be done today:
>>
>> ```
>> struct Foo {
>>   func id(t: T) -> T { return t }
>> }
>>
>> extension Foo where T == Void {
>>   func id() -> T { return () }
>> }
>>
>> extension Foo where T == Int {
>>   func id() -> T { return 0 }
>> }
>>
>> callsID(_ t: T) {
>>   _ = Foo().id() as T// mark
>> }
>> ```
>>
>> The compiler would still reject the marked line because there's no
>> guarantee that T is one of the types that has the necessary overload.
>>
>> But now that you've mentioned it, it does have me thinking that this
>> problem might be better left to extensions. In one sense, default arguments
>> are a kind of "overload synthesis”,
>>
>>
>> They appear to callers as if they were overloads but I think it’s
>> important that they actually do so *without* introducing an overload.
>> Reducing the size of an overload set is good for users, library authors and
>> the compiler.  The benefits that come to all parties when the size of an
>> overload set is reduced is the primary reason I started this thread.
>>
>> but on the other hand, there's an expectation that the default value
>> expression is of a single type (or set of related types) known at compile
>> time. Even if it's generic, it still must be expressed in terms of whatever
>> constraints are present on that generic type—you can't use a disjunction of
>> types, but instead have to have a common protocol that would provide some
>> operation.
>>
>>
>> This should not change for any given default value expression.  This
>> thread doesn’t discuss changing that.  I discusses the ability to constrain
>> the presence of a default value expression.
>>
>
> But that's exactly the distinction that I'm driving at—the more I think
> about it, the more I have difficulty expressing it cleanly and I think
> that's because these aren't the same as default arguments—they really are
> distinct overloads because you're talking about the presence or absence of
> an argument based on specific constraints instead of something that applies
> to the function uniformly for all possible types that satisfy the
> constraint. So what I'm suggesting is that maybe conflating this concept
> with default arguments is the wrong approach after all.
>
>
>
>>  While it would be useful to allow multiple default value expressions for
>> different constraints the most common case will be a single constrained
>> default value expression for any given argument.  We could just allow a
>> trailing where clause on the default value expression itself like this:
>>
>> func makeResource(
>> with configuration: Configuration = () where Configuration == Void,
>> actionHandler: @escaping (Action) -> Void = { _ in } where Action ==
>> Never
>> )
>>
>> That addresses the most common cases for this feature with a fairly
>> obvious and direct syntax.
>>
>
> Allowing only one constraint seems arbitrary and I imagine that the
> immediately following question would be "what if I want more than one?" I
> wouldn't want to address only part of the problem. My ICU example further
> up in the thread shows a scenario where I would want defaults for two
> distinct types (if I also wanted to publicize the general initializer to
> everyone).
>
>
> Did you really mean “only one constraint” here?  Or did you mean “only one
> default value expression”?  These are very different.  I would not want to
> restrict the number of constraints.  What I am suggesting is that limiting
> this to a single (possibly constrained) default value expression presents a
> relatively obvious syntactic solution.
>

Yes, I probably should have said "one where clause". Regardless, such a
restriction feels incomplete.


>
>
>

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 11:01 AM, Tony Allevato  wrote:
> 
> 
> 
> On Tue, Nov 28, 2017 at 8:45 AM Matthew Johnson  > wrote:
>> On Nov 28, 2017, at 10:06 AM, Tony Allevato > > wrote:
>> 
>> 
>> 
>> On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov > > wrote:
>> Hi Tony,
>> 
>> So if my understanding is correct, the basic proposal is the following:
>> 
>> func id(t: T ?= T.defaultValue) { return t }
>> 
>> extension Int { static var defaultValue = 0 }
>> 
>> extension String { static var defaultValue = “” }
>> 
>> id() as Int // returns 0
>> id() as String // returns “”
>> id() as SomeRandomType // fails to type check — no default argument
>> 
>> I don’t understand what would happen if the caller is itself generic though, 
>> for example:
>> 
>> callsID(_ t: T) {
>>   _ = id() as T
>> }
>> 
>> It appears that body of callsID() itself cannot type check without knowledge 
>> of the concrete T that will be used with this function.
>> 
>> Thanks for bringing up this example, Slava.
>> 
>> Unless I'm misunderstanding, the issue you're describing is inherent to the 
>> *problem* described in the original post, not to any specific hypothetical 
>> syntax for adding the default arguments, correct? In other words, if this 
>> was written using extensions as can be done today:
>> 
>> ```
>> struct Foo {
>>   func id(t: T) -> T { return t }
>> }
>> 
>> extension Foo where T == Void {
>>   func id() -> T { return () }
>> }
>> 
>> extension Foo where T == Int {
>>   func id() -> T { return 0 }
>> }
>> 
>> callsID(_ t: T) {
>>   _ = Foo().id() as T// mark
>> }
>> ```
>> 
>> The compiler would still reject the marked line because there's no guarantee 
>> that T is one of the types that has the necessary overload.
>> 
>> But now that you've mentioned it, it does have me thinking that this problem 
>> might be better left to extensions. In one sense, default arguments are a 
>> kind of "overload synthesis”,
> 
> They appear to callers as if they were overloads but I think it’s important 
> that they actually do so *without* introducing an overload.  Reducing the 
> size of an overload set is good for users, library authors and the compiler.  
> The benefits that come to all parties when the size of an overload set is 
> reduced is the primary reason I started this thread.
> 
>> but on the other hand, there's an expectation that the default value 
>> expression is of a single type (or set of related types) known at compile 
>> time. Even if it's generic, it still must be expressed in terms of whatever 
>> constraints are present on that generic type—you can't use a disjunction of 
>> types, but instead have to have a common protocol that would provide some 
>> operation.
> 
> This should not change for any given default value expression.  This thread 
> doesn’t discuss changing that.  I discusses the ability to constrain the 
> presence of a default value expression.
> 
> But that's exactly the distinction that I'm driving at—the more I think about 
> it, the more I have difficulty expressing it cleanly and I think that's 
> because these aren't the same as default arguments—they really are distinct 
> overloads because you're talking about the presence or absence of an argument 
> based on specific constraints instead of something that applies to the 
> function uniformly for all possible types that satisfy the constraint. So 
> what I'm suggesting is that maybe conflating this concept with default 
> arguments is the wrong approach after all.
> 
>  
>  While it would be useful to allow multiple default value expressions for 
> different constraints the most common case will be a single constrained 
> default value expression for any given argument.  We could just allow a 
> trailing where clause on the default value expression itself like this:
> 
> func makeResource(
> with configuration: Configuration = () where Configuration == Void, 
> actionHandler: @escaping (Action) -> Void = { _ in } where Action == Never
> )
> 
> That addresses the most common cases for this feature with a fairly obvious 
> and direct syntax.
> 
> Allowing only one constraint seems arbitrary and I imagine that the 
> immediately following question would be "what if I want more than one?" I 
> wouldn't want to address only part of the problem. My ICU example further up 
> in the thread shows a scenario where I would want defaults for two distinct 
> types (if I also wanted to publicize the general initializer to everyone).

Did you really mean “only one constraint” here?  Or did you mean “only one 
default value expression”?  These are very different.  I would not want to 
restrict the number of constraints.  What I am suggesting is that limiting this 
to a single (possibly constrained) default value expression presents a 
relatively obvious syntactic solution.

> 
>  
>  It also avoids the potential ambiguity that could arise from allowing 
> multiple d

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Tony Allevato via swift-evolution
On Tue, Nov 28, 2017 at 8:45 AM Matthew Johnson 
wrote:

> On Nov 28, 2017, at 10:06 AM, Tony Allevato 
> wrote:
>
>
>
> On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov  wrote:
>
>> Hi Tony,
>>
>> So if my understanding is correct, the basic proposal is the following:
>>
>> func id(t: T ?= T.defaultValue) { return t }
>>
>> extension Int { static var defaultValue = 0 }
>>
>> extension String { static var defaultValue = “” }
>>
>> id() as Int // returns 0
>> id() as String // returns “”
>> id() as SomeRandomType // fails to type check — no default argument
>>
>> I don’t understand what would happen if the caller is itself generic
>> though, for example:
>>
>> callsID(_ t: T) {
>>   _ = id() as T
>> }
>>
>> It appears that body of callsID() itself cannot type check without
>> knowledge of the concrete T that will be used with this function.
>>
>
> Thanks for bringing up this example, Slava.
>
> Unless I'm misunderstanding, the issue you're describing is inherent to
> the *problem* described in the original post, not to any specific
> hypothetical syntax for adding the default arguments, correct? In other
> words, if this was written using extensions as can be done today:
>
> ```
> struct Foo {
>   func id(t: T) -> T { return t }
> }
>
> extension Foo where T == Void {
>   func id() -> T { return () }
> }
>
> extension Foo where T == Int {
>   func id() -> T { return 0 }
> }
>
> callsID(_ t: T) {
>   _ = Foo().id() as T// mark
> }
> ```
>
> The compiler would still reject the marked line because there's no
> guarantee that T is one of the types that has the necessary overload.
>
> But now that you've mentioned it, it does have me thinking that this
> problem might be better left to extensions. In one sense, default arguments
> are a kind of "overload synthesis”,
>
>
> They appear to callers as if they were overloads but I think it’s
> important that they actually do so *without* introducing an overload.
> Reducing the size of an overload set is good for users, library authors and
> the compiler.  The benefits that come to all parties when the size of an
> overload set is reduced is the primary reason I started this thread.
>
> but on the other hand, there's an expectation that the default value
> expression is of a single type (or set of related types) known at compile
> time. Even if it's generic, it still must be expressed in terms of whatever
> constraints are present on that generic type—you can't use a disjunction of
> types, but instead have to have a common protocol that would provide some
> operation.
>
>
> This should not change for any given default value expression.  This
> thread doesn’t discuss changing that.  I discusses the ability to constrain
> the presence of a default value expression.
>

But that's exactly the distinction that I'm driving at—the more I think
about it, the more I have difficulty expressing it cleanly and I think
that's because these aren't the same as default arguments—they really are
distinct overloads because you're talking about the presence or absence of
an argument based on specific constraints instead of something that applies
to the function uniformly for all possible types that satisfy the
constraint. So what I'm suggesting is that maybe conflating this concept
with default arguments is the wrong approach after all.



>  While it would be useful to allow multiple default value expressions for
> different constraints the most common case will be a single constrained
> default value expression for any given argument.  We could just allow a
> trailing where clause on the default value expression itself like this:
>
> func makeResource(
> with configuration: Configuration = () where Configuration == Void,
> actionHandler: @escaping (Action) -> Void = { _ in } where Action ==
> Never
> )
>
> That addresses the most common cases for this feature with a fairly
> obvious and direct syntax.
>

Allowing only one constraint seems arbitrary and I imagine that the
immediately following question would be "what if I want more than one?" I
wouldn't want to address only part of the problem. My ICU example further
up in the thread shows a scenario where I would want defaults for two
distinct types (if I also wanted to publicize the general initializer to
everyone).



>  It also avoids the potential ambiguity that could arise from allowing
> multiple defaults with different (potentially overlapping) constraints.
>

That would be no different than the existing problem of multiple overloads
with different (potentially overlapping) constraints, right? If what you're
looking for is a way to have the compiler automatically synthesize
overloads in extensions for you based on certain constraints, I would
expect the same restrictions to apply as if you had explicitly written them
out long-form.



>
>
>
>>
>> Slava
>>
>> On Nov 27, 2017, at 4:10 PM, Tony Allevato via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>> I totally agree that that's a good rule in general—I'm

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 10:06 AM, Tony Allevato  wrote:
> 
> 
> 
> On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov  > wrote:
> Hi Tony,
> 
> So if my understanding is correct, the basic proposal is the following:
> 
> func id(t: T ?= T.defaultValue) { return t }
> 
> extension Int { static var defaultValue = 0 }
> 
> extension String { static var defaultValue = “” }
> 
> id() as Int // returns 0
> id() as String // returns “”
> id() as SomeRandomType // fails to type check — no default argument
> 
> I don’t understand what would happen if the caller is itself generic though, 
> for example:
> 
> callsID(_ t: T) {
>   _ = id() as T
> }
> 
> It appears that body of callsID() itself cannot type check without knowledge 
> of the concrete T that will be used with this function.
> 
> Thanks for bringing up this example, Slava.
> 
> Unless I'm misunderstanding, the issue you're describing is inherent to the 
> *problem* described in the original post, not to any specific hypothetical 
> syntax for adding the default arguments, correct? In other words, if this was 
> written using extensions as can be done today:
> 
> ```
> struct Foo {
>   func id(t: T) -> T { return t }
> }
> 
> extension Foo where T == Void {
>   func id() -> T { return () }
> }
> 
> extension Foo where T == Int {
>   func id() -> T { return 0 }
> }
> 
> callsID(_ t: T) {
>   _ = Foo().id() as T// mark
> }
> ```
> 
> The compiler would still reject the marked line because there's no guarantee 
> that T is one of the types that has the necessary overload.
> 
> But now that you've mentioned it, it does have me thinking that this problem 
> might be better left to extensions. In one sense, default arguments are a 
> kind of "overload synthesis”,

They appear to callers as if they were overloads but I think it’s important 
that they actually do so *without* introducing an overload.  Reducing the size 
of an overload set is good for users, library authors and the compiler.  The 
benefits that come to all parties when the size of an overload set is reduced 
is the primary reason I started this thread.

> but on the other hand, there's an expectation that the default value 
> expression is of a single type (or set of related types) known at compile 
> time. Even if it's generic, it still must be expressed in terms of whatever 
> constraints are present on that generic type—you can't use a disjunction of 
> types, but instead have to have a common protocol that would provide some 
> operation.

This should not change for any given default value expression.  This thread 
doesn’t discuss changing that.  I discusses the ability to constrain the 
presence of a default value expression.  While it would be useful to allow 
multiple default value expressions for different constraints the most common 
case will be a single constrained default value expression for any given 
argument.  We could just allow a trailing where clause on the default value 
expression itself like this:

func makeResource(
with configuration: Configuration = () where Configuration == Void, 
actionHandler: @escaping (Action) -> Void = { _ in } where Action == Never
)

That addresses the most common cases for this feature with a fairly obvious and 
direct syntax.  It also avoids the potential ambiguity that could arise from 
allowing multiple defaults with different (potentially overlapping) constraints.


>  
> 
> Slava
> 
>> On Nov 27, 2017, at 4:10 PM, Tony Allevato via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> I totally agree that that's a good rule in general—I'm not 100% comfortable 
>> making an exception to it for this, but I wanted to start a discussion about 
>> a different approach than had been considered so far.
>> 
>> The idea of forcing the user to acknowledge the explicitness of SFINAE with 
>> a strawman syntax `=?` instead of `=` was a thought experiment to bridge the 
>> wild-west-C++ world of templates and Swift's stricter generics, but I can 
>> definitely understand if even that kind of approach is something that the 
>> core team (who are far more familiar with the C++ side of that coin than I 
>> am) doesn't wish to support. As was pointed out, it's not something Swift 
>> supports anywhere else today.
>> 
>> If we look at it from that point of view, where such a semantic treatment of 
>> generics would not be supported, I think it becomes a lot harder to 
>> rationalize treating this as "default arguments". What you really do have 
>> (and what writing it as constrained extensions makes clear) is additional 
>> overloads, because they only apply to certain subsets of types. If that's 
>> the case, maybe it's the wrong approach to try to turn overloads into 
>> "partial default values".
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 10:32 PM Slava Pestov  wrote:

> Hi Tony,
>
> So if my understanding is correct, the basic proposal is the following:
>
> func id(t: T ?= T.defaultValue) { return t }
>
> extension Int { static var defaultValue = 0 }
>
> extension String { static var defaultValue = “” }
>
> id() as Int // returns 0
> id() as String // returns “”
> id() as SomeRandomType // fails to type check — no default argument
>
> I don’t understand what would happen if the caller is itself generic
> though, for example:
>
> callsID(_ t: T) {
>   _ = id() as T
> }
>
> It appears that body of callsID() itself cannot type check without
> knowledge of the concrete T that will be used with this function.
>

Thanks for bringing up this example, Slava.

Unless I'm misunderstanding, the issue you're describing is inherent to the
*problem* described in the original post, not to any specific hypothetical
syntax for adding the default arguments, correct? In other words, if this
was written using extensions as can be done today:

```
struct Foo {
  func id(t: T) -> T { return t }
}

extension Foo where T == Void {
  func id() -> T { return () }
}

extension Foo where T == Int {
  func id() -> T { return 0 }
}

callsID(_ t: T) {
  _ = Foo().id() as T// mark
}
```

The compiler would still reject the marked line because there's no
guarantee that T is one of the types that has the necessary overload.

But now that you've mentioned it, it does have me thinking that this
problem might be better left to extensions. In one sense, default arguments
are a kind of "overload synthesis", but on the other hand, there's an
expectation that the default value expression is of a single type (or set
of related types) known at compile time. Even if it's generic, it still
must be expressed in terms of whatever constraints are present on that
generic type—you can't use a disjunction of types, but instead have to have
a common protocol that would provide some operation.


>
> Slava
>
> On Nov 27, 2017, at 4:10 PM, Tony Allevato via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> I totally agree that that's a good rule in general—I'm not 100%
> comfortable making an exception to it for this, but I wanted to start a
> discussion about a different approach than had been considered so far.
>
> The idea of forcing the user to acknowledge the explicitness of SFINAE
> with a strawman syntax `=?` instead of `=` was a thought experiment to
> bridge the wild-west-C++ world of templates and Swift's stricter generics,
> but I can definitely understand if even that kind of approach is something
> that the core team (who are far more familiar with the C++ side of that
> coin than I am) doesn't wish to support. As was pointed out, it's not
> something Swift supports anywhere else today.
>
> If we look at it from that point of view, where such a semantic treatment
> of generics would not be supported, I think it becomes a lot harder to
> rationalize treating this as "default arguments". What you really do have
> (and what writing it as constrained extensions makes clear) is additional
> overloads, because they only apply to certain subsets of types. If that's
> the case, maybe it's the wrong approach to try to turn overloads into
> "partial default values".
>
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-28 Thread Matthew Johnson via swift-evolution

> On Nov 28, 2017, at 12:34 AM, Slava Pestov  wrote:
> 
> 
> 
>> On Nov 27, 2017, at 3:38 PM, Matthew Johnson via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> You are effectively proposing that in this very narrow case we perform 
>> overload resolution on a symbol in a generic type context *after* the 
>> generic type has been replaced with a concrete type. 
> 
> Keep in mind that in general, this would require runtime support — we don’t 
> always know the concrete substitution for a generic parameter at compile 
> time, especially in the presence of separate compilation (but even without, 
> for instance when optimizations are not enabled or unable to recover concrete 
> type information).

Thanks for mentioning that.  IMO, it rules out this approach as it means we 
wouldn’t always know statically whether a default argument is available.  C++ 
gets away with it because templates are always substituted during compilation 
and that isn’t true for Swift generics.

> 
> Slava

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Slava Pestov via swift-evolution


> On Nov 27, 2017, at 3:38 PM, Matthew Johnson via swift-evolution 
>  wrote:
> 
> You are effectively proposing that in this very narrow case we perform 
> overload resolution on a symbol in a generic type context *after* the generic 
> type has been replaced with a concrete type. 

Keep in mind that in general, this would require runtime support — we don’t 
always know the concrete substitution for a generic parameter at compile time, 
especially in the presence of separate compilation (but even without, for 
instance when optimizations are not enabled or unable to recover concrete type 
information).

Slava___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Slava Pestov via swift-evolution
Hi Tony,

So if my understanding is correct, the basic proposal is the following:

func id(t: T ?= T.defaultValue) { return t }

extension Int { static var defaultValue = 0 }

extension String { static var defaultValue = “” }

id() as Int // returns 0
id() as String // returns “”
id() as SomeRandomType // fails to type check — no default argument

I don’t understand what would happen if the caller is itself generic though, 
for example:

callsID(_ t: T) {
  _ = id() as T
}

It appears that body of callsID() itself cannot type check without knowledge of 
the concrete T that will be used with this function.

Slava

> On Nov 27, 2017, at 4:10 PM, Tony Allevato via swift-evolution 
>  wrote:
> 
> I totally agree that that's a good rule in general—I'm not 100% comfortable 
> making an exception to it for this, but I wanted to start a discussion about 
> a different approach than had been considered so far.
> 
> The idea of forcing the user to acknowledge the explicitness of SFINAE with a 
> strawman syntax `=?` instead of `=` was a thought experiment to bridge the 
> wild-west-C++ world of templates and Swift's stricter generics, but I can 
> definitely understand if even that kind of approach is something that the 
> core team (who are far more familiar with the C++ side of that coin than I 
> am) doesn't wish to support. As was pointed out, it's not something Swift 
> supports anywhere else today.
> 
> If we look at it from that point of view, where such a semantic treatment of 
> generics would not be supported, I think it becomes a lot harder to 
> rationalize treating this as "default arguments". What you really do have 
> (and what writing it as constrained extensions makes clear) is additional 
> overloads, because they only apply to certain subsets of types. If that's the 
> case, maybe it's the wrong approach to try to turn overloads into "partial 
> default values".

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 3:38 PM Matthew Johnson 
wrote:

> On Nov 27, 2017, at 4:55 PM, Tony Allevato 
> wrote:
>
>
>
> On Mon, Nov 27, 2017 at 2:39 PM Matthew Johnson 
> wrote:
>
>> On Nov 27, 2017, at 4:25 PM, Tony Allevato 
>> wrote:
>>
>>
>>
>> On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson 
>> wrote:
>>
>>> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>> Really like Tony’s suggestion, much cleaner than yet another annotation
>>> rammed into the signature. Also the idea of a static factory that could
>>> accept previously initialized arguments would be very powerful.
>>>
>>>
>>> It is syntactically cleaner at the site of the function declaration for
>>> sure.  Wouldn’t you agree that it is semantically less clear though?  Both
>>> are important, but which is more important?  If not semantics, why not?
>>>
>>> It’s also worth noting that it would be more verbose in at least some
>>> use cases (when a declaration such as `defaultConfiguration()` is used in
>>> the conditional default expression and is not otherwise necessary).
>>>
>>
>> (Apologies for the earlier blank reply—why is Cmd+Enter a keyboard
>> shortcut for Send something someone would think is a good idea?)
>>
>> That verbosity is kind of a feature of my design, in the sense that it
>> describes exactly what's going on at the location in code where someone
>> expects to see default values. If anything, the default factory name is an
>> opportunity to add context where normally there might be none.
>>
>> It's also worth noting that this design works well if you have multiple
>> methods that need to share the same defaults. Instead of repeating the same
>> annotations for each method that needs them, you just define the functions
>> once and refer to them everywhere. (Could you do the same with the
>> annotation based method? Probably, if you allow arbitrary expressions
>> within them. But that seems less obvious compared to this approach.)
>>
>>
>> You make a really good point here.  The annotations would need to be
>> applied to every declaration that uses them whereas your proposed syntax
>> would just rely on overload resolution succeeding or failing in a given
>> type context.  The use case I have would benefit in this respect as the
>> conditional defaults would be shared by several declarations.
>>
>> Howard’s idea of restricting conditional defaults to only use
>> declarations in the same file seems somewhat arbitrary but it would go a
>> long way towards helping an author understand clearly what the provided
>> defaults are as the rest of the module and imported symbols would not need
>> to be considered.  I wonder if this approach could be refined a bit so it
>> feels less arbitrary.  Users would probably need to rely on tooling to
>> discover defaults but I think I’m ok with that.  I am mostly concerned with
>> the ability of an author to reason locally about the API contract they are
>> publishing.  Do you have any ideas on how to refine Howard's idea?
>>
>
> I mentioned earlier in the thread (with a few messed up details) that
> there are already some restrictions on default arguments that I think
> already work well here. Declarations referenced in the default value
> expression of a function today must be accessible within the scope of the
> declaration being defined, so I'm not sure if we need to go further than
> that.
>
> Here's an example that I'll admit is completely contrived, but should I be
> prevented from doing this? Let's say I define an "Identities" module with
> this type:
>
> ```
> enum Identities {
>   func identity() -> Int { return 0 }  // let's ignore additive vs.
> multiplicative for a moment
>   func identity() -> Double { return 0.0 }
>   func identity() -> () { return () }
>   func identity() -> String { return "" }
>   func identity() -> (T) -> Void { return { _ in } }
>   // and so on
> }
> ```
>
> Then somewhere I want to define a conditional default, using those
> identities:
>
> ```
> import Identities
>
> func someWeirdThing( ...contrived args..., defaultValue: T =?
> Identities.identity()) { ... }
> ```
>
> Swift today already allows this with regular default value expressions, so
> the problem of tooling being needed to discover defaults already exists and
> this hypothetical construct doesn't change that. We *could* restrict such
> defaults to same file or same module and it seems reasonable to do so, but
> should we? If the defaults I want happen to live elsewhere, why not let me
> use them? Or, if it's a serious concern, why not lock down all default
> expressions the same way?
>
>
> You make a really good point about the current behavior default value
> expressions that I hadn’t fully considered.  It is currently possible for a
> new, more specific declaration to be introduced that would be selected.  It
> is also currently possible for the symbol that is resolved to be removed
> while a less specific declaration is available for res

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 4:55 PM, Tony Allevato  wrote:
> 
> 
> 
> On Mon, Nov 27, 2017 at 2:39 PM Matthew Johnson  > wrote:
>> On Nov 27, 2017, at 4:25 PM, Tony Allevato > > wrote:
>> 
>> 
>> 
>> On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson > > wrote:
>>> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution 
>>> mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> Really like Tony’s suggestion, much cleaner than yet another annotation 
>>> rammed into the signature. Also the idea of a static factory that could 
>>> accept previously initialized arguments would be very powerful. 
>> 
>> It is syntactically cleaner at the site of the function declaration for 
>> sure.  Wouldn’t you agree that it is semantically less clear though?  Both 
>> are important, but which is more important?  If not semantics, why not?
>> 
>> It’s also worth noting that it would be more verbose in at least some use 
>> cases (when a declaration such as `defaultConfiguration()` is used in the 
>> conditional default expression and is not otherwise necessary).
>> 
>> (Apologies for the earlier blank reply—why is Cmd+Enter a keyboard shortcut 
>> for Send something someone would think is a good idea?)
>> 
>> That verbosity is kind of a feature of my design, in the sense that it 
>> describes exactly what's going on at the location in code where someone 
>> expects to see default values. If anything, the default factory name is an 
>> opportunity to add context where normally there might be none.
>> 
>> It's also worth noting that this design works well if you have multiple 
>> methods that need to share the same defaults. Instead of repeating the same 
>> annotations for each method that needs them, you just define the functions 
>> once and refer to them everywhere. (Could you do the same with the 
>> annotation based method? Probably, if you allow arbitrary expressions within 
>> them. But that seems less obvious compared to this approach.)
> 
> You make a really good point here.  The annotations would need to be applied 
> to every declaration that uses them whereas your proposed syntax would just 
> rely on overload resolution succeeding or failing in a given type context.  
> The use case I have would benefit in this respect as the conditional defaults 
> would be shared by several declarations.
> 
> Howard’s idea of restricting conditional defaults to only use declarations in 
> the same file seems somewhat arbitrary but it would go a long way towards 
> helping an author understand clearly what the provided defaults are as the 
> rest of the module and imported symbols would not need to be considered.  I 
> wonder if this approach could be refined a bit so it feels less arbitrary.  
> Users would probably need to rely on tooling to discover defaults but I think 
> I’m ok with that.  I am mostly concerned with the ability of an author to 
> reason locally about the API contract they are publishing.  Do you have any 
> ideas on how to refine Howard's idea?
> 
> I mentioned earlier in the thread (with a few messed up details) that there 
> are already some restrictions on default arguments that I think already work 
> well here. Declarations referenced in the default value expression of a 
> function today must be accessible within the scope of the declaration being 
> defined, so I'm not sure if we need to go further than that.
> 
> Here's an example that I'll admit is completely contrived, but should I be 
> prevented from doing this? Let's say I define an "Identities" module with 
> this type:
> 
> ```
> enum Identities {
>   func identity() -> Int { return 0 }  // let's ignore additive vs. 
> multiplicative for a moment
>   func identity() -> Double { return 0.0 }
>   func identity() -> () { return () }
>   func identity() -> String { return "" }
>   func identity() -> (T) -> Void { return { _ in } }
>   // and so on
> }
> ```
> 
> Then somewhere I want to define a conditional default, using those identities:
> 
> ```
> import Identities
> 
> func someWeirdThing( ...contrived args..., defaultValue: T =? 
> Identities.identity()) { ... }
> ```
> 
> Swift today already allows this with regular default value expressions, so 
> the problem of tooling being needed to discover defaults already exists and 
> this hypothetical construct doesn't change that. We *could* restrict such 
> defaults to same file or same module and it seems reasonable to do so, but 
> should we? If the defaults I want happen to live elsewhere, why not let me 
> use them? Or, if it's a serious concern, why not lock down all default 
> expressions the same way?

You make a really good point about the current behavior default value 
expressions that I hadn’t fully considered.  It is currently possible for a 
new, more specific declaration to be introduced that would be selected.  It is 
also currently possible for the symbol that is resolved to be removed while

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 2:39 PM Matthew Johnson 
wrote:

> On Nov 27, 2017, at 4:25 PM, Tony Allevato 
> wrote:
>
>
>
> On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson 
> wrote:
>
>> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>> Really like Tony’s suggestion, much cleaner than yet another annotation
>> rammed into the signature. Also the idea of a static factory that could
>> accept previously initialized arguments would be very powerful.
>>
>>
>> It is syntactically cleaner at the site of the function declaration for
>> sure.  Wouldn’t you agree that it is semantically less clear though?  Both
>> are important, but which is more important?  If not semantics, why not?
>>
>> It’s also worth noting that it would be more verbose in at least some use
>> cases (when a declaration such as `defaultConfiguration()` is used in the
>> conditional default expression and is not otherwise necessary).
>>
>
> (Apologies for the earlier blank reply—why is Cmd+Enter a keyboard
> shortcut for Send something someone would think is a good idea?)
>
> That verbosity is kind of a feature of my design, in the sense that it
> describes exactly what's going on at the location in code where someone
> expects to see default values. If anything, the default factory name is an
> opportunity to add context where normally there might be none.
>
> It's also worth noting that this design works well if you have multiple
> methods that need to share the same defaults. Instead of repeating the same
> annotations for each method that needs them, you just define the functions
> once and refer to them everywhere. (Could you do the same with the
> annotation based method? Probably, if you allow arbitrary expressions
> within them. But that seems less obvious compared to this approach.)
>
>
> You make a really good point here.  The annotations would need to be
> applied to every declaration that uses them whereas your proposed syntax
> would just rely on overload resolution succeeding or failing in a given
> type context.  The use case I have would benefit in this respect as the
> conditional defaults would be shared by several declarations.
>
> Howard’s idea of restricting conditional defaults to only use declarations
> in the same file seems somewhat arbitrary but it would go a long way
> towards helping an author understand clearly what the provided defaults are
> as the rest of the module and imported symbols would not need to be
> considered.  I wonder if this approach could be refined a bit so it feels
> less arbitrary.  Users would probably need to rely on tooling to discover
> defaults but I think I’m ok with that.  I am mostly concerned with the
> ability of an author to reason locally about the API contract they are
> publishing.  Do you have any ideas on how to refine Howard's idea?
>

I mentioned earlier in the thread (with a few messed up details) that there
are already some restrictions on default arguments that I think already
work well here. Declarations referenced in the default value expression of
a function today must be accessible within the scope of the declaration
being defined, so I'm not sure if we need to go further than that.

Here's an example that I'll admit is completely contrived, but should I be
prevented from doing this? Let's say I define an "Identities" module with
this type:

```
enum Identities {
  func identity() -> Int { return 0 }  // let's ignore additive vs.
multiplicative for a moment
  func identity() -> Double { return 0.0 }
  func identity() -> () { return () }
  func identity() -> String { return "" }
  func identity() -> (T) -> Void { return { _ in } }
  // and so on
}
```

Then somewhere I want to define a conditional default, using those
identities:

```
import Identities

func someWeirdThing( ...contrived args..., defaultValue: T =?
Identities.identity()) { ... }
```

Swift today already allows this with regular default value expressions, so
the problem of tooling being needed to discover defaults already exists and
this hypothetical construct doesn't change that. We *could* restrict such
defaults to same file or same module and it seems reasonable to do so, but
should we? If the defaults I want happen to live elsewhere, why not let me
use them? Or, if it's a serious concern, why not lock down all default
expressions the same way?

I think the bigger concern is the other one Xiaodi mentioned—we probably
don't want people to be able to retroactively add overloads that would
introduce a default where previously there was none. (This wouldn't be
possible for global functions in different modules, but could be for
non-private type members.) This might be something that Just Works Out™
depending on how and when the compiler resolves such expressions—but I
don't know the compiler deeply enough to say for sure without investigating
more.



>
>
>
>
>>
>> -- Howard.
>>
>> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution <
>> swift-e

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 4:25 PM, Tony Allevato  wrote:
> 
> 
> 
> On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson  > wrote:
>> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> Really like Tony’s suggestion, much cleaner than yet another annotation 
>> rammed into the signature. Also the idea of a static factory that could 
>> accept previously initialized arguments would be very powerful. 
> 
> It is syntactically cleaner at the site of the function declaration for sure. 
>  Wouldn’t you agree that it is semantically less clear though?  Both are 
> important, but which is more important?  If not semantics, why not?
> 
> It’s also worth noting that it would be more verbose in at least some use 
> cases (when a declaration such as `defaultConfiguration()` is used in the 
> conditional default expression and is not otherwise necessary).
> 
> (Apologies for the earlier blank reply—why is Cmd+Enter a keyboard shortcut 
> for Send something someone would think is a good idea?)
> 
> That verbosity is kind of a feature of my design, in the sense that it 
> describes exactly what's going on at the location in code where someone 
> expects to see default values. If anything, the default factory name is an 
> opportunity to add context where normally there might be none.
> 
> It's also worth noting that this design works well if you have multiple 
> methods that need to share the same defaults. Instead of repeating the same 
> annotations for each method that needs them, you just define the functions 
> once and refer to them everywhere. (Could you do the same with the annotation 
> based method? Probably, if you allow arbitrary expressions within them. But 
> that seems less obvious compared to this approach.)

You make a really good point here.  The annotations would need to be applied to 
every declaration that uses them whereas your proposed syntax would just rely 
on overload resolution succeeding or failing in a given type context.  The use 
case I have would benefit in this respect as the conditional defaults would be 
shared by several declarations.

Howard’s idea of restricting conditional defaults to only use declarations in 
the same file seems somewhat arbitrary but it would go a long way towards 
helping an author understand clearly what the provided defaults are as the rest 
of the module and imported symbols would not need to be considered.  I wonder 
if this approach could be refined a bit so it feels less arbitrary.  Users 
would probably need to rely on tooling to discover defaults but I think I’m ok 
with that.  I am mostly concerned with the ability of an author to reason 
locally about the API contract they are publishing.  Do you have any ideas on 
how to refine Howard's idea?


> 
> 
>> 
>> -- Howard.
>> 
>> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu >> > wrote:
>>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson >> > wrote:
 On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
 mailto:swift-evolution@swift.org>> wrote:
 
 On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
 mailto:swift-evolution@swift.org>> wrote:
 
 
 
 It's kludgy, but we could have something like:
 
 ```
 @defaultArgument(configuration = (), where R.Configuration == Void)
 @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
 func makeResource(with configuration: R.Configuration, actionHandler: 
 @escaping (R.Action) -> Void) -> R { ... }
 ```
 
 I don't like that we'd be setting a default argument on something 
 lexically before even encountering it in the declaration, but it's 
 serviceable.
 
 
 What if we could take advantage of the fact that you can have non-constant 
 expressions in default arguments? Overload resolution could already do 
 most of the job—what we need on top of that is a way for the author to say 
 that “if no overload matches, then it’s not an error—just don’t have a 
 default argument in that case”. Something like SFINAE in C++, but more 
 explicit.
 
 I’m imagining something like this:
 
 func defaultConfiguration() -> Void {
   return ()
 }
 
 func defaultActionHandler() -> (Never) -> Void {
   return { _ in }
 }
 
 struct ResourceDescription {
   func makeResource(
 with configuration: R.Configuration =? defaultConfiguration(),
 actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
   ) -> R {
 // create a resource using the provided configuration
 // connect the action handler
 // return the resource
   }
 }
 The main difference here is the strawman =? syntax, wh

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson 
wrote:

> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> Really like Tony’s suggestion, much cleaner than yet another annotation
> rammed into the signature. Also the idea of a static factory that could
> accept previously initialized arguments would be very powerful.
>
>
> It is syntactically cleaner at the site of the function declaration for
> sure.  Wouldn’t you agree that it is semantically less clear though?  Both
> are important, but which is more important?  If not semantics, why not?
>
> It’s also worth noting that it would be more verbose in at least some use
> cases (when a declaration such as `defaultConfiguration()` is used in the
> conditional default expression and is not otherwise necessary).
>

(Apologies for the earlier blank reply—why is Cmd+Enter a keyboard shortcut
for Send something someone would think is a good idea?)

That verbosity is kind of a feature of my design, in the sense that it
describes exactly what's going on at the location in code where someone
expects to see default values. If anything, the default factory name is an
opportunity to add context where normally there might be none.

It's also worth noting that this design works well if you have multiple
methods that need to share the same defaults. Instead of repeating the same
annotations for each method that needs them, you just define the functions
once and refer to them everywhere. (Could you do the same with the
annotation based method? Probably, if you allow arbitrary expressions
within them. But that seems less obvious compared to this approach.)


>
> -- Howard.
>
> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>
>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
>> wrote:
>>
>>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>>
>>> It's kludgy, but we could have something like:

 ```
 @defaultArgument(configuration = (), where R.Configuration == Void)
 @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
 func makeResource(with configuration: R.Configuration, actionHandler:
 @escaping (R.Action) -> Void) -> R { ... }
 ```

 I don't like that we'd be setting a default argument on something
 lexically before even encountering it in the declaration, but it's
 serviceable.

>>>
>>>
>>> What if we could take advantage of the fact that you can have
>>> non-constant expressions in default arguments? Overload resolution could
>>> already do most of the job—what we need on top of that is a way for the
>>> author to say that “if no overload matches, then it’s not an error—just
>>> don’t have a default argument in that case”. Something like SFINAE in C++,
>>> but more explicit.
>>>
>>> I’m imagining something like this:
>>>
>>> func defaultConfiguration() -> Void {
>>>   return ()
>>> }
>>> func defaultActionHandler() -> (Never) -> Void {
>>>   return { _ in }
>>> }
>>> struct ResourceDescription {
>>>   func makeResource(
>>> with configuration: R.Configuration *=?* defaultConfiguration(),
>>> actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
>>>   ) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>>
>>> The main difference here is the strawman =? syntax, which would
>>> indicate that “the default argument exists if there is a way the RHS can be
>>> satisfied for some instances of the generic arguments; otherwise, there is
>>> no default”, instead of today’s behavior where it would be an error. There
>>> could be multiple overloads of defaultConfiguration and
>>> defaultActionHandler (even ones that are themselves generic) and it
>>> would do the right thing when there are matches and when there aren’t.
>>>
>>> I like this approach because it mostly takes advantage of existing
>>> language features and is fairly lightweight in terms of how it’s expressed
>>> in code compared to regular default arguments—we’d just need to design the
>>> new operator and type-checker logic around it.
>>>
>>> This is an interesting approach.  One advantage to something in this
>>> direction is that it could support defining different defaults for the same
>>> argument under different constraints by overloading the default argument
>>> factories on their return type.
>>>
>>> One concern I have is that it doesn’t allows us to clearly define under
>>> which constraints a default argument is available.  I suspect this might be
>>> problematic especially for public interfaces where source compatibility is
>>> a concern.
>>>
>>
>> It's certainly an interesting idea but it wou

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 2:19 PM Matthew Johnson 
wrote:

> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> Really like Tony’s suggestion, much cleaner than yet another annotation
> rammed into the signature. Also the idea of a static factory that could
> accept previously initialized arguments would be very powerful.
>
>
> It is syntactically cleaner at the site of the function declaration for
> sure.  Wouldn’t you agree that it is semantically less clear though?  Both
> are important, but which is more important?  If not semantics, why not?
>
> It’s also worth noting that it would be more verbose in at least some use
> cases (when a declaration such as `defaultConfiguration()` is used in the
> conditional default expression and is not otherwise necessary).
>
>
> -- Howard.
>
> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>
>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
>> wrote:
>>
>>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>>
>>> It's kludgy, but we could have something like:

 ```
 @defaultArgument(configuration = (), where R.Configuration == Void)
 @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
 func makeResource(with configuration: R.Configuration, actionHandler:
 @escaping (R.Action) -> Void) -> R { ... }
 ```

 I don't like that we'd be setting a default argument on something
 lexically before even encountering it in the declaration, but it's
 serviceable.

>>>
>>>
>>> What if we could take advantage of the fact that you can have
>>> non-constant expressions in default arguments? Overload resolution could
>>> already do most of the job—what we need on top of that is a way for the
>>> author to say that “if no overload matches, then it’s not an error—just
>>> don’t have a default argument in that case”. Something like SFINAE in C++,
>>> but more explicit.
>>>
>>> I’m imagining something like this:
>>>
>>> func defaultConfiguration() -> Void {
>>>   return ()
>>> }
>>> func defaultActionHandler() -> (Never) -> Void {
>>>   return { _ in }
>>> }
>>> struct ResourceDescription {
>>>   func makeResource(
>>> with configuration: R.Configuration *=?* defaultConfiguration(),
>>> actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
>>>   ) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>>
>>> The main difference here is the strawman =? syntax, which would
>>> indicate that “the default argument exists if there is a way the RHS can be
>>> satisfied for some instances of the generic arguments; otherwise, there is
>>> no default”, instead of today’s behavior where it would be an error. There
>>> could be multiple overloads of defaultConfiguration and
>>> defaultActionHandler (even ones that are themselves generic) and it
>>> would do the right thing when there are matches and when there aren’t.
>>>
>>> I like this approach because it mostly takes advantage of existing
>>> language features and is fairly lightweight in terms of how it’s expressed
>>> in code compared to regular default arguments—we’d just need to design the
>>> new operator and type-checker logic around it.
>>>
>>> This is an interesting approach.  One advantage to something in this
>>> direction is that it could support defining different defaults for the same
>>> argument under different constraints by overloading the default argument
>>> factories on their return type.
>>>
>>> One concern I have is that it doesn’t allows us to clearly define under
>>> which constraints a default argument is available.  I suspect this might be
>>> problematic especially for public interfaces where source compatibility is
>>> a concern.
>>>
>>
>> It's certainly an interesting idea but it would suggest that the
>> constraints under which a default argument is available can change at
>> runtime. I'm concerned, like you, that this is difficult to reason about.
>> It is still unclear to me how widespread the underlying issue is that
>> requires conditional default arguments, but the conversation thus far has
>> been about compile-time constraints and Tony's design seems to envision
>> much more than that.
>>
>
> This runtime/reasoning problem *already exists* today with default
> arguments, because you can write something like this:
>
> struct Foo {
>   static var defaultExponent = 2.0
>
>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
> print(pow(x, exponent))
>   }
> }
> Foo().raise(4)  // "16.0"Foo.defaultExponent = 3.0Foo().raise(4)  // "64.0"
>
> Swift lets you write a default value expression tha

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 3:56 PM, Howard Lovatt via swift-evolution 
>  wrote:
> 
> Really like Tony’s suggestion, much cleaner than yet another annotation 
> rammed into the signature. Also the idea of a static factory that could 
> accept previously initialized arguments would be very powerful. 

It is syntactically cleaner at the site of the function declaration for sure.  
Wouldn’t you agree that it is semantically less clear though?  Both are 
important, but which is more important?  If not semantics, why not?

It’s also worth noting that it would be more verbose in at least some use cases 
(when a declaration such as `defaultConfiguration()` is used in the conditional 
default expression and is not otherwise necessary).

> 
> -- Howard.
> 
> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> 
>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu > > wrote:
>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson > > wrote:
>>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
>>> mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
>>> mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> 
>>> 
>>> It's kludgy, but we could have something like:
>>> 
>>> ```
>>> @defaultArgument(configuration = (), where R.Configuration == Void)
>>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>>> func makeResource(with configuration: R.Configuration, actionHandler: 
>>> @escaping (R.Action) -> Void) -> R { ... }
>>> ```
>>> 
>>> I don't like that we'd be setting a default argument on something lexically 
>>> before even encountering it in the declaration, but it's serviceable.
>>> 
>>> 
>>> What if we could take advantage of the fact that you can have non-constant 
>>> expressions in default arguments? Overload resolution could already do most 
>>> of the job—what we need on top of that is a way for the author to say that 
>>> “if no overload matches, then it’s not an error—just don’t have a default 
>>> argument in that case”. Something like SFINAE in C++, but more explicit.
>>> 
>>> I’m imagining something like this:
>>> 
>>> func defaultConfiguration() -> Void {
>>>   return ()
>>> }
>>> 
>>> func defaultActionHandler() -> (Never) -> Void {
>>>   return { _ in }
>>> }
>>> 
>>> struct ResourceDescription {
>>>   func makeResource(
>>> with configuration: R.Configuration =? defaultConfiguration(),
>>> actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
>>>   ) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>> The main difference here is the strawman =? syntax, which would indicate 
>>> that “the default argument exists if there is a way the RHS can be 
>>> satisfied for some instances of the generic arguments; otherwise, there is 
>>> no default”, instead of today’s behavior where it would be an error. There 
>>> could be multiple overloads of defaultConfiguration and 
>>> defaultActionHandler (even ones that are themselves generic) and it would 
>>> do the right thing when there are matches and when there aren’t.
>>> 
>>> I like this approach because it mostly takes advantage of existing language 
>>> features and is fairly lightweight in terms of how it’s expressed in code 
>>> compared to regular default arguments—we’d just need to design the new 
>>> operator and type-checker logic around it.
>>> 
>> 
>> This is an interesting approach.  One advantage to something in this 
>> direction is that it could support defining different defaults for the same 
>> argument under different constraints by overloading the default argument 
>> factories on their return type.
>> 
>> One concern I have is that it doesn’t allows us to clearly define under 
>> which constraints a default argument is available.  I suspect this might be 
>> problematic especially for public interfaces where source compatibility is a 
>> concern.  
>> 
>> It's certainly an interesting idea but it would suggest that the constraints 
>> under which a default argument is available can change at runtime. I'm 
>> concerned, like you, that this is difficult to reason about. It is still 
>> unclear to me how widespread the underlying issue is that requires 
>> conditional default arguments, but the conversation thus far has been about 
>> compile-time constraints and Tony's design seems to envision much more than 
>> that.
>> 
>> This runtime/reasoning problem already exists today with default arguments, 
>> because you can write something like this:
>> 
>> struct Foo {
>>   static var defaultExponent = 2.0
>> 
>>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
>> print(pow(x, exponent))
>>   }
>> }
>> 
>> Foo().raise(4)  // "16.0"
>> Foo.defaultExponent = 3.0
>> Foo().raise(4)  // "64.0"
>> Swift lets you write a 

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Howard Lovatt via swift-evolution
You could even restrict to the same file, like extension access to private. 

-- Howard.

> On 26 Nov 2017, at 9:44 am, Tony Allevato via swift-evolution 
>  wrote:
> 
>> On Sat, Nov 25, 2017 at 2:35 PM Xiaodi Wu  wrote:
>>> On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato  
>>> wrote:
>> 
 On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>>> 
 On Sat, Nov 25, 2017 at 15:06 Matthew Johnson  
 wrote:
>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
>>  wrote:
>> 
>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
>>  wrote:
>> 
>> 
>> 
>>> It's kludgy, but we could have something like:
>>> 
>>> ```
>>> @defaultArgument(configuration = (), where R.Configuration == Void)
>>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>>> func makeResource(with configuration: R.Configuration, actionHandler: 
>>> @escaping (R.Action) -> Void) -> R { ... }
>>> ```
>>> 
>>> I don't like that we'd be setting a default argument on something 
>>> lexically before even encountering it in the declaration, but it's 
>>> serviceable.
>> 
>> 
>> What if we could take advantage of the fact that you can have 
>> non-constant expressions in default arguments? Overload resolution could 
>> already do most of the job—what we need on top of that is a way for the 
>> author to say that “if no overload matches, then it’s not an error—just 
>> don’t have a default argument in that case”. Something like SFINAE in 
>> C++, but more explicit.
>> 
>> I’m imagining something like this:
>> 
>> func defaultConfiguration() -> Void {
>>   return ()
>> }
>> 
>> func defaultActionHandler() -> (Never) -> Void {
>>   return { _ in }
>> }
>> 
>> struct ResourceDescription {
>>   func makeResource(
>> with configuration: R.Configuration =? defaultConfiguration(),
>> actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
>>   ) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>> The main difference here is the strawman =? syntax, which would indicate 
>> that “the default argument exists if there is a way the RHS can be 
>> satisfied for some instances of the generic arguments; otherwise, there 
>> is no default”, instead of today’s behavior where it would be an error. 
>> There could be multiple overloads of defaultConfiguration and 
>> defaultActionHandler (even ones that are themselves generic) and it 
>> would do the right thing when there are matches and when there aren’t.
>> 
>> I like this approach because it mostly takes advantage of existing 
>> language features and is fairly lightweight in terms of how it’s 
>> expressed in code compared to regular default arguments—we’d just need 
>> to design the new operator and type-checker logic around it.
>> 
> 
> This is an interesting approach.  One advantage to something in this 
> direction is that it could support defining different defaults for the 
> same argument under different constraints by overloading the default 
> argument factories on their return type.
> 
> One concern I have is that it doesn’t allows us to clearly define under 
> which constraints a default argument is available.  I suspect this might 
> be problematic especially for public interfaces where source 
> compatibility is a concern.  
 
 It's certainly an interesting idea but it would suggest that the 
 constraints under which a default argument is available can change at 
 runtime. I'm concerned, like you, that this is difficult to reason about. 
 It is still unclear to me how widespread the underlying issue is that 
 requires conditional default arguments, but the conversation thus far has 
 been about compile-time constraints and Tony's design seems to envision 
 much more than that.
>>> 
>>> This runtime/reasoning problem already exists today with default arguments, 
>>> because you can write something like this:
>>> 
>>> struct Foo {
>>>   static var defaultExponent = 2.0
>>> 
>>>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
>>> print(pow(x, exponent))
>>>   }
>>> }
>>> 
>>> Foo().raise(4)  // "16.0"
>>> Foo.defaultExponent = 3.0
>>> Foo().raise(4)  // "64.0"
>>> Swift lets you write a default value expression that references static (but 
>>> not instance) vars of the enclosing type, as well as anything else that’s 
>>> visible from that expression’s scope. Should people do this? Probably not, 
>>> for the reasons that you described.
>>> 
>>> But the point is that my example is no more harmful or difficult to reason 
>>> about than default arguments in the language today. My proposed solution in 
>>> no

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 3:27 PM, Tony Allevato  wrote:
> 
> On Mon, Nov 27, 2017 at 12:49 PM Matthew Johnson  > wrote:
>> On Nov 27, 2017, at 1:24 PM, Tony Allevato > > wrote:
>> 
>> 
>> 
>> On Mon, Nov 27, 2017 at 11:12 AM Matthew Johnson via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>>> On Nov 27, 2017, at 12:50 PM, Douglas Gregor >> > wrote:
>>> 
>>> 
>>> 
 On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
 mailto:swift-evolution@swift.org>> wrote:
 
 As mentioned in my prior message, I currently have a PR open to update the 
 generics manifesto (https://github.com/apple/swift/pull/13012 
 ).  I removed one topic from 
 that update at Doug Gregor’s request that it be discussed on the list 
 first.  
 
 The idea is to add the ability to make default arguments conditional (i.e. 
 depend on generic constraints).  It is currently possible to emulate 
 conditional default arguments using an overload set.  This is verbose, 
 especially when several arguments are involved.  Here is an example use 
 case using the overload method to emulate this feature:
 
 ```swift
 protocol Resource {
   associatedtype Configuration
   associatedtype Action
 }
 struct ResourceDescription {
   func makeResource(with configuration: R.Configuration, actionHandler: 
 @escaping (R.Action) -> Void) -> R {
 // create a resource using the provided configuration
 // connect the action handler
 // return the resource
   }
 }
 
 extension ResourceDescription where R.Configuration == Void {
   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
 return makeResource(with: (), actionHandler: actionHandler)
   }
 }
 
 extension ResourceDescription where R.Action == Never {
   func makeResource(with configuration: R.Configuration) -> R {
 return makeResource(with: configuration, actionHandler: { _ in })
   }
 }
 
 extension ResourceDescription where R.Configuration == Void, R.Action == 
 Never {
   func makeResource() -> R {
 return makeResource(with: (), actionHandler: { _ in })
   }
 }
 
 ```
 
 Adding language support for defining these more directly would eliminate a 
 lot of boilerplate and reduce the need for overloads.
>>> 
>>> 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:
>>> 
 protocol Resource {
   associatedtype Configuration
   associatedtype Action
>>> func defaultConfiguration() -> Configuration
>>> func defaultHandler() -> ((R.Action) -> Void)
 }
>> 
>> This won’t work on its own for this use case because there is only a valid 
>> default in relatively narrow (but common) cases.  For most values of 
>> Configuration and Action an argument must be provided by the caller.  
>> Xiaodi’s proposed syntax is the best fit (so far) for the use case I had in 
>> mind.  
>> 
>> Out of curiosity, what part of my proposed syntax misses the mark for your 
>> use case?
> 
> I think it’s important that we be clear about when a default is and is not 
> available.  Your syntax makes the availability of a default non-local.
> 
> 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:
> 
> ```
> struct ResourceDescription {
>   func defaultConfiguration() -> Void { return () }
>   func defaultActionHandler() -> (Never) -> Void { return _ in }
>   func makeResource(...) {
> ...
>   }
> }
> ```
> 
> 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”.

Non-locality of the value doesn’t bother me.  The availability of the default 
is part of the API contract, the specific value need not be.

> 
>  
> 
>> 
>> 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.
> 
> Xiaodi acknowledged that his suggestion is a bit clunky but I’m not sure we 
> can do be

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Howard Lovatt via swift-evolution
Really like Tony’s suggestion, much cleaner than yet another annotation rammed 
into the signature. Also the idea of a static factory that could accept 
previously initialized arguments would be very powerful. 

-- Howard.

> On 26 Nov 2017, at 9:25 am, Tony Allevato via swift-evolution 
>  wrote:
> 
>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson  wrote:
 On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
  wrote:
 
 On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
  wrote:
 
 
 
> It's kludgy, but we could have something like:
> 
> ```
> @defaultArgument(configuration = (), where R.Configuration == Void)
> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
> func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R { ... }
> ```
> 
> I don't like that we'd be setting a default argument on something 
> lexically before even encountering it in the declaration, but it's 
> serviceable.
 
 
 What if we could take advantage of the fact that you can have non-constant 
 expressions in default arguments? Overload resolution could already do 
 most of the job—what we need on top of that is a way for the author to say 
 that “if no overload matches, then it’s not an error—just don’t have a 
 default argument in that case”. Something like SFINAE in C++, but more 
 explicit.
 
 I’m imagining something like this:
 
 func defaultConfiguration() -> Void {
   return ()
 }
 
 func defaultActionHandler() -> (Never) -> Void {
   return { _ in }
 }
 
 struct ResourceDescription {
   func makeResource(
 with configuration: R.Configuration =? defaultConfiguration(),
 actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
   ) -> R {
 // create a resource using the provided configuration
 // connect the action handler
 // return the resource
   }
 }
 The main difference here is the strawman =? syntax, which would indicate 
 that “the default argument exists if there is a way the RHS can be 
 satisfied for some instances of the generic arguments; otherwise, there is 
 no default”, instead of today’s behavior where it would be an error. There 
 could be multiple overloads of defaultConfiguration and 
 defaultActionHandler (even ones that are themselves generic) and it would 
 do the right thing when there are matches and when there aren’t.
 
 I like this approach because it mostly takes advantage of existing 
 language features and is fairly lightweight in terms of how it’s expressed 
 in code compared to regular default arguments—we’d just need to design the 
 new operator and type-checker logic around it.
 
>>> 
>>> This is an interesting approach.  One advantage to something in this 
>>> direction is that it could support defining different defaults for the same 
>>> argument under different constraints by overloading the default argument 
>>> factories on their return type.
>>> 
>>> One concern I have is that it doesn’t allows us to clearly define under 
>>> which constraints a default argument is available.  I suspect this might be 
>>> problematic especially for public interfaces where source compatibility is 
>>> a concern.  
>> 
>> It's certainly an interesting idea but it would suggest that the constraints 
>> under which a default argument is available can change at runtime. I'm 
>> concerned, like you, that this is difficult to reason about. It is still 
>> unclear to me how widespread the underlying issue is that requires 
>> conditional default arguments, but the conversation thus far has been about 
>> compile-time constraints and Tony's design seems to envision much more than 
>> that.
> 
> This runtime/reasoning problem already exists today with default arguments, 
> because you can write something like this:
> 
> struct Foo {
>   static var defaultExponent = 2.0
> 
>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
> print(pow(x, exponent))
>   }
> }
> 
> Foo().raise(4)  // "16.0"
> Foo.defaultExponent = 3.0
> Foo().raise(4)  // "64.0"
> Swift lets you write a default value expression that references static (but 
> not instance) vars of the enclosing type, as well as anything else that’s 
> visible from that expression’s scope. Should people do this? Probably not, 
> for the reasons that you described.
> 
> But the point is that my example is no more harmful or difficult to reason 
> about than default arguments in the language today. My proposed solution in 
> no way changes the runtime behavior of default argument expressions. I’m not 
> envisioning anything more than what default arguments can already do except 
> for adding a way to choose different default factor

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 12:49 PM Matthew Johnson 
wrote:

> On Nov 27, 2017, at 1:24 PM, Tony Allevato 
> wrote:
>
>
>
> On Mon, Nov 27, 2017 at 11:12 AM Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>> On Nov 27, 2017, at 12:50 PM, Douglas Gregor  wrote:
>>
>>
>>
>> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>> As mentioned in my prior message, I currently have a PR open to update
>> the generics manifesto (https://github.com/apple/swift/pull/13012).  I
>> removed one topic from that update at Doug Gregor’s request that it be
>> discussed on the list first.
>>
>> The idea is to add the ability to make default arguments conditional
>> (i.e. depend on generic constraints).  It is currently possible to emulate
>> conditional default arguments using an overload set.  This is verbose,
>> especially when several arguments are involved.  Here is an example use
>> case using the overload method to emulate this feature:
>>
>> ```swift
>> protocol Resource {
>>   associatedtype Configuration
>>   associatedtype Action
>> }
>> struct ResourceDescription {
>>   func makeResource(with configuration: R.Configuration, actionHandler:
>> @escaping (R.Action) -> Void) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>>
>> extension ResourceDescription where R.Configuration == Void {
>>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
>> return makeResource(with: (), actionHandler: actionHandler)
>>   }
>> }
>>
>> extension ResourceDescription where R.Action == Never {
>>   func makeResource(with configuration: R.Configuration) -> R {
>> return makeResource(with: configuration, actionHandler: { _ in })
>>   }
>> }
>>
>> extension ResourceDescription where R.Configuration == Void, R.Action ==
>> Never {
>>   func makeResource() -> R {
>> return makeResource(with: (), actionHandler: { _ in })
>>   }
>> }
>>
>> ```
>>
>> Adding language support for defining these more directly would eliminate
>> a lot of boilerplate and reduce the need for overloads.
>>
>>
>> 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:
>>
>> protocol Resource {
>>   associatedtype Configuration
>>   associatedtype Action
>>
>> func defaultConfiguration() -> Configuration
>> func defaultHandler() -> ((R.Action) -> Void)
>>
>> }
>>
>>
>> This won’t work on its own for this use case because there is only a
>> valid default in relatively narrow (but common) cases.  For most values of
>> Configuration and Action an argument must be provided by the caller.
>> Xiaodi’s proposed syntax is the best fit (so far) for the use case I had in
>> mind.
>>
>
> Out of curiosity, what part of my proposed syntax misses the mark for your
> use case?
>
>
> I think it’s important that we be clear about when a default is and is not
> available.  Your syntax makes the availability of a default non-local.
>

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:

```
struct ResourceDescription {
  func defaultConfiguration() -> Void { return () }
  func defaultActionHandler() -> (Never) -> Void { return _ in }
  func makeResource(...) {
...
  }
}
```

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".



>
>
> 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.
>
>
> 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.  If we added this feature it would be relatively
> advanced and usually used by library developers.  While I would be pleased
> with more elegant syntax I’m not sure there is a better solution.  I prefer
> semantic clarity to syntactic elegance.
>

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.

For me, grafting on a second completely different default value syntax for
functions is hard to swall

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 1:24 PM, Tony Allevato  wrote:
> 
> 
> 
> On Mon, Nov 27, 2017 at 11:12 AM Matthew Johnson via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
>> On Nov 27, 2017, at 12:50 PM, Douglas Gregor > > wrote:
>> 
>> 
>> 
>>> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
>>> mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> As mentioned in my prior message, I currently have a PR open to update the 
>>> generics manifesto (https://github.com/apple/swift/pull/13012 
>>> ).  I removed one topic from 
>>> that update at Doug Gregor’s request that it be discussed on the list 
>>> first.  
>>> 
>>> The idea is to add the ability to make default arguments conditional (i.e. 
>>> depend on generic constraints).  It is currently possible to emulate 
>>> conditional default arguments using an overload set.  This is verbose, 
>>> especially when several arguments are involved.  Here is an example use 
>>> case using the overload method to emulate this feature:
>>> 
>>> ```swift
>>> protocol Resource {
>>>   associatedtype Configuration
>>>   associatedtype Action
>>> }
>>> struct ResourceDescription {
>>>   func makeResource(with configuration: R.Configuration, actionHandler: 
>>> @escaping (R.Action) -> Void) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>> 
>>> extension ResourceDescription where R.Configuration == Void {
>>>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
>>> return makeResource(with: (), actionHandler: actionHandler)
>>>   }
>>> }
>>> 
>>> extension ResourceDescription where R.Action == Never {
>>>   func makeResource(with configuration: R.Configuration) -> R {
>>> return makeResource(with: configuration, actionHandler: { _ in })
>>>   }
>>> }
>>> 
>>> extension ResourceDescription where R.Configuration == Void, R.Action == 
>>> Never {
>>>   func makeResource() -> R {
>>> return makeResource(with: (), actionHandler: { _ in })
>>>   }
>>> }
>>> 
>>> ```
>>> 
>>> Adding language support for defining these more directly would eliminate a 
>>> lot of boilerplate and reduce the need for overloads.
>> 
>> 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:
>> 
>>> protocol Resource {
>>>   associatedtype Configuration
>>>   associatedtype Action
>> func defaultConfiguration() -> Configuration
>> func defaultHandler() -> ((R.Action) -> Void)
>>> }
> 
> This won’t work on its own for this use case because there is only a valid 
> default in relatively narrow (but common) cases.  For most values of 
> Configuration and Action an argument must be provided by the caller.  
> Xiaodi’s proposed syntax is the best fit (so far) for the use case I had in 
> mind.  
> 
> Out of curiosity, what part of my proposed syntax misses the mark for your 
> use case?

I think it’s important that we be clear about when a default is and is not 
available.  Your syntax makes the availability of a default non-local.

> 
> 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.

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.  If we added this feature it would be relatively advanced and 
usually used by library developers.  While I would be pleased with more elegant 
syntax I’m not sure there is a better solution.  I prefer semantic clarity to 
syntactic elegance.

> 
> 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™, 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 alo

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Tony Allevato via swift-evolution
On Mon, Nov 27, 2017 at 11:12 AM Matthew Johnson via swift-evolution <
swift-evolution@swift.org> wrote:

> On Nov 27, 2017, at 12:50 PM, Douglas Gregor  wrote:
>
>
>
> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> As mentioned in my prior message, I currently have a PR open to update the
> generics manifesto (https://github.com/apple/swift/pull/13012).  I
> removed one topic from that update at Doug Gregor’s request that it be
> discussed on the list first.
>
> The idea is to add the ability to make default arguments conditional (i.e.
> depend on generic constraints).  It is currently possible to emulate
> conditional default arguments using an overload set.  This is verbose,
> especially when several arguments are involved.  Here is an example use
> case using the overload method to emulate this feature:
>
> ```swift
> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
> }
> struct ResourceDescription {
>   func makeResource(with configuration: R.Configuration, actionHandler:
> @escaping (R.Action) -> Void) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
>
> extension ResourceDescription where R.Configuration == Void {
>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
> return makeResource(with: (), actionHandler: actionHandler)
>   }
> }
>
> extension ResourceDescription where R.Action == Never {
>   func makeResource(with configuration: R.Configuration) -> R {
> return makeResource(with: configuration, actionHandler: { _ in })
>   }
> }
>
> extension ResourceDescription where R.Configuration == Void, R.Action ==
> Never {
>   func makeResource() -> R {
> return makeResource(with: (), actionHandler: { _ in })
>   }
> }
>
> ```
>
> Adding language support for defining these more directly would eliminate a
> lot of boilerplate and reduce the need for overloads.
>
>
> 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:
>
> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
>
> func defaultConfiguration() -> Configuration
> func defaultHandler() -> ((R.Action) -> Void)
>
> }
>
>
> This won’t work on its own for this use case because there is only a valid
> default in relatively narrow (but common) cases.  For most values of
> Configuration and Action an argument must be provided by the caller.
> Xiaodi’s proposed syntax is the best fit (so far) for the use case I had in
> mind.
>

Out of curiosity, what part of my proposed syntax misses the mark for your
use case?

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. "compile 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.

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™, 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:
https://github.com/allevato/icu-swift/blob/master/Sources/ICU/RuleBasedBreakCursor.swift#L208

With your proposed addition, I could hoist those closures into default
arguments based on the constraints instead of adding extensions.

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.



>
> 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.  For example, in the example above it would
> allow a resource to provide a nontrivial default configuration.
>
>
>  Doug mentioned that it may also help simplify associated type inference (
> ht

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Matthew Johnson via swift-evolution

> On Nov 27, 2017, at 12:50 PM, Douglas Gregor  wrote:
> 
> 
> 
>> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> As mentioned in my prior message, I currently have a PR open to update the 
>> generics manifesto (https://github.com/apple/swift/pull/13012 
>> ).  I removed one topic from that 
>> update at Doug Gregor’s request that it be discussed on the list first.  
>> 
>> The idea is to add the ability to make default arguments conditional (i.e. 
>> depend on generic constraints).  It is currently possible to emulate 
>> conditional default arguments using an overload set.  This is verbose, 
>> especially when several arguments are involved.  Here is an example use case 
>> using the overload method to emulate this feature:
>> 
>> ```swift
>> protocol Resource {
>>   associatedtype Configuration
>>   associatedtype Action
>> }
>> struct ResourceDescription {
>>   func makeResource(with configuration: R.Configuration, actionHandler: 
>> @escaping (R.Action) -> Void) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>> 
>> extension ResourceDescription where R.Configuration == Void {
>>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
>> return makeResource(with: (), actionHandler: actionHandler)
>>   }
>> }
>> 
>> extension ResourceDescription where R.Action == Never {
>>   func makeResource(with configuration: R.Configuration) -> R {
>> return makeResource(with: configuration, actionHandler: { _ in })
>>   }
>> }
>> 
>> extension ResourceDescription where R.Configuration == Void, R.Action == 
>> Never {
>>   func makeResource() -> R {
>> return makeResource(with: (), actionHandler: { _ in })
>>   }
>> }
>> 
>> ```
>> 
>> Adding language support for defining these more directly would eliminate a 
>> lot of boilerplate and reduce the need for overloads.
> 
> 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:
> 
>> protocol Resource {
>>   associatedtype Configuration
>>   associatedtype Action
> func defaultConfiguration() -> Configuration
> func defaultHandler() -> ((R.Action) -> Void)
>> }

This won’t work on its own for this use case because there is only a valid 
default in relatively narrow (but common) cases.  For most values of 
Configuration and Action an argument must be provided by the caller.  Xiaodi’s 
proposed syntax is the best fit (so far) for the use case I had in mind.  

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.  For example, in the example above it would allow a resource to 
provide a nontrivial default configuration.

> 
>>  Doug mentioned that it may also help simplify associated type inference 
>> (https://github.com/apple/swift/pull/13012#discussion_r152124535 
>> ).
> 
> 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).

I was wondering how it was related and wondered if it was somehow due to 
reduction in the size of the overload set.  If you have any ideas on changes 
that might help guide the inference algorithm somehow please start a new 
thread.  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.  Improving the 
reliability and predictability of inference is a very important topic!

> 
>   - Doug
> 
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-27 Thread Douglas Gregor via swift-evolution


> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
>  wrote:
> 
> As mentioned in my prior message, I currently have a PR open to update the 
> generics manifesto (https://github.com/apple/swift/pull/13012 
> ).  I removed one topic from that 
> update at Doug Gregor’s request that it be discussed on the list first.  
> 
> The idea is to add the ability to make default arguments conditional (i.e. 
> depend on generic constraints).  It is currently possible to emulate 
> conditional default arguments using an overload set.  This is verbose, 
> especially when several arguments are involved.  Here is an example use case 
> using the overload method to emulate this feature:
> 
> ```swift
> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
> }
> struct ResourceDescription {
>   func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> 
> extension ResourceDescription where R.Configuration == Void {
>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
> return makeResource(with: (), actionHandler: actionHandler)
>   }
> }
> 
> extension ResourceDescription where R.Action == Never {
>   func makeResource(with configuration: R.Configuration) -> R {
> return makeResource(with: configuration, actionHandler: { _ in })
>   }
> }
> 
> extension ResourceDescription where R.Configuration == Void, R.Action == 
> Never {
>   func makeResource() -> R {
> return makeResource(with: (), actionHandler: { _ in })
>   }
> }
> 
> ```
> 
> Adding language support for defining these more directly would eliminate a 
> lot of boilerplate and reduce the need for overloads.

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:

> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
func defaultConfiguration() -> Configuration
func defaultHandler() -> ((R.Action) -> Void)
> }


>  Doug mentioned that it may also help simplify associated type inference 
> (https://github.com/apple/swift/pull/13012#discussion_r152124535 
> ).

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).

- Doug


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-26 Thread Jonathan Hull via swift-evolution
I have wanted something similar to this for a while, and haven’t suggested it 
for a while for the same reason… I can’t really think of a good syntax.

My best idea so far is to put it after the function declaration but before the 
open brace:

func myCrazyFunction(myParam: T)->Int
@default myParam = 0 where T == Int.Self
@default myParam = “” where T == String.Self
{
//Function body goes here
}

When multiple options are available, it would match the most specific one that 
qualifies.  The idea I had would also allow it to have expressions on the rhs, 
and would potentially allow other input types outside of T (as long as the rhs 
returns T):

func evenCrazier(myParam: String)->Int
@default myParam = “\($0)” where myParam : CustomStringConvertible
{
//Function body goes here
}

There are some obvious issues with the syntax here (e.g. I am using $0), but I 
think the general idea is a really useful one.  That is, I am able to provide a 
conversion formula that lets someone pass anything which is 
customStringConvertible to this String parameter and have it pass the 
appropriate String to my actual function. I would use this constantly, and it 
would prevent the combinatorial explosion of overloads I have now…

This also gets rid of the need for the 
often-requested-but-never-going-to-happen union types:

enum MyEnum {
case int (Int)
case str (String)
}

func noNeedForUnion(_ intOrStr: MyEnum)
@default intOrStr = .int($0) where intOrStr == Int.Self
@default intOrStr = .str($0) where intOrStr == String.Self
{
//Function body here
}

noNeedForUnion(3) //This passes .int(3)
noNeedForUnion(“Hey”) //this passes .str(“Hey”)

I could see this making a bunch of frameworks nicer to use… (or at least nicer 
to write)

Thanks,
Jon


> On Nov 24, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
>  wrote:
> 
> As mentioned in my prior message, I currently have a PR open to update the 
> generics manifesto (https://github.com/apple/swift/pull/13012 
> ).  I removed one topic from that 
> update at Doug Gregor’s request that it be discussed on the list first.  
> 
> The idea is to add the ability to make default arguments conditional (i.e. 
> depend on generic constraints).  It is currently possible to emulate 
> conditional default arguments using an overload set.  This is verbose, 
> especially when several arguments are involved.  Here is an example use case 
> using the overload method to emulate this feature:
> 
> ```swift
> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
> }
> struct ResourceDescription {
>   func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> 
> extension ResourceDescription where R.Configuration == Void {
>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
> return makeResource(with: (), actionHandler: actionHandler)
>   }
> }
> 
> extension ResourceDescription where R.Action == Never {
>   func makeResource(with configuration: R.Configuration) -> R {
> return makeResource(with: configuration, actionHandler: { _ in })
>   }
> }
> 
> extension ResourceDescription where R.Configuration == Void, R.Action == 
> Never {
>   func makeResource() -> R {
> return makeResource(with: (), actionHandler: { _ in })
>   }
> }
> 
> ```
> 
> Adding language support for defining these more directly would eliminate a 
> lot of boilerplate and reduce the need for overloads.  Doug mentioned that it 
> may also help simplify associated type inference 
> (https://github.com/apple/swift/pull/13012#discussion_r152124535 
> ).
> 
> The reason that I call this a pre-pitch and one reason Doug requested it be 
> discussed on list is that I haven’t thought of a good way to express this 
> syntactically.  I am interested in hearing general feedback on the idea.  I 
> am also looking for syntax suggestions.
> 
> Matthew
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Tony Allevato via swift-evolution
Yeah, I realized that wasn’t true after sending it—it could come from an
imported module, as long as it’s visible. Still, I imagine that retroactive
conformance wouldn’t be an issue because when the compiler type checks the
default at the declaration site, it would only see declarations that it
already knows about.

As for locality, yes, that’s a drawback of my approach, but I still would
prefer it to an odd-looking annotation based approach that introduced a
whole new and different syntax for certain kinds of default arguments.

On Sat, Nov 25, 2017 at 3:18 PM Xiaodi Wu  wrote:

> On Sat, Nov 25, 2017 at 4:44 PM, Tony Allevato 
> wrote:
>
>> On Sat, Nov 25, 2017 at 2:35 PM Xiaodi Wu  wrote:
>>
>>> On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato 
>>> wrote:
>>>
 On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:

> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
> wrote:
>
>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>>
>> It's kludgy, but we could have something like:
>>>
>>> ```
>>> @defaultArgument(configuration = (), where R.Configuration == Void)
>>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>>> func makeResource(with configuration: R.Configuration,
>>> actionHandler: @escaping (R.Action) -> Void) -> R { ... }
>>> ```
>>>
>>> I don't like that we'd be setting a default argument on something
>>> lexically before even encountering it in the declaration, but it's
>>> serviceable.
>>>
>>
>>
>> What if we could take advantage of the fact that you can have
>> non-constant expressions in default arguments? Overload resolution could
>> already do most of the job—what we need on top of that is a way for the
>> author to say that “if no overload matches, then it’s not an error—just
>> don’t have a default argument in that case”. Something like SFINAE in 
>> C++,
>> but more explicit.
>>
>> I’m imagining something like this:
>>
>> func defaultConfiguration() -> Void {
>>   return ()
>> }
>> func defaultActionHandler() -> (Never) -> Void {
>>   return { _ in }
>> }
>> struct ResourceDescription {
>>   func makeResource(
>> with configuration: R.Configuration *=?* defaultConfiguration(),
>> actionHandler: @escaping (R.Action) -> Void *=?* 
>> defaultActionHandler()
>>   ) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>>
>> The main difference here is the strawman =? syntax, which would
>> indicate that “the default argument exists if there is a way the RHS can 
>> be
>> satisfied for some instances of the generic arguments; otherwise, there 
>> is
>> no default”, instead of today’s behavior where it would be an error. 
>> There
>> could be multiple overloads of defaultConfiguration and
>> defaultActionHandler (even ones that are themselves generic) and it
>> would do the right thing when there are matches and when there aren’t.
>>
>> I like this approach because it mostly takes advantage of existing
>> language features and is fairly lightweight in terms of how it’s 
>> expressed
>> in code compared to regular default arguments—we’d just need to design 
>> the
>> new operator and type-checker logic around it.
>>
>> This is an interesting approach.  One advantage to something in this
>> direction is that it could support defining different defaults for the 
>> same
>> argument under different constraints by overloading the default argument
>> factories on their return type.
>>
>> One concern I have is that it doesn’t allows us to clearly define
>> under which constraints a default argument is available.  I suspect this
>> might be problematic especially for public interfaces where source
>> compatibility is a concern.
>>
>
> It's certainly an interesting idea but it would suggest that the
> constraints under which a default argument is available can change at
> runtime. I'm concerned, like you, that this is difficult to reason about.
> It is still unclear to me how widespread the underlying issue is that
> requires conditional default arguments, but the conversation thus far has
> been about compile-time constraints and Tony's design seems to envision
> much more than that.
>

 This runtime/reasoning problem *already exists* today with default
 arguments, because you can write something like this:

 struct Foo {
   static var defaultExponent = 2.0

   func raise(_ x: Double, to exponent: Double = defaultExponent) {
   

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Xiaodi Wu via swift-evolution
On Sat, Nov 25, 2017 at 4:44 PM, Tony Allevato 
wrote:

> On Sat, Nov 25, 2017 at 2:35 PM Xiaodi Wu  wrote:
>
>> On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato 
>> wrote:
>>
>>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>>>
 On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
 wrote:

> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
> It's kludgy, but we could have something like:
>>
>> ```
>> @defaultArgument(configuration = (), where R.Configuration == Void)
>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>> func makeResource(with configuration: R.Configuration, actionHandler:
>> @escaping (R.Action) -> Void) -> R { ... }
>> ```
>>
>> I don't like that we'd be setting a default argument on something
>> lexically before even encountering it in the declaration, but it's
>> serviceable.
>>
>
>
> What if we could take advantage of the fact that you can have
> non-constant expressions in default arguments? Overload resolution could
> already do most of the job—what we need on top of that is a way for the
> author to say that “if no overload matches, then it’s not an error—just
> don’t have a default argument in that case”. Something like SFINAE in C++,
> but more explicit.
>
> I’m imagining something like this:
>
> func defaultConfiguration() -> Void {
>   return ()
> }
> func defaultActionHandler() -> (Never) -> Void {
>   return { _ in }
> }
> struct ResourceDescription {
>   func makeResource(
> with configuration: R.Configuration *=?* defaultConfiguration(),
> actionHandler: @escaping (R.Action) -> Void *=?* 
> defaultActionHandler()
>   ) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
>
> The main difference here is the strawman =? syntax, which would
> indicate that “the default argument exists if there is a way the RHS can 
> be
> satisfied for some instances of the generic arguments; otherwise, there is
> no default”, instead of today’s behavior where it would be an error. There
> could be multiple overloads of defaultConfiguration and
> defaultActionHandler (even ones that are themselves generic) and it
> would do the right thing when there are matches and when there aren’t.
>
> I like this approach because it mostly takes advantage of existing
> language features and is fairly lightweight in terms of how it’s expressed
> in code compared to regular default arguments—we’d just need to design the
> new operator and type-checker logic around it.
>
> This is an interesting approach.  One advantage to something in this
> direction is that it could support defining different defaults for the 
> same
> argument under different constraints by overloading the default argument
> factories on their return type.
>
> One concern I have is that it doesn’t allows us to clearly define
> under which constraints a default argument is available.  I suspect this
> might be problematic especially for public interfaces where source
> compatibility is a concern.
>

 It's certainly an interesting idea but it would suggest that the
 constraints under which a default argument is available can change at
 runtime. I'm concerned, like you, that this is difficult to reason about.
 It is still unclear to me how widespread the underlying issue is that
 requires conditional default arguments, but the conversation thus far has
 been about compile-time constraints and Tony's design seems to envision
 much more than that.

>>>
>>> This runtime/reasoning problem *already exists* today with default
>>> arguments, because you can write something like this:
>>>
>>> struct Foo {
>>>   static var defaultExponent = 2.0
>>>
>>>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
>>> print(pow(x, exponent))
>>>   }
>>> }
>>> Foo().raise(4)  // "16.0"Foo.defaultExponent = 3.0Foo().raise(4)  // "64.0"
>>>
>>> Swift lets you write a default value expression that references static
>>> (but not instance) vars of the enclosing type, as well as anything else
>>> that’s visible from that expression’s scope. Should people do this?
>>> Probably not, for the reasons that you described.
>>>
>>> But the point is that my example is no more harmful or difficult to
>>> reason about than default arguments in the language today. My proposed
>>> solution *in no way* changes the runtime behavior of default argument
>>> expressions. I’m not envisioning anything more than what default arguments
>>> can already do except for

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Dave Abrahams via swift-evolution
This sort of “it compiles if it’s syntactically valid, regardless of declared 
constraints” thing is deliberately avoided in Swift’s generics design with good 
reason; it’s possible that in this instance there are no problems, but I’m 
skeptical. 

Sent from my iPhone

> On Nov 25, 2017, at 1:16 PM, Xiaodi Wu via swift-evolution 
>  wrote:
> 
> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson  wrote:
>>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
>>>  wrote:
>>> 
>>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
>>>  wrote:
>>> 
>>> 
>>> 
 It's kludgy, but we could have something like:
 
 ```
 @defaultArgument(configuration = (), where R.Configuration == Void)
 @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
 func makeResource(with configuration: R.Configuration, actionHandler: 
 @escaping (R.Action) -> Void) -> R { ... }
 ```
 
 I don't like that we'd be setting a default argument on something 
 lexically before even encountering it in the declaration, but it's 
 serviceable.
>>> 
>>> 
>>> What if we could take advantage of the fact that you can have non-constant 
>>> expressions in default arguments? Overload resolution could already do most 
>>> of the job—what we need on top of that is a way for the author to say that 
>>> “if no overload matches, then it’s not an error—just don’t have a default 
>>> argument in that case”. Something like SFINAE in C++, but more explicit.
>>> 
>>> I’m imagining something like this:
>>> 
>>> func defaultConfiguration() -> Void {
>>>   return ()
>>> }
>>> 
>>> func defaultActionHandler() -> (Never) -> Void {
>>>   return { _ in }
>>> }
>>> 
>>> struct ResourceDescription {
>>>   func makeResource(
>>> with configuration: R.Configuration =? defaultConfiguration(),
>>> actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
>>>   ) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>> The main difference here is the strawman =? syntax, which would indicate 
>>> that “the default argument exists if there is a way the RHS can be 
>>> satisfied for some instances of the generic arguments; otherwise, there is 
>>> no default”, instead of today’s behavior where it would be an error. There 
>>> could be multiple overloads of defaultConfiguration and 
>>> defaultActionHandler (even ones that are themselves generic) and it would 
>>> do the right thing when there are matches and when there aren’t.
>>> 
>>> I like this approach because it mostly takes advantage of existing language 
>>> features and is fairly lightweight in terms of how it’s expressed in code 
>>> compared to regular default arguments—we’d just need to design the new 
>>> operator and type-checker logic around it.
>>> 
>> 
>> This is an interesting approach.  One advantage to something in this 
>> direction is that it could support defining different defaults for the same 
>> argument under different constraints by overloading the default argument 
>> factories on their return type.
>> 
>> One concern I have is that it doesn’t allows us to clearly define under 
>> which constraints a default argument is available.  I suspect this might be 
>> problematic especially for public interfaces where source compatibility is a 
>> concern.  
> 
> It's certainly an interesting idea but it would suggest that the constraints 
> under which a default argument is available can change at runtime. I'm 
> concerned, like you, that this is difficult to reason about. It is still 
> unclear to me how widespread the underlying issue is that requires 
> conditional default arguments, but the conversation thus far has been about 
> compile-time constraints and Tony's design seems to envision much more than 
> that.
> 
>> I think I prefer Xiaodi’s suggestion for that reason.  His approach could 
>> also support multiple defaults for the same parameter as long as the 
>> constraints are not allowed to overlap (overlapping constraints would result 
>> in ambiguity similar to ambiguous overload resolution) or an explicit 
>> argument is required if they do.
>> 
>>> 
>>> 
>>> 
 
 
> On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution 
>  wrote:
> I am all for this. are many types where there is an obvious 'zero' or 
> 'default' value and the ability to express "use that when possible" 
> without an overload is welcome.
> 
> 
> The best thing that I can think of right now, in terms of syntax, is 
> actually using @overload
> 
> ```
> struct ResourceDescription {
> 
>   func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R 
>  @overload(R.Configuration == Void) func makeResource(actionHandler: 
> @escaping (R.Action) -> Void) -> R
> @overload(R.Action == Never)  func makeResour

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Tony Allevato via swift-evolution
On Sat, Nov 25, 2017 at 2:35 PM Xiaodi Wu  wrote:

> On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato 
> wrote:
>
>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>>
>>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
>>> wrote:
>>>
 On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
 swift-evolution@swift.org> wrote:

 On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
 swift-evolution@swift.org> wrote:


 It's kludgy, but we could have something like:
>
> ```
> @defaultArgument(configuration = (), where R.Configuration == Void)
> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
> func makeResource(with configuration: R.Configuration, actionHandler:
> @escaping (R.Action) -> Void) -> R { ... }
> ```
>
> I don't like that we'd be setting a default argument on something
> lexically before even encountering it in the declaration, but it's
> serviceable.
>


 What if we could take advantage of the fact that you can have
 non-constant expressions in default arguments? Overload resolution could
 already do most of the job—what we need on top of that is a way for the
 author to say that “if no overload matches, then it’s not an error—just
 don’t have a default argument in that case”. Something like SFINAE in C++,
 but more explicit.

 I’m imagining something like this:

 func defaultConfiguration() -> Void {
   return ()
 }
 func defaultActionHandler() -> (Never) -> Void {
   return { _ in }
 }
 struct ResourceDescription {
   func makeResource(
 with configuration: R.Configuration *=?* defaultConfiguration(),
 actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
   ) -> R {
 // create a resource using the provided configuration
 // connect the action handler
 // return the resource
   }
 }

 The main difference here is the strawman =? syntax, which would
 indicate that “the default argument exists if there is a way the RHS can be
 satisfied for some instances of the generic arguments; otherwise, there is
 no default”, instead of today’s behavior where it would be an error. There
 could be multiple overloads of defaultConfiguration and
 defaultActionHandler (even ones that are themselves generic) and it
 would do the right thing when there are matches and when there aren’t.

 I like this approach because it mostly takes advantage of existing
 language features and is fairly lightweight in terms of how it’s expressed
 in code compared to regular default arguments—we’d just need to design the
 new operator and type-checker logic around it.

 This is an interesting approach.  One advantage to something in this
 direction is that it could support defining different defaults for the same
 argument under different constraints by overloading the default argument
 factories on their return type.

 One concern I have is that it doesn’t allows us to clearly define under
 which constraints a default argument is available.  I suspect this might be
 problematic especially for public interfaces where source compatibility is
 a concern.

>>>
>>> It's certainly an interesting idea but it would suggest that the
>>> constraints under which a default argument is available can change at
>>> runtime. I'm concerned, like you, that this is difficult to reason about.
>>> It is still unclear to me how widespread the underlying issue is that
>>> requires conditional default arguments, but the conversation thus far has
>>> been about compile-time constraints and Tony's design seems to envision
>>> much more than that.
>>>
>>
>> This runtime/reasoning problem *already exists* today with default
>> arguments, because you can write something like this:
>>
>> struct Foo {
>>   static var defaultExponent = 2.0
>>
>>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
>> print(pow(x, exponent))
>>   }
>> }
>> Foo().raise(4)  // "16.0"Foo.defaultExponent = 3.0Foo().raise(4)  // "64.0"
>>
>> Swift lets you write a default value expression that references static
>> (but not instance) vars of the enclosing type, as well as anything else
>> that’s visible from that expression’s scope. Should people do this?
>> Probably not, for the reasons that you described.
>>
>> But the point is that my example is no more harmful or difficult to
>> reason about than default arguments in the language today. My proposed
>> solution *in no way* changes the runtime behavior of default argument
>> expressions. I’m not envisioning anything more than what default arguments
>> can already do except for adding a way to choose different default
>> factories (or choose none without error) based on the *static* types of
>> the generic arguments that are bound at a particular call site.
>>
> Un

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Xiaodi Wu via swift-evolution
On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato 
wrote:

> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:
>
>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
>> wrote:
>>
>>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
>>>
>>> It's kludgy, but we could have something like:

 ```
 @defaultArgument(configuration = (), where R.Configuration == Void)
 @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
 func makeResource(with configuration: R.Configuration, actionHandler:
 @escaping (R.Action) -> Void) -> R { ... }
 ```

 I don't like that we'd be setting a default argument on something
 lexically before even encountering it in the declaration, but it's
 serviceable.

>>>
>>>
>>> What if we could take advantage of the fact that you can have
>>> non-constant expressions in default arguments? Overload resolution could
>>> already do most of the job—what we need on top of that is a way for the
>>> author to say that “if no overload matches, then it’s not an error—just
>>> don’t have a default argument in that case”. Something like SFINAE in C++,
>>> but more explicit.
>>>
>>> I’m imagining something like this:
>>>
>>> func defaultConfiguration() -> Void {
>>>   return ()
>>> }
>>> func defaultActionHandler() -> (Never) -> Void {
>>>   return { _ in }
>>> }
>>> struct ResourceDescription {
>>>   func makeResource(
>>> with configuration: R.Configuration *=?* defaultConfiguration(),
>>> actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
>>>   ) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>>
>>> The main difference here is the strawman =? syntax, which would
>>> indicate that “the default argument exists if there is a way the RHS can be
>>> satisfied for some instances of the generic arguments; otherwise, there is
>>> no default”, instead of today’s behavior where it would be an error. There
>>> could be multiple overloads of defaultConfiguration and
>>> defaultActionHandler (even ones that are themselves generic) and it
>>> would do the right thing when there are matches and when there aren’t.
>>>
>>> I like this approach because it mostly takes advantage of existing
>>> language features and is fairly lightweight in terms of how it’s expressed
>>> in code compared to regular default arguments—we’d just need to design the
>>> new operator and type-checker logic around it.
>>>
>>> This is an interesting approach.  One advantage to something in this
>>> direction is that it could support defining different defaults for the same
>>> argument under different constraints by overloading the default argument
>>> factories on their return type.
>>>
>>> One concern I have is that it doesn’t allows us to clearly define under
>>> which constraints a default argument is available.  I suspect this might be
>>> problematic especially for public interfaces where source compatibility is
>>> a concern.
>>>
>>
>> It's certainly an interesting idea but it would suggest that the
>> constraints under which a default argument is available can change at
>> runtime. I'm concerned, like you, that this is difficult to reason about.
>> It is still unclear to me how widespread the underlying issue is that
>> requires conditional default arguments, but the conversation thus far has
>> been about compile-time constraints and Tony's design seems to envision
>> much more than that.
>>
>
> This runtime/reasoning problem *already exists* today with default
> arguments, because you can write something like this:
>
> struct Foo {
>   static var defaultExponent = 2.0
>
>   func raise(_ x: Double, to exponent: Double = defaultExponent) {
> print(pow(x, exponent))
>   }
> }
> Foo().raise(4)  // "16.0"Foo.defaultExponent = 3.0Foo().raise(4)  // "64.0"
>
> Swift lets you write a default value expression that references static
> (but not instance) vars of the enclosing type, as well as anything else
> that’s visible from that expression’s scope. Should people do this?
> Probably not, for the reasons that you described.
>
> But the point is that my example is no more harmful or difficult to reason
> about than default arguments in the language today. My proposed solution *in
> no way* changes the runtime behavior of default argument expressions. I’m
> not envisioning anything more than what default arguments can already do
> except for adding a way to choose different default factories (or choose
> none without error) based on the *static* types of the generic arguments
> that are bound at a particular call site.
>
Unless I misunderstand, with your example, a method would retroactively
gain a default argument if someone retroactively defines a function in an
extension. Is that not th

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Tony Allevato via swift-evolution
On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu  wrote:

> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
> wrote:
>
>> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>>
>> It's kludgy, but we could have something like:
>>>
>>> ```
>>> @defaultArgument(configuration = (), where R.Configuration == Void)
>>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>>> func makeResource(with configuration: R.Configuration, actionHandler:
>>> @escaping (R.Action) -> Void) -> R { ... }
>>> ```
>>>
>>> I don't like that we'd be setting a default argument on something
>>> lexically before even encountering it in the declaration, but it's
>>> serviceable.
>>>
>>
>>
>> What if we could take advantage of the fact that you can have
>> non-constant expressions in default arguments? Overload resolution could
>> already do most of the job—what we need on top of that is a way for the
>> author to say that “if no overload matches, then it’s not an error—just
>> don’t have a default argument in that case”. Something like SFINAE in C++,
>> but more explicit.
>>
>> I’m imagining something like this:
>>
>> func defaultConfiguration() -> Void {
>>   return ()
>> }
>> func defaultActionHandler() -> (Never) -> Void {
>>   return { _ in }
>> }
>> struct ResourceDescription {
>>   func makeResource(
>> with configuration: R.Configuration *=?* defaultConfiguration(),
>> actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
>>   ) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>>
>> The main difference here is the strawman =? syntax, which would indicate
>> that “the default argument exists if there is a way the RHS can be
>> satisfied for some instances of the generic arguments; otherwise, there is
>> no default”, instead of today’s behavior where it would be an error. There
>> could be multiple overloads of defaultConfiguration and
>> defaultActionHandler (even ones that are themselves generic) and it
>> would do the right thing when there are matches and when there aren’t.
>>
>> I like this approach because it mostly takes advantage of existing
>> language features and is fairly lightweight in terms of how it’s expressed
>> in code compared to regular default arguments—we’d just need to design the
>> new operator and type-checker logic around it.
>>
>> This is an interesting approach.  One advantage to something in this
>> direction is that it could support defining different defaults for the same
>> argument under different constraints by overloading the default argument
>> factories on their return type.
>>
>> One concern I have is that it doesn’t allows us to clearly define under
>> which constraints a default argument is available.  I suspect this might be
>> problematic especially for public interfaces where source compatibility is
>> a concern.
>>
>
> It's certainly an interesting idea but it would suggest that the
> constraints under which a default argument is available can change at
> runtime. I'm concerned, like you, that this is difficult to reason about.
> It is still unclear to me how widespread the underlying issue is that
> requires conditional default arguments, but the conversation thus far has
> been about compile-time constraints and Tony's design seems to envision
> much more than that.
>

This runtime/reasoning problem *already exists* today with default
arguments, because you can write something like this:

struct Foo {
  static var defaultExponent = 2.0

  func raise(_ x: Double, to exponent: Double = defaultExponent) {
print(pow(x, exponent))
  }
}
Foo().raise(4)  // "16.0"Foo.defaultExponent = 3.0Foo().raise(4)  // "64.0"

Swift lets you write a default value expression that references static (but
not instance) vars of the enclosing type, as well as anything else that’s
visible from that expression’s scope. Should people do this? Probably not,
for the reasons that you described.

But the point is that my example is no more harmful or difficult to reason
about than default arguments in the language today. My proposed solution *in
no way* changes the runtime behavior of default argument expressions. I’m
not envisioning anything more than what default arguments can already do
except for adding a way to choose different default factories (or choose
none without error) based on the *static* types of the generic arguments
that are bound at a particular call site.
​


> I think I prefer Xiaodi’s suggestion for that reason.  His approach could
>> also support multiple defaults for the same parameter as long as the
>> constraints are not allowed to overlap (overlapping constraints would
>> result in ambiguity similar to ambiguous overload resolution) or an
>> explicit argument is required if they do.
>>
>>
>>
>>
>>
>>>

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Xiaodi Wu via swift-evolution
On Sat, Nov 25, 2017 at 15:06 Matthew Johnson 
wrote:

> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
> It's kludgy, but we could have something like:
>>
>> ```
>> @defaultArgument(configuration = (), where R.Configuration == Void)
>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>> func makeResource(with configuration: R.Configuration, actionHandler:
>> @escaping (R.Action) -> Void) -> R { ... }
>> ```
>>
>> I don't like that we'd be setting a default argument on something
>> lexically before even encountering it in the declaration, but it's
>> serviceable.
>>
>
>
> What if we could take advantage of the fact that you can have non-constant
> expressions in default arguments? Overload resolution could already do most
> of the job—what we need on top of that is a way for the author to say that
> “if no overload matches, then it’s not an error—just don’t have a default
> argument in that case”. Something like SFINAE in C++, but more explicit.
>
> I’m imagining something like this:
>
> func defaultConfiguration() -> Void {
>   return ()
> }
> func defaultActionHandler() -> (Never) -> Void {
>   return { _ in }
> }
> struct ResourceDescription {
>   func makeResource(
> with configuration: R.Configuration *=?* defaultConfiguration(),
> actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
>   ) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
>
> The main difference here is the strawman =? syntax, which would indicate
> that “the default argument exists if there is a way the RHS can be
> satisfied for some instances of the generic arguments; otherwise, there is
> no default”, instead of today’s behavior where it would be an error. There
> could be multiple overloads of defaultConfiguration and
> defaultActionHandler (even ones that are themselves generic) and it would
> do the right thing when there are matches and when there aren’t.
>
> I like this approach because it mostly takes advantage of existing
> language features and is fairly lightweight in terms of how it’s expressed
> in code compared to regular default arguments—we’d just need to design the
> new operator and type-checker logic around it.
>
> This is an interesting approach.  One advantage to something in this
> direction is that it could support defining different defaults for the same
> argument under different constraints by overloading the default argument
> factories on their return type.
>
> One concern I have is that it doesn’t allows us to clearly define under
> which constraints a default argument is available.  I suspect this might be
> problematic especially for public interfaces where source compatibility is
> a concern.
>

It's certainly an interesting idea but it would suggest that the
constraints under which a default argument is available can change at
runtime. I'm concerned, like you, that this is difficult to reason about.
It is still unclear to me how widespread the underlying issue is that
requires conditional default arguments, but the conversation thus far has
been about compile-time constraints and Tony's design seems to envision
much more than that.

I think I prefer Xiaodi’s suggestion for that reason.  His approach could
> also support multiple defaults for the same parameter as long as the
> constraints are not allowed to overlap (overlapping constraints would
> result in ambiguity similar to ambiguous overload resolution) or an
> explicit argument is required if they do.
>
>
>
>
>
>>
>> On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>>> I am all for this. are many types where there is an obvious 'zero' or
>>> 'default' value and the ability to express "use that when possible" without
>>> an overload is welcome.
>>>
>>>
>>> The best thing that I can think of right now, in terms of syntax, is
>>> actually using @overload
>>>
>>> ```
>>> struct ResourceDescription {
>>>
>>>   func makeResource(with configuration: R.Configuration, actionHandler:
>>> @escaping (R.Action) -> Void) -> R
>>>  @overload(R.Configuration == Void) func makeResource(actionHandler:
>>> @escaping (R.Action) -> Void) -> R
>>> @overload(R.Action == Never)  func makeResource(with configuration:
>>> R.Configuration) -> R
>>> {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>> ```
>>>
>>>
>>> This isn't great though…
>>>
>>> On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution <
>>> swift-evolution@swift.org> wrote:
>>>
 As mentioned in my prior message, I currently have a PR open to update
 the generics manifesto (https://github.com/apple/swift/pull/13012).  I
 removed on

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Thorsten Seitz via swift-evolution
I like Tony's idea. Maybe the default argument functions could even take 
arguments 
with earlier parameters from the parameter list.

-Thorsten 


> Am 25.11.2017 um 20:28 schrieb Tony Allevato via swift-evolution 
> :
> 
>> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
>>  wrote:
>> 
>> It's kludgy, but we could have something like:
>> 
>> ```
>> @defaultArgument(configuration = (), where R.Configuration == Void)
>> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
>> func makeResource(with configuration: R.Configuration, actionHandler: 
>> @escaping (R.Action) -> Void) -> R { ... }
>> ```
>> 
>> I don't like that we'd be setting a default argument on something lexically 
>> before even encountering it in the declaration, but it's serviceable.
> 
> What if we could take advantage of the fact that you can have non-constant 
> expressions in default arguments? Overload resolution could already do most 
> of the job—what we need on top of that is a way for the author to say that 
> “if no overload matches, then it’s not an error—just don’t have a default 
> argument in that case”. Something like SFINAE in C++, but more explicit.
> 
> I’m imagining something like this:
> 
> func defaultConfiguration() -> Void {
>   return ()
> }
> 
> func defaultActionHandler() -> (Never) -> Void {
>   return { _ in }
> }
> 
> struct ResourceDescription {
>   func makeResource(
> with configuration: R.Configuration =? defaultConfiguration(),
> actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
>   ) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> The main difference here is the strawman =? syntax, which would indicate that 
> “the default argument exists if there is a way the RHS can be satisfied for 
> some instances of the generic arguments; otherwise, there is no default”, 
> instead of today’s behavior where it would be an error. There could be 
> multiple overloads of defaultConfiguration and defaultActionHandler (even 
> ones that are themselves generic) and it would do the right thing when there 
> are matches and when there aren’t.
> 
> I like this approach because it mostly takes advantage of existing language 
> features and is fairly lightweight in terms of how it’s expressed in code 
> compared to regular default arguments—we’d just need to design the new 
> operator and type-checker logic around it.
> 
> 
> 
>> 
>> 
>>> On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution 
>>>  wrote:
>>> I am all for this. are many types where there is an obvious 'zero' or 
>>> 'default' value and the ability to express "use that when possible" without 
>>> an overload is welcome.
>>> 
>>> 
>>> The best thing that I can think of right now, in terms of syntax, is 
>>> actually using @overload
>>> 
>>> ```
>>> struct ResourceDescription {
>>> 
>>>   func makeResource(with configuration: R.Configuration, actionHandler: 
>>> @escaping (R.Action) -> Void) -> R 
>>>  @overload(R.Configuration == Void) func makeResource(actionHandler: 
>>> @escaping (R.Action) -> Void) -> R
>>> @overload(R.Action == Never)  func makeResource(with configuration: 
>>> R.Configuration) -> R
>>> {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>> ```
>>> 
>>> 
>>> This isn't great though…
>>> 
 On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution 
  wrote:
 As mentioned in my prior message, I currently have a PR open to update the 
 generics manifesto (https://github.com/apple/swift/pull/13012).  I removed 
 one topic from that update at Doug Gregor’s request that it be discussed 
 on the list first.  
 
 The idea is to add the ability to make default arguments conditional (i.e. 
 depend on generic constraints).  It is currently possible to emulate 
 conditional default arguments using an overload set.  This is verbose, 
 especially when several arguments are involved.  Here is an example use 
 case using the overload method to emulate this feature:
 
 ```swift
 protocol Resource {
   associatedtype Configuration
   associatedtype Action
 }
 struct ResourceDescription {
   func makeResource(with configuration: R.Configuration, actionHandler: 
 @escaping (R.Action) -> Void) -> R {
 // create a resource using the provided configuration
 // connect the action handler
 // return the resource
   }
 }
 
 extension ResourceDescription where R.Configuration == Void {
   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
 return makeResource(with: (), actionHandler: actionHandler)
   }
 }
 
 extension ResourceDescription where R.Action == Never {
   func makeResource(with configuration: R.Configuration) -> R {
>>

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Matthew Johnson via swift-evolution

> On Nov 25, 2017, at 1:28 PM, Tony Allevato via swift-evolution 
>  wrote:
> 
> On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> 
> 
> 
> It's kludgy, but we could have something like:
> 
> ```
> @defaultArgument(configuration = (), where R.Configuration == Void)
> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
> func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R { ... }
> ```
> 
> I don't like that we'd be setting a default argument on something lexically 
> before even encountering it in the declaration, but it's serviceable.
> 
> 
> What if we could take advantage of the fact that you can have non-constant 
> expressions in default arguments? Overload resolution could already do most 
> of the job—what we need on top of that is a way for the author to say that 
> “if no overload matches, then it’s not an error—just don’t have a default 
> argument in that case”. Something like SFINAE in C++, but more explicit.
> 
> I’m imagining something like this:
> 
> func defaultConfiguration() -> Void {
>   return ()
> }
> 
> func defaultActionHandler() -> (Never) -> Void {
>   return { _ in }
> }
> 
> struct ResourceDescription {
>   func makeResource(
> with configuration: R.Configuration =? defaultConfiguration(),
> actionHandler: @escaping (R.Action) -> Void =? defaultActionHandler()
>   ) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> The main difference here is the strawman =? syntax, which would indicate that 
> “the default argument exists if there is a way the RHS can be satisfied for 
> some instances of the generic arguments; otherwise, there is no default”, 
> instead of today’s behavior where it would be an error. There could be 
> multiple overloads of defaultConfiguration and defaultActionHandler (even 
> ones that are themselves generic) and it would do the right thing when there 
> are matches and when there aren’t.
> 
> I like this approach because it mostly takes advantage of existing language 
> features and is fairly lightweight in terms of how it’s expressed in code 
> compared to regular default arguments—we’d just need to design the new 
> operator and type-checker logic around it.
> 
This is an interesting approach.  One advantage to something in this direction 
is that it could support defining different defaults for the same argument 
under different constraints by overloading the default argument factories on 
their return type.

One concern I have is that it doesn’t allows us to clearly define under which 
constraints a default argument is available.  I suspect this might be 
problematic especially for public interfaces where source compatibility is a 
concern.  

I think I prefer Xiaodi’s suggestion for that reason.  His approach could also 
support multiple defaults for the same parameter as long as the constraints are 
not allowed to overlap (overlapping constraints would result in ambiguity 
similar to ambiguous overload resolution) or an explicit argument is required 
if they do.

> 
> 
> 
> 
> 
> On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> I am all for this. are many types where there is an obvious 'zero' or 
> 'default' value and the ability to express "use that when possible" without 
> an overload is welcome.
> 
> 
> The best thing that I can think of right now, in terms of syntax, is actually 
> using @overload
> 
> ```
> struct ResourceDescription {
> 
>   func makeResource(with configuration: R.Configuration, actionHandler: 
> @escaping (R.Action) -> Void) -> R 
>  @overload(R.Configuration == Void) func makeResource(actionHandler: 
> @escaping (R.Action) -> Void) -> R
> @overload(R.Action == Never)  func makeResource(with configuration: 
> R.Configuration) -> R
> {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> ```
> 
> 
> This isn't great though…
> 
> On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> As mentioned in my prior message, I currently have a PR open to update the 
> generics manifesto (https://github.com/apple/swift/pull/13012 
> ).  I removed one topic from that 
> update at Doug Gregor’s request that it be discussed on the list first.  
> 
> The idea is to add the ability to make default arguments conditional (i.e. 
> depend on generic constraints).  It is currently possible to emulate 
> conditional default arguments using an overload set.  This is verbose, 
> especially when several arguments are involved.  Here is an example use case 
> using the overload method to emulate this feature:
> 
> ```swift
> protocol Resource {
>   associatedtype Configuration

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-25 Thread Tony Allevato via swift-evolution
On Fri, Nov 24, 2017 at 7:18 PM Xiaodi Wu via swift-evolution <
swift-evolution@swift.org> wrote:

It's kludgy, but we could have something like:
>
> ```
> @defaultArgument(configuration = (), where R.Configuration == Void)
> @defaultArgument(actionHandler = { _ in }, where R.Action == Never)
> func makeResource(with configuration: R.Configuration, actionHandler:
> @escaping (R.Action) -> Void) -> R { ... }
> ```
>
> I don't like that we'd be setting a default argument on something
> lexically before even encountering it in the declaration, but it's
> serviceable.
>
What if we could take advantage of the fact that you can have non-constant
expressions in default arguments? Overload resolution could already do most
of the job—what we need on top of that is a way for the author to say that
“if no overload matches, then it’s not an error—just don’t have a default
argument in that case”. Something like SFINAE in C++, but more explicit.

I’m imagining something like this:

func defaultConfiguration() -> Void {
  return ()
}
func defaultActionHandler() -> (Never) -> Void {
  return { _ in }
}
struct ResourceDescription {
  func makeResource(
with configuration: R.Configuration *=?* defaultConfiguration(),
actionHandler: @escaping (R.Action) -> Void *=?* defaultActionHandler()
  ) -> R {
// create a resource using the provided configuration
// connect the action handler
// return the resource
  }
}

The main difference here is the strawman =? syntax, which would indicate
that “the default argument exists if there is a way the RHS can be
satisfied for some instances of the generic arguments; otherwise, there is
no default”, instead of today’s behavior where it would be an error. There
could be multiple overloads of defaultConfiguration and defaultActionHandler
(even ones that are themselves generic) and it would do the right thing
when there are matches and when there aren’t.

I like this approach because it mostly takes advantage of existing language
features and is fairly lightweight in terms of how it’s expressed in code
compared to regular default arguments—we’d just need to design the new
operator and type-checker logic around it.



>
> On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>> I am all for this. are many types where there is an obvious 'zero' or
>> 'default' value and the ability to express "use that when possible" without
>> an overload is welcome.
>>
>>
>> The best thing that I can think of right now, in terms of syntax, is
>> actually using @overload
>>
>> ```
>> struct ResourceDescription {
>>
>>   func makeResource(with configuration: R.Configuration, actionHandler:
>> @escaping (R.Action) -> Void) -> R
>>  @overload(R.Configuration == Void) func makeResource(actionHandler:
>> @escaping (R.Action) -> Void) -> R
>> @overload(R.Action == Never)  func makeResource(with configuration:
>> R.Configuration) -> R
>> {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>> ```
>>
>>
>> This isn't great though…
>>
>> On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution <
>> swift-evolution@swift.org> wrote:
>>
>>> As mentioned in my prior message, I currently have a PR open to update
>>> the generics manifesto (https://github.com/apple/swift/pull/13012).  I
>>> removed one topic from that update at Doug Gregor’s request that it be
>>> discussed on the list first.
>>>
>>> The idea is to add the ability to make default arguments conditional
>>> (i.e. depend on generic constraints).  It is currently possible to emulate
>>> conditional default arguments using an overload set.  This is verbose,
>>> especially when several arguments are involved.  Here is an example use
>>> case using the overload method to emulate this feature:
>>>
>>> ```swift
>>> protocol Resource {
>>>   associatedtype Configuration
>>>   associatedtype Action
>>> }
>>> struct ResourceDescription {
>>>   func makeResource(with configuration: R.Configuration, actionHandler:
>>> @escaping (R.Action) -> Void) -> R {
>>> // create a resource using the provided configuration
>>> // connect the action handler
>>> // return the resource
>>>   }
>>> }
>>>
>>> extension ResourceDescription where R.Configuration == Void {
>>>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
>>> return makeResource(with: (), actionHandler: actionHandler)
>>>   }
>>> }
>>>
>>> extension ResourceDescription where R.Action == Never {
>>>   func makeResource(with configuration: R.Configuration) -> R {
>>> return makeResource(with: configuration, actionHandler: { _ in })
>>>   }
>>> }
>>>
>>> extension ResourceDescription where R.Configuration == Void, R.Action ==
>>> Never {
>>>   func makeResource() -> R {
>>> return makeResource(with: (), actionHandler: { _ in })
>>>   }
>>> }
>>>
>>> ```
>>>
>>> Adding language support for defining these more 

Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-24 Thread Xiaodi Wu via swift-evolution
It's kludgy, but we could have something like:

```
@defaultArgument(configuration = (), where R.Configuration == Void)
@defaultArgument(actionHandler = { _ in }, where R.Action == Never)
func makeResource(with configuration: R.Configuration, actionHandler:
@escaping (R.Action) -> Void) -> R { ... }
```

I don't like that we'd be setting a default argument on something lexically
before even encountering it in the declaration, but it's serviceable.


On Fri, Nov 24, 2017 at 8:36 PM, T.J. Usiyan via swift-evolution <
swift-evolution@swift.org> wrote:

> I am all for this. are many types where there is an obvious 'zero' or
> 'default' value and the ability to express "use that when possible" without
> an overload is welcome.
>
>
> The best thing that I can think of right now, in terms of syntax, is
> actually using @overload
>
> ```
> struct ResourceDescription {
>
>   func makeResource(with configuration: R.Configuration, actionHandler:
> @escaping (R.Action) -> Void) -> R
>  @overload(R.Configuration == Void) func makeResource(actionHandler:
> @escaping (R.Action) -> Void) -> R
> @overload(R.Action == Never)  func makeResource(with configuration:
> R.Configuration) -> R
> {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
> ```
>
>
> This isn't great though…
>
> On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>> As mentioned in my prior message, I currently have a PR open to update
>> the generics manifesto (https://github.com/apple/swift/pull/13012).  I
>> removed one topic from that update at Doug Gregor’s request that it be
>> discussed on the list first.
>>
>> The idea is to add the ability to make default arguments conditional
>> (i.e. depend on generic constraints).  It is currently possible to emulate
>> conditional default arguments using an overload set.  This is verbose,
>> especially when several arguments are involved.  Here is an example use
>> case using the overload method to emulate this feature:
>>
>> ```swift
>> protocol Resource {
>>   associatedtype Configuration
>>   associatedtype Action
>> }
>> struct ResourceDescription {
>>   func makeResource(with configuration: R.Configuration, actionHandler:
>> @escaping (R.Action) -> Void) -> R {
>> // create a resource using the provided configuration
>> // connect the action handler
>> // return the resource
>>   }
>> }
>>
>> extension ResourceDescription where R.Configuration == Void {
>>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
>> return makeResource(with: (), actionHandler: actionHandler)
>>   }
>> }
>>
>> extension ResourceDescription where R.Action == Never {
>>   func makeResource(with configuration: R.Configuration) -> R {
>> return makeResource(with: configuration, actionHandler: { _ in })
>>   }
>> }
>>
>> extension ResourceDescription where R.Configuration == Void, R.Action ==
>> Never {
>>   func makeResource() -> R {
>> return makeResource(with: (), actionHandler: { _ in })
>>   }
>> }
>>
>> ```
>>
>> Adding language support for defining these more directly would eliminate
>> a lot of boilerplate and reduce the need for overloads.  Doug mentioned
>> that it may also help simplify associated type inference (
>> https://github.com/apple/swift/pull/13012#discussion_r152124535).
>>
>> The reason that I call this a pre-pitch and one reason Doug requested it
>> be discussed on list is that I haven’t thought of a good way to express
>> this syntactically.  I am interested in hearing general feedback on the
>> idea.  I am also looking for syntax suggestions.
>>
>> Matthew
>>
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pre-pitch] Conditional default arguments

2017-11-24 Thread T.J. Usiyan via swift-evolution
I am all for this. are many types where there is an obvious 'zero' or
'default' value and the ability to express "use that when possible" without
an overload is welcome.


The best thing that I can think of right now, in terms of syntax, is
actually using @overload

```
struct ResourceDescription {

  func makeResource(with configuration: R.Configuration, actionHandler:
@escaping (R.Action) -> Void) -> R
 @overload(R.Configuration == Void) func makeResource(actionHandler:
@escaping (R.Action) -> Void) -> R
@overload(R.Action == Never)  func makeResource(with configuration:
R.Configuration) -> R
{
// create a resource using the provided configuration
// connect the action handler
// return the resource
  }
}
```


This isn't great though…

On Fri, Nov 24, 2017 at 6:11 PM, Matthew Johnson via swift-evolution <
swift-evolution@swift.org> wrote:

> As mentioned in my prior message, I currently have a PR open to update the
> generics manifesto (https://github.com/apple/swift/pull/13012).  I
> removed one topic from that update at Doug Gregor’s request that it be
> discussed on the list first.
>
> The idea is to add the ability to make default arguments conditional (i.e.
> depend on generic constraints).  It is currently possible to emulate
> conditional default arguments using an overload set.  This is verbose,
> especially when several arguments are involved.  Here is an example use
> case using the overload method to emulate this feature:
>
> ```swift
> protocol Resource {
>   associatedtype Configuration
>   associatedtype Action
> }
> struct ResourceDescription {
>   func makeResource(with configuration: R.Configuration, actionHandler:
> @escaping (R.Action) -> Void) -> R {
> // create a resource using the provided configuration
> // connect the action handler
> // return the resource
>   }
> }
>
> extension ResourceDescription where R.Configuration == Void {
>   func makeResource(actionHandler: @escaping (R.Action) -> Void) -> R {
> return makeResource(with: (), actionHandler: actionHandler)
>   }
> }
>
> extension ResourceDescription where R.Action == Never {
>   func makeResource(with configuration: R.Configuration) -> R {
> return makeResource(with: configuration, actionHandler: { _ in })
>   }
> }
>
> extension ResourceDescription where R.Configuration == Void, R.Action ==
> Never {
>   func makeResource() -> R {
> return makeResource(with: (), actionHandler: { _ in })
>   }
> }
>
> ```
>
> Adding language support for defining these more directly would eliminate a
> lot of boilerplate and reduce the need for overloads.  Doug mentioned that
> it may also help simplify associated type inference (
> https://github.com/apple/swift/pull/13012#discussion_r152124535).
>
> The reason that I call this a pre-pitch and one reason Doug requested it
> be discussed on list is that I haven’t thought of a good way to express
> this syntactically.  I am interested in hearing general feedback on the
> idea.  I am also looking for syntax suggestions.
>
> Matthew
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution