I'm trying to make pointfree's generic struct Tagged
conditionally conform to FloatingPoint
and I'm running into a problem: when I try to use (for example) operator *
on my type, I get an ambiguous use of operator '*'
error.
To simplify the problem, I stripped out the generic Tag
parameter, leaving just the RawValue
parameter. Here's the type definition and a bunch of conditional conformances, all the way to FloatingPoint
. Sorry it's so long but I couldn't find anything else that I could remove while still conforming to FloatingPoint
.
struct GenericWrapper<RawValue> {
var rawValue: RawValue
init(_ value: RawValue) { rawValue = value }
}
func combined<RawValue>(_ a: GenericWrapper<RawValue>, _ b: GenericWrapper<RawValue>, using combiner: (RawValue, RawValue) throws -> RawValue) rethrows -> GenericWrapper<RawValue> {
return GenericWrapper<RawValue>.init(try combiner(a.rawValue, b.rawValue))
}
func formCombination<RawValue>(_ a: inout GenericWrapper<RawValue>, _ b: GenericWrapper<RawValue>, using combiner: (RawValue, RawValue) throws -> RawValue) rethrows {
a = try combined(a, b, using: combiner)
}
extension GenericWrapper: Equatable where RawValue: Equatable { }
extension GenericWrapper: Hashable where RawValue: Hashable { }
extension GenericWrapper: Comparable where RawValue: Comparable {
static func <(_ a: GenericWrapper, _ b: GenericWrapper) -> Bool { return a.rawValue < b.rawValue }
}
extension GenericWrapper: ExpressibleByFloatLiteral where RawValue: ExpressibleByFloatLiteral {
typealias FloatLiteralType = RawValue.FloatLiteralType
init(floatLiteral: FloatLiteralType) { self.init(RawValue(floatLiteral: floatLiteral)) }
}
extension GenericWrapper: ExpressibleByIntegerLiteral where RawValue: ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = RawValue.IntegerLiteralType
init(integerLiteral: IntegerLiteralType) { self.init(RawValue(integerLiteral: integerLiteral)) }
}
extension GenericWrapper: Numeric where RawValue: Numeric {
typealias Magnitude = GenericWrapper<RawValue.Magnitude>
init?<T>(exactly source: T) where T : BinaryInteger {
guard let value = RawValue(exactly: source) else { return nil }
self.init(value)
}
var magnitude: Magnitude { return Magnitude.init(rawValue.magnitude) }
static func +(_ a: GenericWrapper, _ b: GenericWrapper) -> GenericWrapper { return combined(a, b, using: +) }
static func +=(_ a: inout GenericWrapper, _ b: GenericWrapper) { formCombination(&a, b, using: +) }
static func -(_ a: GenericWrapper, _ b: GenericWrapper) -> GenericWrapper { return combined(a, b, using: -) }
static func -=(_ a: inout GenericWrapper, _ b: GenericWrapper) { formCombination(&a, b, using: -) }
static func *(_ a: GenericWrapper, _ b: GenericWrapper) -> GenericWrapper { return combined(a, b, using: *) }
static func *=(_ a: inout GenericWrapper, _ b: GenericWrapper) { formCombination(&a, b, using: *) }
}
extension GenericWrapper: SignedNumeric where RawValue: SignedNumeric { }
extension GenericWrapper: Strideable where RawValue: Strideable {
typealias Stride = GenericWrapper<RawValue.Stride>
func distance(to other: GenericWrapper<RawValue>) -> Stride { return GenericWrapper<RawValue.Stride>.init(rawValue.distance(to: other.rawValue)) }
func advanced(by n: Stride) -> GenericWrapper<RawValue> { return GenericWrapper<RawValue>.init(rawValue.advanced(by: n.rawValue)) }
}
extension GenericWrapper: FloatingPoint where RawValue: FloatingPoint {
typealias Exponent = RawValue.Exponent
init(sign: FloatingPointSign, exponent: Exponent, significand: GenericWrapper) { self.init(RawValue.init(sign: sign, exponent: exponent, significand: significand.rawValue)) }
init(signOf signDonor: GenericWrapper, magnitudeOf magnitudeDonor: GenericWrapper) { self.init(RawValue(signOf: signDonor.rawValue, magnitudeOf: magnitudeDonor.rawValue)) }
init(_ value: UInt8) { self.init(RawValue(value)) }
init(_ value: Int8) { self.init(RawValue(value)) }
init(_ value: UInt16) { self.init(RawValue(value)) }
init(_ value: Int16) { self.init(RawValue(value)) }
init(_ value: UInt32) { self.init(RawValue(value)) }
init(_ value: Int32) { self.init(RawValue(value)) }
init(_ value: UInt64) { self.init(RawValue(value)) }
init(_ value: Int64) { self.init(RawValue(value)) }
init(_ value: UInt) { self.init(RawValue(value)) }
init(_ value: Int) { self.init(RawValue(value)) }
init<Source>(_ value: Source) where Source : BinaryInteger { self.init(RawValue(value)) }
static var radix: Int { return RawValue.radix }
static var nan: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.nan) }
static var signalingNaN: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.signalingNaN) }
static var infinity: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.infinity) }
static var greatestFiniteMagnitude: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.greatestFiniteMagnitude) }
static var pi: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.pi) }
var ulp: GenericWrapper { return GenericWrapper<RawValue>.init(rawValue.ulp) }
static var leastNormalMagnitude: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.leastNormalMagnitude) }
static var leastNonzeroMagnitude: GenericWrapper { return GenericWrapper<RawValue>.init(RawValue.leastNonzeroMagnitude) }
var sign: FloatingPointSign { return rawValue.sign }
var exponent: RawValue.Exponent { return rawValue.exponent }
var significand: GenericWrapper { return GenericWrapper<RawValue>.init(rawValue.significand) }
static func / (lhs: GenericWrapper, rhs: GenericWrapper) -> GenericWrapper { return combined(lhs, rhs, using: /) }
static func /= (lhs: inout GenericWrapper, rhs: GenericWrapper) { lhs.rawValue /= rhs.rawValue }
mutating func formRemainder(dividingBy other: GenericWrapper) { rawValue.formRemainder(dividingBy: other.rawValue) }
mutating func formTruncatingRemainder(dividingBy other: GenericWrapper) { rawValue.formTruncatingRemainder(dividingBy: other.rawValue) }
mutating func formSquareRoot() { rawValue.formSquareRoot() }
mutating func addProduct(_ lhs: GenericWrapper, _ rhs: GenericWrapper) { rawValue.addProduct(lhs.rawValue, rhs.rawValue) }
var nextUp: GenericWrapper { return GenericWrapper<RawValue>.init(rawValue.nextUp) }
func isEqual(to other: GenericWrapper) -> Bool { return rawValue.isEqual(to: other.rawValue) }
func isLess(than other: GenericWrapper) -> Bool { return rawValue.isLess(than: other.rawValue) }
func isLessThanOrEqualTo(_ other: GenericWrapper) -> Bool { return rawValue.isLessThanOrEqualTo(other.rawValue) }
func isTotallyOrdered(belowOrEqualTo other: GenericWrapper) -> Bool { return rawValue.isTotallyOrdered(belowOrEqualTo: other.rawValue) }
var isNormal: Bool { return rawValue.isNormal }
var isFinite: Bool { return rawValue.isFinite }
var isZero: Bool { return rawValue.isZero }
var isSubnormal: Bool { return rawValue.isSubnormal }
var isInfinite: Bool { return rawValue.isFinite }
var isNaN: Bool { return rawValue.isNaN }
var isSignalingNaN: Bool { return rawValue.isSignalingNaN }
var isCanonical: Bool { return rawValue.isCanonical }
mutating func round(_ rule: FloatingPointRoundingRule) { rawValue.round(rule) }
}
So then I want to use the type like this:
typealias DataValue = GenericWrapper<Double>
let d0: DataValue = 10
let d1: DataValue = 12
print(d0 * d1) // error: ambiguous use of operator '*'
But compilation fails:
:; /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2018-08-14-a.xctoolchain/usr/bin/swiftc -c main.swift
main.swift:113:14: error: ambiguous use of operator '*'
print(d0 * d1)
^
main.swift:43:21: note: found this candidate
static func *(_ a: GenericWrapper, _ b: GenericWrapper) -> GenericWrapper { return combined(a, b, using: *) }
^
Swift.FloatingPoint:36:24: note: found this candidate
public static func * (lhs: Self, rhs: Self) -> Self
^
Swift.Numeric:9:24: note: found this candidate
public static func * (lhs: Self, rhs: Self) -> Self
The first candidate is my definition of *
for GenericWrapper
. The other two appear to just point at the protocol requirements. I searched the Swift standard library source code and couldn't find any actual method definitions for *
on Numeric
or FloatingPoint
.
Incidentally, the Swift.Numeric
candidate is new to the master branch. The swift-4.2-DEVELOPMENT-SNAPSHOT-2018-08-14-a.xctoolchain
doesn't print that candidate, but does print the Swift.FloatingPoint
candidate.
Note that the follow tests compile and run successfully:
func f<T: FloatingPoint>(_ a: T, _ b: T) -> T {
return a * b
}
print(f(d0, d1))
let g: (DataValue, DataValue) -> DataValue = f
print(g(d0, d1))
So here are my questions:
-
What, if any, is the bug in my code that's causing the “ambiguous operator” error?
-
If it's not a bug in my code, is there a workaround to make
d0 * d1
compile?