I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
On Feb 22, 2017, at 10:16 AM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:
Hello all,
I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
On Feb 22, 2017, at 10:16 AM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:
Hello all,
I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
After further examination, it looks like default parameter values are evaluated in a type context, not an instance context, which is why this isn't working. Using the instance context there would be provide more flexibility, since we could still explicitly access type-level vars.
Does anyone have a sense of how frequently type-level expressions are used in default parameters? As far as I've seen the standard library only uses literals. A change to this behavior would be source-breaking, but perhaps not painfully so.
Nate
···
On Feb 22, 2017, at 12:16 PM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:
Hello all,
I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
I think this is a manifestation of a more general problem, that default arguments cannot capture values from outer scope. Saying they’re evaluated in “type context” and not “instance context” is one way to skirt around the issue, but you can still trigger capturing anyway, and crash in SILGen:
public func foo() {
let x = 10
func bar(y: Int = x) {}
}
Fixing this would not be too difficult, just refactoring some code in Sema so that we actually compute the captures of each default argument generator, and then emit the captures when we call one.
Slava
···
On Feb 22, 2017, at 10:16 AM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:
Hello all,
I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
It's nothing that is miss all the time, but I was informed about this limitation by the compiler before ;-), so unless there are some unexpected downsides, it should be added — and I wouldn't limit it to instance members:
Other parameters could be useful defaults as well (or think of scenarios like
func addPerson(firstName: String, lastName: String, fullName: String = firstName + lastName))
I would use this all the time if it was available. What I do now most of the time is take an optional instead and then ‘??’ with the instance variable. That doesn’t work if I need an optional for other reasons, and nil is kind of a magical value…
Thanks,
Jon
···
On Feb 22, 2017, at 11:53 AM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:
After further examination, it looks like default parameter values are evaluated in a type context, not an instance context, which is why this isn't working. Using the instance context there would be provide more flexibility, since we could still explicitly access type-level vars.
Does anyone have a sense of how frequently type-level expressions are used in default parameters? As far as I've seen the standard library only uses literals. A change to this behavior would be source-breaking, but perhaps not painfully so.
Nate
On Feb 22, 2017, at 12:16 PM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello all,
I was surprised to find that I can't use an instance member as the default value of a method parameter, only constants and the run-time calculated #file, #line, etc. Is it possible to remove this limitation?
I'd like to propose that we add an overload of the collection index(_:offsetBy:) methods that use the collection's startIndex as a default parameter, but we can't provide that without either this feature or an extra overload. If the declaration looks like this:
extension Collection {
func index(_ i: Index = startIndex, offsetBy n: IndexDistance) -> Index {
// ...
}
}
then calling that method with an omitted first parameter would treat it as something like this:
extension Collection {
func index(offsetBy n: IndexDistance) -> Index {
let i = startIndex
// ...
}
}
Is this just syntactic sugar, or am I missing something that makes this harder than it looks? I can see how more complex expressions could be useful there, too, and can't find an obvious reason we couldn't use any code that could be inserted at the top of the method body.
The downside to making it IUO or Optional is that that changes the type
signature of `index(i:offsetBy:)` for no reason other than a default
value. If you decide to change to or away from providing a default value it
probably shouldn't change the type accepted in that spot.
···
On Wed, Feb 22, 2017 at 7:32 PM, Ben Cohen via swift-evolution < swift-evolution@swift.org> wrote:
On Feb 22, 2017, at 10:42 AM, Nate Cook via swift-evolution < > swift-evolution@swift.org> wrote:
Oops, left out that there's this horrifying way of writing it right now:
extension Collection {
func index(_ i: Index! = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? startIndex
// ...
}
}
Nobody wants that.
Oh I don’t think it’s all that bad! It also doesn’t need to be an IUO,
since you’re unwrapping it immediately into another variable no matter what.
This also gives you the flexibility to write this:
extension Collection {
func index(_ i: Index? = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? (n < 0 ? endIndex : startIndex)
// ...
}
}
One big issue is that can cause erroneous usage to propagate:
// want to index to the first character after the paren
str.index(str.index(of: “(“), offsetBy: 1)
before this sort of code would give an error saying that the case where “(“ was not found wasn’t handled, but now it will silently give unexpected behavior.
-DW
···
On Feb 22, 2017, at 5:32 PM, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 22, 2017, at 10:42 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Oops, left out that there's this horrifying way of writing it right now:
<snip>
Nobody wants that.
Oh I don’t think it’s all that bad! It also doesn’t need to be an IUO, since you’re unwrapping it immediately into another variable no matter what.
Did you know you can use a static variable as a default parameter?
class poncho {
static var color= "yellow"
func foo(color: String = poncho.color){}
}
I think instance members should not be allowed as default members though.
It may lead to unexpected behavior.
···
On Wed, Feb 22, 2017 at 11:06 PM T.J. Usiyan via swift-evolution < swift-evolution@swift.org> wrote:
+1 if this doesn't have terrible implications.
The downside to making it IUO or Optional is that that changes the type
signature of `index(i:offsetBy:)` for no reason other than a default
value. If you decide to change to or away from providing a default value it
probably shouldn't change the type accepted in that spot.
On Wed, Feb 22, 2017 at 7:32 PM, Ben Cohen via swift-evolution < > swift-evolution@swift.org> wrote:
On Feb 22, 2017, at 10:42 AM, Nate Cook via swift-evolution < > swift-evolution@swift.org> wrote:
Oops, left out that there's this horrifying way of writing it right now:
extension Collection {
func index(_ i: Index! = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? startIndex
// ...
}
}
Nobody wants that.
Oh I don’t think it’s all that bad! It also doesn’t need to be an IUO,
since you’re unwrapping it immediately into another variable no matter what.
This also gives you the flexibility to write this:
extension Collection {
func index(_ i: Index? = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? (n < 0 ? endIndex : startIndex)
// ...
}
}
Did you know you can use a static variable as a default parameter?
class poncho {
static var color= "yellow"
func foo(color: String = poncho.color){}
}
I think instance members should not be allowed as default members though. It may lead to unexpected behavior.
Could you explain or give an example of how the behavior would be unexpected?
···
On Feb 22, 2017, at 8:25 PM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:
On Wed, Feb 22, 2017 at 11:06 PM T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
+1 if this doesn't have terrible implications.
The downside to making it IUO or Optional is that that changes the type signature of `index(i:offsetBy:)` for no reason other than a default value. If you decide to change to or away from providing a default value it probably shouldn't change the type accepted in that spot.
On Wed, Feb 22, 2017 at 7:32 PM, Ben Cohen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Feb 22, 2017, at 10:42 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Oops, left out that there's this horrifying way of writing it right now:
extension Collection {
func index(_ i: Index! = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? startIndex
// ...
}
}
Nobody wants that.
Oh I don’t think it’s all that bad! It also doesn’t need to be an IUO, since you’re unwrapping it immediately into another variable no matter what.
This also gives you the flexibility to write this:
extension Collection {
func index(_ i: Index? = nil, offsetBy n: IndexDistance) -> Index {
let i = i ?? (n < 0 ? endIndex : startIndex)
// ...
}
}