However, I read though the readme and I have no idea what the usefulness of this is. Can anyone explain, in simple terms, some practical use cases for this?
However, I read though the readme and I have no idea what the usefulness of this is. Can anyone explain, in simple terms, some practical use cases for this?
This lib has a method `select` to return an observable list of DOM elements for a given CSS selector. What should be its signature? If using `xstream`, it should be `select(selector: string): Stream<HTMLElement[]>`, and if using `rxjs` it should be `select(selector: string): Observable<HTMLElement[]>`. Let's also assume that it has a second method `windowHeight` returning an observable for the window height (`Stream<number>` or `Observable<number>`).
We can make the lib object generic over the observable implementation, but you need HKTs to type it properly. The reason is that the lib is generic over an already generic type.
Here is an example:
// Without HKT (regular generic)
// Problem: both `select` and `windowHeight` return the same type (we lose the HTMLElement[]/number information)
interface Cycle<Obs> {
select(selector: string): Obs;
windowHeight(): Obs;
}
// The best we can do is type it as `Cycle<Stream<unknown>>` or `Cycle<rxjs.Observable<unknown>>`.
// With HKTs, using syntax from this lib, you could do:
interface Cycle<$HktObs> {
select(selector: string): apply<$HktObs, HTMLElement[]>;
windowHeight(): apply<$HktObs, number>;
}
// This allows to get the precise signatures we wanted (with the right observable impl, without `unknown`)There’s a couple of ways to think about it: it gives you a way to talk about List rather than List of T, it enables you to write partial types like partially-applied functions, or it makes it possible to define Monads.
But as I say, none of these things will sound immediately useful unless you have experience of using those concepts already.
I don’t think the benefit ever materializes and highly abstract code is just indulgence.
Much like the people who endlessly tinker with their IDE/emacs/desktop environment/shell in the name of productivity.
People believed (and some still do believe) the same thing about generics.
A real world use-case could be parsing GraphQL raw string queries and automatically infer the returned types based on a common schema, without using special code-generators. For instance you can come up with some magic function `gql_parsed` like:
doc = gql_parsed`query GetUser { user { name }}`
where doc is inferred as something like Doc<Query<{GetUser:{user:{name:string}}}>>
They're fairly common in Scala too, and I believe in OCaml through modules.