No higher-kinded types

Option, Result, Iterate and Task share method names with the same meaning as a convention of the standard library, not as a shared trait, because the language has no way to be generic over a type constructor.

Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite

Option, Result, Iterate and Task all have a map, and map always means "transform what is inside, keep the shape" - by convention of the standard library, not because a trait ties the four maps together.

Example#

const found: Int? = 3
print(found.map { _ * 2 })

const numbers: List<Int> = [1, 2, 3]
print(numbers.map { _ * 2 }.toList())

Syntax#

Option<Value>.map<Output>(transform: Transform<Value, Output>): Output?
Iterate<Item>.map<Output>(transform: Transform<Item, Output>): Iterate<Output>

Same name, same shape of signature, two unrelated declarations - neither extends a trait the other implements.

Rules#

  1. The language has no higher-kinded types: no Functor<F<_>>, no Self<U>, no partially applied type constructor. A type parameter always stands for a concrete type, never for "some type with a hole in it."

    Kompiliert nicht
    trait Functor<Item> {
      fn map<Output>(): Self<Output>
    }
    // error: `Self` takes no type arguments

What this is not#

The shared vocabulary is not a reason to write one generic function over all of them. No bound exists that could accept an Option, a Result and an Iterate in the same parameter.

Kompiliert nicht
fn anyDoubled<Container: Functor>(value: Container): Container {
  value
}
// error: Unknown type `Functor`
  • Why there are no higher-kinded types - why this trade-off was made, the alternatives it rejected, and what stands in for a Functor on each of the four types.
  • Type parameters - what a type parameter can stand for, and what it cannot.
  • Witness tables - what does exist for calling a bound's members generically.
  • Bounds - what a bound can name, since it cannot name a shape like Functor.
Esc