Reputation: 307
Say I have two structs that conform to a protocol,
protocol NumberType {
var doubleValue: Double { get }
}
struct Rational: NumberType {
let numerator: Int
let denominator: Int
var doubleValue: Double { get { return Double(numerator) / Double(denominator) } }
}
struct FixedPoint: NumberType {
let value: Double
var doubleValue: Double { get { return value } }
}
And I want to define arithmetic operators for NumberType
that will in turn return NumberType
s.
func *(lhs: NumberType, rhs: NumberType) -> NumberType { return FixedPoint(lhs.doubleValue * rhs.doubleValue) }
But I also want to add more specific operators so when both are Rational
s, I return a Rational
.
func *(lhs: Rational, rhs: Rational) -> NumberType { return Rational(...) }
Is there a more elegant way to do this than to have one super-function per operator that does all type checking?
You should be able to multiply FixedPoint by Rational, since they both conform to NumberType.
The overloading strategy won't work, because if I do a * b * c
where all values are Rational
, the result of a * b
is NumberType
, and so (a * b) * c
will fall back to using the generic NumberType
multiplication operator. I'll lose my precision here.
I'm not bothered about being able to multiply by Doubles
, Ints
etc.
It's not correct to assume that the most generic case always returns a FixedPoint
. I'm showing basic usage here, but if you have a FixedPoint
equal to 0
, and another Rational
, and you wanted to add them, it's feasible that you could get a Rational
in your most generic case.
Upvotes: 1
Views: 733
Reputation: 77661
You could use generics here...
If you define the protocol like...
protocol NumberType {
var doubleValue: Double { get }
init(doubleValue double: Double)
}
Then you could define a function like...
func *<T: NumberType>(lhs: T, rhs: T) -> T {
return T(doubleValue: lhs.doubleValue * rhs.doubleValue)
}
This will allow you to use this function with any type of object as long as it conforms to NumberType
. The only caveat is that both lhs
and rhs
must be the same type.
Then in the implementation of each NumberType you can define how the init works...
struct FixedPoint: NumberType {
let value: Double
var doubleValue: Double { get { return value } }
init(doubleValue double: Double) {
// this implementation may not be practical for things like Rationals etc...
self.value = double
}
}
Without having some common way of creating NumberTypes
then you'd have to have multiple implementations of how the function works and therefore have your method of creating multiple instance depending on what the inputs are.
Upvotes: 2
Reputation: 86691
I would have the protocol declare a function that does what you want.
protocol NumberType {
var doubleValue: Double { get }
func multipliedBy(other: NumberType) -> NumberType
}
And then define the operator
func *(l: NumberType, r: NumberType) -> NumberType
{
return l.multipliedBy(other: r)
}
Each type that implements the protocol, implements multipliedBy(other:)
in a way that is suitable for its type.
Upvotes: 0