Strange error message using a standard operator to compose types

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:

  1. Type composition with the * operator doesn't work with 3 or more identical types, but it works with 2 types.
  2. The * operator works with any number of types, as long as at least two different types are used.
  3. 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.

1 Like