I’ve code that follows the overall design of:
protocol DispatchType {}
class DispatchType1: DispatchType {}
class DispatchType2: DispatchType {}
func doBar(worth:D) {
print("normal perform referred to as")
}
func doBar(worth:DispatchType1) {
print("DispatchType1 referred to as")
}
func doBar(worth:DispatchType2) {
print("DispatchType2 referred to as")
}
the place in actuality DispatchType
is definitely a backend storage. The doBar
features are optimized strategies that rely on the proper storage sort. The whole lot works tremendous if I do:
let d1 = DispatchType1()
let d2 = DispatchType2()
doBar(worth: d1) // "DispatchType1 referred to as"
doBar(worth: d2) // "DispatchType2 referred to as"
Nevertheless, if I make a perform that calls doBar
:
func check(worth:D) {
doBar(worth: worth)
}
and I strive an identical calling sample, I get:
check(worth: d1) // "normal perform referred to as"
check(worth: d2) // "normal perform referred to as"
This looks as if one thing that Swift ought to be capable to deal with because it ought to be capable to decide at compile time the sort constraints. Simply as a fast check, I additionally tried writing doBar
as:
func doBar(worth:D) the place D:DispatchType1 {
print("DispatchType1 referred to as")
}
func doBar(worth:D) the place D:DispatchType2 {
print("DispatchType2 referred to as")
}
however get the identical outcomes.
Any concepts if that is right Swift habits, and if that’s the case, a great way to get round this habits?
Edit 1: Instance of why I used to be attempting to keep away from utilizing protocols. Suppose I’ve the code (vastly simplified from my precise code):
protocol Storage {
// ...
}
class Tensor {
// ...
}
For the Tensor
class I’ve a base set of operations that may be carried out on the Tensor
s. Nevertheless, the operations themselves will change their habits primarily based on the storage. Presently I accomplish this with:
func dot(_ lhs:Tensor, _ rhs:Tensor) -> Tensor { ... }
Whereas I can put these within the Tensor
class and use extensions:
extension Tensor the place S:CBlasStorage {
func dot(_ tensor:Tensor) -> Tensor {
// ...
}
}
this has a number of uncomfortable side effects which I do not like:
-
I believe
dot(lhs, rhs)
is preferable tolhs.dot(rhs)
. Comfort features may be written to get round this, however that may create an enormous explosion of code. -
This may trigger the
Tensor
class to turn out to be monolithic. I actually want having it include the minimal quantity of code mandatory and increase its performance by auxiliary features. -
Associated to (2), which means that anybody who desires so as to add new performance should contact the bottom class, which I think about unhealthy design.
Edit 2: One different is that issues work anticipated in the event you use constraints for every part:
func check(worth:D) the place D:DispatchType1 {
doBar(worth: worth)
}
func check(worth:D) the place D:DispatchType2 {
doBar(worth: worth)
}
will trigger the proper doBar
to be referred to as. This additionally is not ideally suited, as it would trigger loads of further code to be written, however at the very least lets me maintain my present design.
Edit 3: I got here throughout documentation displaying using static
key phrase with generics. This helps at the very least with level (1):
class Tensor {
// ...
static func cos(_ tensor:Tensor) -> Tensor {
// ...
}
}
lets you write:
let end result = Tensor.cos(worth)
and it helps operator overloading:
let end result = value1 + value2
it does have the added verbosity of required Tensor
. This will made a bit of higher with:
typealias T = Tensor