HKTs and currying

withoutboats makes an interesting comment here:

Higher kinded polymorphism results in trivially undecidable type inferences without something like currying; the restrictions needed to support it would be arbitrary and weird given that Rust does not have currying (essentially some rules to reconstruct the restrictions of currying in type operator context).

I'm not a Haskell expert but I've dabbled enough to know what currying is, and I have rough familiarity with HKTs from C++ (which has both GATs and template template parameters). Still, I'm surprised to learn these things are somehow intertwined. How does currying help? How is currying a "restriction"? It just seems like a terser way to write a lambda function wrapper that has already locked in one argument to the underlying function, e.g. double = multiply 2.

3 Likes

Consider the type:

struct Foo<A, B, C>;

Now, note that Foo is a HKT (Whereas Foo<Bar, Baz, Quux> is a regular type).


In a parallel context, consider the function:

fn bar(a: usize, b: usize, c: usize) -> String { panic!() }

Note that foo is a function (whereas bar(0, 1, 2) is a String).


If we curry bar with 2, we essentially say |b, c| bar(2, b, c) and create a curried function. Similarly, one may want to curry the HKT with, say, String:

type Curried<B, C> = Foo<String, B, C>;

However, currying isn't really built into Rust. Amos is referencing this fact, but in the type context, where currying HKTs is useful in his perspective.

In my perspective, I see some value to this operation, as though using hacks or things isn't really nice, and is usually verbose or very opaque in understanding.

1 Like

Currying at the type level just seems like syntactic sugar for the alias you wrote. I still don't see why currying creates any kind of restriction or would limit the usefulness of HKTs?

Here's an article discussing some of the limitations and challenges.

1 Like