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