This is a bare-bones example of the problem I encounter.
I'm scratching my head trying to figure out why this happens.
Can anyone explain to me the reason or a way to fix this? Thanks.
import Foundation
protocol Unit {
static func factor<Real:BinaryFloatingPoint>() -> Real
}
protocol BasicUnit : Unit {
// etc...
}
enum Units {
struct m : BasicUnit {
static func factor<Real:BinaryFloatingPoint>() -> Real { 1 }
}
struct cm : BasicUnit {
static func factor<Real:BinaryFloatingPoint>() -> Real { 1/100 }
}
struct mul<A:Unit,B:Unit> : Unit {
static func factor<Real:BinaryFloatingPoint>() -> Real { A.factor() * B.factor() }
}
struct div<A:Unit,B:Unit> : Unit {
static func factor<Real:BinaryFloatingPoint>() -> Real { A.factor() / B.factor() }
}
}
infix operator • : MultiplicationPrecedence
extension Unit {
// Create a type that composes two types
static func * <U:Unit>( lhs: Self.Type, rhs: U.Type ) -> Units.mul<Self,U>.Type {
Units.mul<Self,U>.self
}
// Same thing, but with a specifically defined operator
static func • <U:Unit>( lhs: Self.Type, rhs: U.Type ) -> Units.mul<Self,U>.Type {
Units.mul<Self,U>.self
}
/* Nothing changes if I define this too:
static func * ( lhs: Self.Type, rhs: Self.Type ) -> Units.mul<Self,Self>.Type {
Units.mul<Self,Self>.self
}
*/
}
let m = Units.m.self
let cm = Units.cm.self
let cm2 = cm * cm // ✅
let cm3_a = cm * cm * cm // ❌ Cannot convert value of type 'Units.mul<Units.cm, Units.cm>.Type' to expected argument type 'Units.cm.Type'
let Bcm3_a = cm • cm • cm // ✅ … but if I use a specially defined operator instead of * it works
let cm3_b = cm * (cm * cm) // ❌ Cannot convert value of type 'Units.mul<Units.cm, Units.cm>.Type' to expected argument type 'Units.cm.Type'
let Bcm3_b = cm • (cm • cm) // ✅ … but if I use a specially defined operator instead of * it works
let cm3_c = cm2 * cm // ✅ Works
let cm3_d = cm * cm2 // ✅ Works
let m_cm2 = m * cm * cm // ✅ Two different types: works!
let cm4_a = cm2 * cm2 // ✅
let cm4_b = cm2 * cm * cm // ✅ Two different types: works!
let cm4_c = cm * cm * cm * cm * cm2 * cm * cm * cm * cm // ✅ Two differente types: works!
let cm6 = cm2 * cm2 * cm2 // ❌ Cannot convert value of type 'Units.mul<Units.mul<Units.cm, Units.cm>, Units.mul<Units.cm, Units.cm>>.Type' to expected argument type 'Units.mul<Units.cm, Units.cm>.Type'
let Bcm6 = cm2 • cm2 • cm2 // ✅ But if I use a specially defined operator instead of * it works
Conclusion:
- Type composition with the * operator doesn't work with 3 or more identical types, but it works with 2 types.
- The * operator works with any number of types, as long as at least two different types are used.
- If I define a custom operator • that has the same function and same precedence as *, type composition always works, including case 1.
I can't figure out why.