Typeclasses

perspective offers many higher kinded versions of typical functional typeclasses. perspective offers these higher kinded typeclasses under the same name as the normal typeclass, with a K suffix. These typeclasses have the kind trait K[F[_[_], _]]. The kind trait KC[F[_[_]]] is exposed under the suffix KC.

Some functions within these typeclasses are also specialized to certain types to help type inference. These functions often have suffixes like Id and Const instead of a K suffix.

Type aliases

perspective uses a few type aliases to make function signatures simpler, and more like their non higher kinded versions.

/** A higher kinded type that ignores its second type parameter. */
type Const[A] = [_] =>> A

type FunctionK[A[_], B[_]] = [Z] => A[Z] => B[Z]

object FunctionK:
  def identity[A[_]]: A ~>: A = [Z] => (a: A[Z]) => a

infix type ~>:[A[_], B[_]] = FunctionK[A, B]

/** A FunctionK returning a [[Const]] type. */
infix type ~>#:[F[_], R] = F ~>: Const[R]

/** A FunctionK taking a [[Const]] type. */
infix type #~>:[T, F[_]] = Const[T] ~>: F

/** A FunctionK taking and returning [[Const]] types. */
infix type #~>#:[T, R] = Const[T] ~>: Const[R]

FunctorK

trait FunctorK[F[_[_], _]]:
  extension [A[_], C](fa: F[A, C])
    def mapK[B[_]](f: A ~>: B): F[B, C]

FunctorK exposes functions to convert F[A] to F[B] using a function to convert from A to B.

ApplyK

trait ApplyK[F[_[_], _]] extends FunctorK[F]:
  extension [A[_], C](fa: F[A, C])
    def map2K[B[_], Z[_]](fb: F[B, C])(f: [X] => (A[X], B[X]) => Z[X]): F[Z, C]

The ApplyK typeclass allows combining several different values in a context into a single value of that context. Apply is a useful tool for many generic programming tasks.

Apply is less common typeclass, a simplification of the typeclass called Applicative. Apply is an Applicative without the function pure. Apply is seperated from Applicative and given more light in perspective because pure seems like useful in higher kinded contexts.

FoldableK

trait FoldableK[F[_[_], _]]:
  extension [A[_], C](fa: F[A, C])
    def foldLeftK[B](b: B)(f: B => A ~>#: B):

FoldableK exposes functions to convert a context F[A] to a value B by applying a function on all the values of the container together the result of the previous application.

For generic programming, FoldableK is useful when each value can be encoded to a non higher kinded type, and combined together.

TraverseK

/** The composition of two higher kinded types. */
type Compose2[A[_], B[_]] = [Z] =>> A[B[Z]]

trait TraverseK[F[_[_], _]] extends FunctorK[F], FoldableK[F]:
  extension [A[_], C](fa: F[A, C])
    def traverseK[G[_] : Applicative, B[_]](f: A ~>: Compose2[G, B]): G[F[B, C]]

As used typically, TraverseK allows one to convert from F[G[A]] to G[F[A]].

For generic programming, TraverseK is useful when doing some operation that can fail, and one wants to collect all these failures.

RepresentableK

trait RepresentableK[F[_[_], _]] extends MonadK[F] with DistributiveK[F]:
  type RepresentationK[_]

  def tabulateK[A[_], C](f: RepresentationK ~>: A): F[A, C]

  def indicesK[C]: F[RepresentationK, C] = tabulateK(FunctionK.identity)

  extension [A[_], C](fa: F[A, C])
    def indexK[Z](i: RepresentationK[Z]): A[Z]

Representable and it's higher kinded variants RepresentableK (and RepresentableKC) is generally a less used typeclass, but plays a central piece for generic programming. This typeclass offers the functions indexK and tabulateK to index and tabulate over a type, in similar ways to List.tabulate and List#apply. The index type used by Representable is not Int, but some other ``