There is not much to say here, this is an issue I just encountered now, where I need two different but very similar inits, but for some reason this seems to be not supported yet.
Here is a code snippet that forced me to create required convenience override init which in my honest opinion just ugly. Instead it would be far better if default parameter on inits could satisfy other required inits.
protocol A {
init(foo: String)
}
protocol B {
init(foo: String, closure: (Self) -> Void)
}
class Super {
let foo: String
init(foo: String) {
self.foo = foo
}
}
final class Sub : Super, A, B {
// This is simplified version of the init and does not mirror
// a full real world implementation
/* required */ init(foo: String, closure: (Sub) -> Void) {
super.init(foo: foo)
closure(self)
}
// Satisfy `A` protocol which collides with the designated
// initializer. To not to implement the same init over again
// without the closure, we can add `convenience` and
// re-route it to the current designated initializer
/* required */ convenience override init(foo: String) {
self.init(foo: foo, closure: { _ in })
}
}
Ideally I'd wish I could just create a single init to satisfy both protocols:
That doesn't scale, unfortunately. As soon as you have more than one default argument, you have to account for all the possibilites, which means a lot of extra code. But I don't think that makes Adrian and Karl's proposals unreasonable.
I don’t think this is a problem in practice. You don’t have to do every order since you can’t change the order of arguments. Also there are few methods with lots of defaults.
Functions with, let's say, > 5 default values for arguments are quite rare after all.
But calling a function means invoking a block by passing some arguments. If default values or information for instantiating them were stored together with the signature, we could 'substitute' the defaults with the passed arguments where needed upon a call and then invoke, without needing to generate functions for all the possible entry points. Is this an ill approach?
The feature is available in other languages, e.g. Scala:
trait Parent {
def doSomething(s:String = "parent") = s
}
class Child extends Parent {
override def doSomething(s:String = "child") = s
}
val p: Parent = new Child()
p.doSomething() // child
The nearest equivalent in Swift, using classes because protocols can't do defaults, would print parent.
The Scala implementation is something like:
trait Parent {
var doSomthingDefaultSArgument = "parent"
def doSomething(s:String) = s
}
class Child extends Parent {
override var doSomthingDefaultSArgument = "child"
override def doSomething(s:String) = s
}
val p: Parent = new Child()
p.doSomething(p.doSomthingDefaultSArgument) // child
That way they don't generate extra methods. However as discussed above it isn't going to be many extra methods and it is much simpler to catch the edge cases like:
trait Trick {
def trick() = "trick" // Note no argument
}
class Treat extends Trick {
override def trick(s:String = "treat") = s // Override trick using a default argument
}