This makes sense: generic typealiases with additional constraints are not "simple type synonyms" for some existing type. If you want to add additional constraints, you have to declare a new type, which may be quite heavyweight.
I wonder if anyone's thought about how to actually "[extend] the model" to support generic typealiases with additional constraints? Would it involve generating implicit type declarations?
/// A layer that sequentially composes 3 layers.
public typealias Sequential3<L1: Module, L2: Layer, L3: Layer> = Sequential<L1, Sequential<L2, L3>>
where
L1.Output == L2.Input, L2.Output == L3.Input,
L1.TangentVector.VectorSpaceScalar == L2.TangentVector.VectorSpaceScalar,
L2.TangentVector.VectorSpaceScalar == L3.TangentVector.VectorSpaceScalar
I suppose the section in SE-48 should be updated so readers aren't misled into thinking generic typealiases don't support where clauses. I'm not sure when support for that was added though - maybe not at the time of writing.
I think it probably does (e.g. you can have aliases for constraints in Haskell, Rust has it as an unstable feature, and I think you can do this in Scala too). Of course, this is just my 2c.
The questions would be the same as for any other language feature:
What are the main problems that this feature will solve and what is out of scope?
How should the feature be designed? (Hopefully in an extensible manner, so that we can accommodate it with potential changes to the generics system.) What is the mental model and specifically how does scoping work?
How should the feature be implemented? (It probably can, given that there's already a way to get the same behavior as someone pointed out in that thread.)
What you're asking here is for a generalized existential. You're constraining a nested type of the existential type Differentiable which is not supported.
No, it doesn’t need to be an existential at all. It can retain the same “only usable as a generic constraint” restriction that PATs already have.
It actually doesn't seem like a trivial desugaring to me, because the IsFloatColl typealias has no generic parameters in one version, but does in the other.
The additional generic parameter impacts usability in the comment you shared:
extension Collection where IsFloatColl<Element>: Any { ... }
But there's no "PAT type" for the "generic typealias with additional constraints" to desugar to, hence why the workaround is to define a refinement protocol with the additional constraints.
I'm not saying it is super straightforward (or whether it's even desirable). All I'm saying is: it's probably possible to have some version of the feature which satisfies the presented requirements without requiring generalized existentials.
Hypothetically, we could have some kind of rule saying "introduce parameters based on the RHS" or something, kinda' like how you can write
extension Array where Element : MyProtocol { /* blah */ }
today without an explicitly introduced <Element>.
Same goes for the IsFloatColl<Element>: Any constraint -- we don't really need it once we have a notion of a "protocol alias" (this is purely syntax that is giving us to attach the newly introduced constraints so to speak) -- you can probably just write Element : IsFloatColl and we'll rewrite it for you based on the protocol alias. Again, whether that should be done or not is a separate question (and not one I'm commenting on). But it certainly seems do-able . It doesn't seem like an insurmountable problem, only a tricky one.
Right, but it would behave like an existential type when used in a generic constraint.
If I wrote this today
typealias Foo<T> = T where T : Sequence, T.Element == Int
Then it is not valid to write
func foo<T : Foo>(_: T) {}
But presumably, you could do that if you instead wrote
typealias Foo = Sequence where .Element == Int
So the syntax being proposed here is quite different than a generic typealias. It is more like a shorthand for a protocol type and a where clause to be expanded at the point of use (even if you decide not to allow it to be used as a type of a value).
In fact the above syntax is almost the same as the proposed syntax for generalized existentials:
Any<Sequence where .Element == Int>
The only difference is that the proposed typealias form forces you to name the new type.
Yes, this is pretty much what I had in mind. Maybe what others had in mind too.
This new topic deviated from generic typealiases after @typesanitizer answered my initial question, sorry. This new topic should probably be its own thread to avoid confusion.
So the syntax being proposed here is quite different than a generic typealias. It is more like a shorthand for a protocol type and a where clause to be expanded at the point of use (even if you decide not to allow it to be used as a type of a value).
(emphasis added)
I agree with this interpretation -- I think what is being asked for is a way to bundle up constraints and refer to them by some shorthand, without any additional exists quantifier. (And this is what I was referring to in my original comment about Haskell's constraint aliases and Rust's trait aliases).