I searched for an answer but only found confirmations that you can't specify default arguments and workarounds.
Can you explain why? I've just run into this myself, and after 20 minutes of searching, I've found workaround, but no answer why. it seems like a perfectly reasonable thing to want to be able to do:
Afaics there are no technical reasons for the limitation - but things that would have to be decided, and decisions can be quite complicated here ;-)
What if the implementing type does not include the default value - or even a different one?
Would that still be a valid conformance? What when you use that type as instance of the protocol? Which value should it take?
I think it might be preferable to allow default arguments, but only use them for autocompletion when implementing the requirement.
The conforming type doesn't and shouldn't care about the default value. They implement the requirement which accepts the parameter, and whether that value is explicit or a default is none of their business.
This is just like defaults and wrapper functions in protocol extensions. You could make loads of wrapper functions which forward to the requirement but with some default values for particular parameters. The conforming type also doesn't get a say in what values those should be.
I agree with @Tino. However I wonder, what is the correct naive approach to understand interaction between protocol declarations and conforming types?
Following image demonstrates such problem from a different angle. I’m not able to conform to a protocol by introducing a function with a default value in a conforming type.
My naive explanation is, that if I introduce function with (one) default parameter, “API” of the type then contains the function and one more function for each combination of default parameters. Therefore the value default parameter is hidden from the protocol and it is reasonable, that protocol can not declare function with default parameter.
But in practise as demonstrated above, there is a difference, which suggests, that the protocol to a degree knows, how the function was declared in a conforming type. Which suggests for me, that if there would be a possibility to declare a function with default parameters in a protocol, conforming type would have to declare precisely the same function.
I think, that this ambiguity for a naive swift user could be solved by and explanatory footnote in the Swift Guide, which would describe how the call is synthesized by the compiler. (Instead of changes to the language itself.)
All these arguments seem to look at it from the conforming types's view. What about from a client's view? A client would like to call a method in a protocol specifying only the desired arguments, just like one might on a method in a struct. The caller doesn't much care about the conforming implementation, it only cares about how much it has to provide.
It's the inconsistency that bothers me. And the workaround, to implement a wrapper method in an extension, can be quite confusing (why doesn't it end up calling itself recursively? I know it's possible to get it to do so, the compiler warned me. But it's not immediately obvious why).
Should be allowed, shouldn't it? But then, it won't be obvious what value find would actually use when the page parameter is missing.
We would end up with another incarnation of the problem with protocol extensions, where the choice which method is executed depends on the context (is it an instance of the type, or of the protocol?).
To be clear, I don't think that all those questions are show stoppers - there just has to be some consensus on the answers.
Another question is:
What if two protocols declare a method with identical signature, but different default values?
Imho this isn't a situation that would happen in real-world code, but still, a sound language needs a proper answer to such edge cases.
I have, though, had occasions in the past where I wished I could specify, in the protocol requirement, that a parameter should have some default value, which is a concern pertaining to the API.
That is distinguishable from the question of what to do if the protocol specifies one particular default value and conforming type specifies another or none.
I think it would work just fine to permit the syntax we already see in interface files and documentation actually to be written as a protocol requirement:
protocol P {
func foo(_ argument: Int = default)
// A conforming method must default this value to something.
}
Note that default arguments are determined statically. Hence this behavior:
class Shape {
func draw(colour: String = "Red") {
print("\(colour) shape")
}
}
class Circle: Shape {
override func draw(colour: String = "Blue") {
print("\(colour) circle")
}
}
let s1 = Shape()
s1.draw() // Red shape
let s2 = Circle()
s2.draw() // Blue circle
let s3: Shape = Circle()
s3.draw() // Red circle
Whereas this "you must provide a default" protocol behavior would need to be determined dynamically (say if you had a Shape existential). Which is not to say it's ruled out, but it would introduce an inconsistency in the language.
I'd be fine with this static behavior of protocols. I guess I don't see the conceptual difference between your Shape & Circle example and this. That is to say, if the class inheritance example's behavior is acceptable, so should be similar behavior of protocol conformance
protocol Shape {
func draw(colour: String = "Red")
}
struct Circle {
func draw(colour: String = "Blue") { ... }
}
let s1: Shape = Circle()
s1.draw() // Red circle
let s2 = Circle()
s2.draw() // Blue circle
I was speaking to @xwu's proposal of func foo(_ argument: Int = default) specifically. That is what mandates runtime defaults because the protocol does not provide a value.
Fantastic idea! There ought to be expressions to declare default parameters, which make a difference to the callers. And this syntax is clear and expressive.
Appreciate this thread is a year old now but are there any plans/discussions around allowing this in protocol definitions?
I come across this regularly and it always feels like a weird and confusing language hack to use a protocol extension that calls through to the concrete implementation.
If you use the @_implements(A, myFunc(arg:)) directive to make an implementation (with a different name) that implements myFunc(arg:) does it still crash if A’s definition changes?