I really like what this idea enables. But when presented as implicit, it does not feel Swifty. How about we present your idea with a different terminology:
Lets start by looking at what we can already do with default parameter values:
class Context { /* ... */ }
var context = Context()
struct Shape {
func draw(into context: Context = context) { /* ... */ }
// ^^^^^^^ default is a global var
}
class DebugContext: Context { /* ... */ }
let shape = Shape()
do {
shape.draw() // Draws to a `Context`
let currentContext = context; defer { context = currentContext }
context = DebugContext()
shape.draw() // Draws to a `DebugContext`
}
This has serious issues, but we can improve it by defining a new kind of default parameter value. I call it a declared default, where we declare the identifier as representing the default value instead of providing an expression. Here is how it would look:
class Context { /* ... */ }
struct Shape {
func draw(into context: Context = default ) // `context` identifier is declared to represent the default value
{ /* ... */ } // This syntax eliminates "What about default for implicit parameter?" question.
}
class DebugContext: Context { /* ... */ }
let default context = Context() // Only `let` binding; override by shadowing in scope
do {
let shape = Shape()
shape.draw() // uses the default `context` from outer scope. if no default was declared, the `context` parameter would be required.
let default context = DebugContext()
shape.draw() // draws to the `DebugContext` from local scope
// We can also explicitly provide the parameter:
let otherContext = Context
shape.draw(into: otherContext)
}
shape.draw() // back to using the `Context` from global scope, just like any identifier.
I would also want to let protocols declare default identifiers for parameter values:
protocol Shape {
func draw(into context: Context = default )
}
What do you think?