I have this syntactical issue with my data structure. What makes it worse is that I can not talk about it in detail. The company I am employed at operates in the public transportation sector and I am under NDA and boss would kill me if I posted anything too specific. I hope you understand!
I have this perfect example though. There are no inconsistencies at all ;) Well, okay, there are. However I am convinced that most of you out there are smart enough to get what is of importance here.
Basic structure:
class Propulsion {
var horsePower: Double
init(horsePower: Double) {
self.horsePower = horsePower
}
static let pedes = Propulsion(horsePower: 0.2)
}
class Motor: Propulsion {
var range: Double
init(range: Double, horsePower: Double) {
self.range = range
super.init(horsePower: horsePower)
}
static let otto = Motor(range: 1000, horsePower: 100)
static let electric = Motor(range: 400, horsePower: 200)
}
class Vehicle<P: Propulsion> {
var propulsion: P
init(propulsion: P) {
self.propulsion = propulsion
}
}
class Bicycle<P: Propulsion>: Vehicle<P> {
var hasFrontSuspension: Bool
init(hasFrontSuspension: Bool, propulsion: P) {
self.hasFrontSuspension = hasFrontSuspension
super.init(propulsion: propulsion)
}
}
class Car<P: Propulsion>: Vehicle<P> {
func rangePerHorsePower() -> Double where P: Motor {
propulsion.range / propulsion.horsePower
}
}
Now I would like to declare a parking spot for a car. Like so:
var carParkingSpot: ParkingSpot<Car<Motor>>
For the class ParkingSpot
I have some class like this in mind:
class ParkingSpot<V: Vehicle<P>> where P: Propulsion {
var vehicle: Vehicle<P>
init(vehicle: Vehicle<P>) {
self.vehicle = vehicle
}
func taxForRange() -> Double where P: Motor {
vehicle.propulsion.range * 50
}
}
From the last bit I get back a bunch of
Cannot find type 'P' in scope
This one doesn’t work either:
class ParkingSpot<V: Vehicle<P: Propulsion>>
Expected '>' to complete generic argument list
This implementation works though:
class ParkingSpot<V: Vehicle<P>, P: Propulsion> {
var vehicle: Vehicle<P>
init(vehicle: Vehicle<P>) {
self.vehicle = vehicle
}
func taxForRange() -> Double where P: Motor {
vehicle.propulsion.range * 50
}
}
However I don’t want to duplicate the Motor
bit:
var carParkingSpot: ParkingSpot<Car<Motor>, Motor>
How can I accomplish this with just one generic parameter?
CodePudding user response:
This seems to work:
class ParkingSpot<V: Vehicle<Propulsion>>
{
var vehicle: V
init(vehicle: V)
{
self.vehicle = vehicle
}
func taxForEngineeNoise() -> Double
{
switch vehicle.propulsion
{
case is Motor:
return vehicle.propulsion.horsePower * 50
default:
...
}
}
func taxForRange() -> Double
{
if let motor = vehicle.propulsion as? Motor
{
return motor.range * 50
}
else
{
...
}
}
}
Alternatively, perhaps hide the duplication where you can?
typealias ParkingSpotX = ParkingSpot<Car<Motor>, Motor>
var parkingSpot: ParkingSpotX
CodePudding user response:
You may use the "Protocol oriented" approach:
protocol PropulsionP {
var horsePower: Double { get }
}
protocol MotorP: PropulsionP {
var range: Double { get }
}
struct MotorS: MotorP {
var range: Double
var horsePower: Double
init(range: Double, horsePower: Double) {
self.range = range
self.horsePower = horsePower
}
}
protocol VehicleP {
associatedtype P: PropulsionP
var propulsion: P { get }
}
struct BicycleS<Prop: PropulsionP>: VehicleP {
let hasFrontSuspension: Bool
var propulsion: Prop
init(
hasFrontSuspension: Bool,
propulsion: Prop
) {
self.hasFrontSuspension = hasFrontSuspension
self.propulsion = propulsion
}
}
struct CarS<Prop: PropulsionP>: VehicleP {
var propulsion: Prop
func rangePerHorsePower() -> Double where P: MotorP {
propulsion.range / propulsion.horsePower
}
}
struct ParkingSpotS<V: VehicleP> {
var vehicle: V
init(vehicle: V) {
self.vehicle = vehicle
}
func taxForRange() -> Double where V.P: MotorP {
vehicle.propulsion.range * 50
}
}
var carParkingSpot: ParkingSpotS<CarS<MotorS>>
No double MotorS
bit.
Quod erat demonstrandum.
I used the somewhat unusual naming to emphasize the point.
(needed to make some edit, erronously typed Motor
where I actually need MotorP
)
Update
I was on the road with my preferred car and tried it out:
var carParkingSpot: ParkingSpotS<CarS<MotorS>> = .init(
vehicle: .init(
propulsion: .init(
range: 760,
horsePower: 240
)
)
)
print(carParkingSpot.taxForRange())
38000.0
Alternatively you can use this initialiser:
var carParkingSpot: ParkingSpotS = .init(
vehicle: CarS(
propulsion: MotorS(
range: 760,
horsePower: 240
)
)
)