Letting us write Self<...> would be extremely helpful for a bunch of functional programming patterns and to me (who doesn't know much about how the compiler works), it seems like we're 90% there? Any posts I've seen that want something similar seem to be older than generic protocols, so I hope you don't mind that I ask :)
Unless I really missed something, that is not the case (and just an example for the confusion predicted in the discussion about primary associated types ).
I've always thought of it this way: In protocols with associated types, all references of type Self actually resolve to (in this case) the type Arrow<S, T>. The syntax you show would have Self resolve to unadorned Arrow which is not a type, but a type constructor. Adding <T, U> to Arrow is precisely using Arrow as a type constructor to construct a new type.
There's just no syntax combination which allows surface level language users to directly access Arrow as a type. Such a type would be a higher kind of type (pun intended).
Dunno if that explanation helps anyone else, but its what I finally settled on for myself.
I don't think Self is relevant to this topic. The generic type parameters of a protocol do not have to match up with those of adopting types, so re-specializing is impossible at the protocol level. Switcheroos of placeholder types, I think, always need to be copy-pasted to the type level.
While the following is not as constrained as re-specialized Selfs, it might be useful, but the second some here can't be expressed:
protocol Arrow<S, T> {
associatedtype S
associatedtype T
func compose<U>(_ next: some Arrow<T, U>) -> some Arrow<S, U> // 'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type?
}
The way I envisioned it, the function wasn’t generic over arrows. Composing arrows with arbitrary arrows of appropriate generic type is pretty meaningless.
Precisely. The challenge I've issued to students before is: write a protocol Mappable which covers map on both Array and Optional. The protocol must be written in a way that does not allow Array's implementation to return Optional or Optional's implementation to return Array.
When folks really grok why they can't do that, they see what higher-kinded types are all about. And they eventually see why Sequence's implementation returns Array and why Combine's implementation returns a specific MapPublisher.
Swift allowing this some day is not outside the realm of possibility, but it's not completely trivial (the Rust folks spent several years designing and implementing this, AFAIK).
It would require an overhaul of how the requirement machine builds a rewrite system from requirements, which currently makes a pretty fundamental assumption that associated types are unary type constructors (T.Element is kind of like Element<T>; a generic associated type T.MapResult<E> has two arguments, T and E).
We would also need to think about how the runtime type metadata side of this works, too.
One day I will finish (start) documenting how the requirement machine works, and maybe then more folks can get involved in discussing future directions for generics.
That's a good question. I think the right way to think about this is that specialMap() returns a generic associated type, not a hypothetical re-parameterized Self:
String would just have to conform with MapResult being Array, to allow map() in full generality:
extension String: Mappable {
typealias Element = Character
typealias MapResult<Result> = Array<Result>
}
However this raises the question of what MapResult could possibly be, other than Array. Clearly Optional could witness MapResult<Result> with Optional<Result>, and perhaps in a LinkedList type, it could be LinkedList<Result>. However even with Set, you'd be prevented from setting typealias MapResult<Result> = Set<Result>, because Result isn't required to be Hashable here.
I would be fascinated to read such documentation. I don’t know if perhaps this would be too casual or simply not your style, but I’m sure it would reduce the friction enormously of creating at least some documentation if you simply did a screen recording of your IDE where you just start talking about how it works in whatever way first comes to your mind (with surely a small amount of initial thought about where to start), jumping into definitions and then back again while you point out any and all relevant information that you think of. No pressure, just wanted to give the idea…
Parameterized associate types are how ML languages model higher kinded types0, but they're free functions (or functions in a module). I think the equivalent would be making a separate mapper type:
That's an excellent link @Slava_Pestov. I especially liked the part about how we teach these concepts, as that has been a particular problem for me. I find that most of my students have problems with the idea of a type constructor (associated or not) and I wonder if referring to them instead as generic_associated_types actually fixes that problem.