10

In fp-ts they have this workaround for higher kinded types:

export interface HKT<URI, A> {
  readonly _URI: URI;
  readonly _A: A;
}

And it can be used like this:

export interface Foldable<F> {
  readonly URI: F;
  reduce: <A, B>(fa: HKT<F, A>, b: B, f: (b: B, a: A) => B) => B;
}

What are the members _URI and what is _A?

chrisber
  • 730
  • 1
  • 12
  • 27
dagda1
  • 26,856
  • 59
  • 237
  • 450
  • is it clearer if written the following way? https://gist.github.com/jnizet/f8cc19c6e6c6f4937268cd5d095e851c. – JB Nizet Apr 14 '18 at 07:44
  • 4
    `String` is a ground type, i.e. it has values. Let's denote ground types with `*`. `[a]` is a parameterized type, because it requires another ground type `a`. Consequently it has the form `* -> *`and is called a first-order type. Now a higher-order (or higher-kinded) type abstracts from the parameterized type `[]` and has the form `(* -> *) -> *`. `_URI` represents this first class paramterized type `(* -> *)` and `_A` represents the ground type `*`. That means an instance of `Foldable` requires a parameterized type (e.g. `[]`) and a ground type (e.g. `String`) to become a ground type itself. –  Apr 14 '18 at 10:11

1 Answers1

10

This article written by the author of fp-ts explains very well,in combination with documentation on TypeScript's union types.

_A is the type of the value in our HKT: HKT<"Option", string> => Option<string>. We'll see how mapping works in a bit.

_URI is the identifier for the HKT, for example Option.ts. It has 2 instances, None and Some, both of which have "Option" as their _URI.

Taking a look at the usage of F in Foldable we can see it's used in the Type type, along with A; Type<F, A>. Assume F == "Option" & A == string so Type<F, A> == Type<"Option", string>, which is very similar to Option<string>. Currently, we cannot pass a generic type to another generic as it's generic parameter: <F,A>(fa: F<A>) will not compile and this is the main problem.

To achieve Type<"Option", string> => Option<string>, there are several "mapping" interfaces (this is explained in the article). We can see this done for Option here:

declare module './HKT' {
  interface URI2HKT<A> {
    Option: Option<A>
  }
}

The leftmost Option is a string key, the rightmost is the actual Option type, this is a normal record interface after all. Let's have a quick look at the definition of Type: type Type<URI extends URIS, A> = URI2HKT<A>[URI].

URI2HKIT is a map from our "Option" to our actual Option. It's allowing is to pass 2 generic parameters to get back our value type wrapped in our higher kinded type, solving the generics issue mentioned earlier.

Community
  • 1
  • 1
MaxWillmott
  • 2,170
  • 2
  • 23
  • 34