*->* ale taky *->*->*->* jsou vyšší druhy (HKT) prvního řádu (analogicky klasické funkce).
(*->*)->* je vyšší druh (HKT) vyššího řádu (HRT) (analogicky funkce vyššího řádu).
Imho se mýlíš. Snad se shodnem na tom, že List<T> v Javě má kind * -> * a List<Int> má kind *. No a pak si už můžeš snadno kdekoli najít, jestli Java má nebo nemá higher-kinded types. Najdeš, že nemá. Protože * -> * je first order kind a není to teda "higher-order kind" neboli "higher-kinded type". V Javě nejsou typy s kindem (* -> *) -> *, narozdíl od Haskellu.
Proč si myslím, že mám pravdu, se pokusím ilustrovat. K tvé poznámce o Javě: Problém je, že Java je na tohle špatný příklad. Její typový systém má různá jiná omezení, a to, že nejde definovat typ s kindem (*→*)→* není jenom kvůli tomu, že nepodporuje HK. Krásně je to vidět v Rustu, ten taky nemá HK, ale přesto se dá deklarovat např:
struct Kráva<T, I: FromIterator<T>> { ... }
Jako demonstrace toho, na co dojede jazyk bez HK, a potažmo kde vlastně začínají HK, si vezměme starou známou monádu. V Haskellu se operátor bind (>>=) deklaruje následovně:
(>>=):: Monad m => m a → (a → m b) → m b
Typové proměnné a a b jsou volné, implicitně je to forall a,b. Parametr je tady pouze m s kindem *→*. To je přesně ten klíčový moment, který jazyk bez HK nedovolí. Když se totiž budu snažit o totéž v Rustu, compiler mi nevezme nic jako
fn bind<A, B, M<A>: Monad>(a: M<A>, f: FnOnce(A)→M<B>)→M<B>
právě proto, že M tady má kind *→*. Jediná možnost bude něco na následující způsob:
trait Monad<A> {
fn bind<B, R: Monad<B>>(self, f: FnOnce(A)→R)→R;
}
kde se Self (neboli ekvivalent M<A>) předem zredukuje na kind * instanciací pro dané A. Totéž platí pro R, resp. M<B>.
V praxi to znamená, že v Haskellu se definuje >>= obecně např. pro monádu IO (*→*) a pak se může rovnou napsat
getLine >>= putStrLn
V Rustu to ale nebude možné. Compiler stejně tak nevezme žádné
impl<A> IO<A> for Monad<A> { ... }
protože typové parametry metody bind zůstávají neinstanciované. Aby to fungovalo, musím nejdřív explicitně deklarovat:
impl IO<()> Monad<()> { … }
impl IO<String> Monad<String> { … }
a pak teprve můžu provést getLine.bind(putStrLn). Neboli musím ručně implementovat Monad<A> (*) pro každý možný typ A, což evidentně není možné, a právě proto v takovám jazyku bez HK nejde zkonstruovat obecnou monádu. Respektive jde, pokud například v Rustu tyhle explicitní deklarace automatizuju makrem, což je přesně to samé, jako když použiju template template v C++. Obojí je ohejbák, jak si vynahradit to, že jazyk neumí typové parametry s kindem *→*.