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 
> <swift-evolution@swift.org> wrote:
> 
>> On Sat, Nov 25, 2017 at 2:35 PM Xiaodi Wu <xiaodi...@gmail.com> wrote:
>>> On Sat, Nov 25, 2017 at 4:25 PM, Tony Allevato <tony.allev...@gmail.com> 
>>> wrote:
>> 
>>>> On Sat, Nov 25, 2017 at 1:16 PM Xiaodi Wu <xiaodi...@gmail.com> wrote:
>>> 
>>>> On Sat, Nov 25, 2017 at 15:06 Matthew Johnson <matt...@anandabits.com> 
>>>> 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<R: Resource> {
>>>>>>   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 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 the case?
> 
> Well, it's a pitch, not a complete design, so it's either possible or not 
> possible depending on what restrictions we place on it :)
> 
> You're right that if this was implemented in a certain way, someone could add 
> overloads in other modules that would allow defaults to exist where they 
> otherwise wouldn't. If that's a concern, then the answer is simple—have the 
> compiler only look in the same module for matching functions. A function used 
> in a default value expression today must be present in the same module or 
> file by virtue of the fact that if it wasn't, the compiler wouldn't be able 
> to reference it, so this would be somewhat consistent with that behavior.
> 
>  
>>  
>>> 
>>>>> 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<R: Resource> {
>>>>>>>> 
>>>>>>>>   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<R: Resource> {
>>>>>>>>>   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
>>>>>> 
>>>>>> 
>>>>>> _______________________________________________
>>>>>> 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

Reply via email to