I just encountered a situation where it seems impossible to make a typealias . The idea is to have a convenient short name for a protocol with certain constraints on its associatedtypes. For example, one might want:
typealias Randomizable = BinaryFloatingPoint where RawSignificand: FixedWidthInteger
However, when I try to write that, it fails to compile:
error: 'where' clause cannot be attached to a non-generic declaration
Am I missing something, or is this really not supported? If it isn’t, would we need an evolution proposal or is it considered an obvious hole / bug?
We don't have "constraint aliases" in the language today (it's "really not supported" AFAIK) - but I'll let you in on a little secret I discovered. This is really ugly, but believe it or not, it works:
typealias IsFloatColl<T> = T where T: Collection, T.Element: Strideable, T.Element.Stride: BinaryFloatingPoint
extension Collection where IsFloatColl<Element>: Any {
func doSomething() {
// type-checking works.
self.first?.first?.advanced(by: .pi)
}
}
func genericFunc_1D<T>(_ t: T) where IsFloatColl<T>: Any {
t.first?.advanced(by: .pi)
}
func test() {
let flt2D: [[Float]] = []
let dbl2D: [[Double]] = []
let int2D: [[Int]] = []
flt2D.doSomething() // Works.
dbl2D.doSomething() // Works.
genericFunc_1D(flt2D[0]) // Works.
genericFunc_1D(dbl2D[0]) // Works.
// int2D.doSomething()
// Referencing instance method 'doSomething()' on 'Collection' requires that 'Int.Stride' (aka 'Int') conform to 'BinaryFloatingPoint'
// genericFunc_1D(int2D[0])
// Global function 'genericFunc_1D' requires that 'Int.Stride' (aka 'Int') conform to 'BinaryFloatingPoint'
}
It's surprisingly complete - constrained extensions and generics work, and even diagnostics see through the hack and provide good error messages. It can be a big help if you need to repeat long strings of constraints.
I'm not sure I understand what the expected behavior is. The left hand side of a where clause is a type parameter, rooted in a generic parameter of the type alias itself or an outer context. In this case the type alias does not have any generic parameters, and I'm assuming its not in a generic context, so the name RawSignificand is not in scope. If you want to refer to the associated type RawSignificand of a protocol, you need a base type, eg T.RawSignificand, and then add a generic parameter T to the type alias.
So while I'm sure it would be possible to give meaningful semantics to this example, I would not say it's an obvious feature, and certainly not a bug :)