Code Monkey home page Code Monkey logo

higher_kinded's Introduction

"Higher kinded types"

OCaml natively supports parameterized type constructors, such as option. The parameters of a type constructor may only be types, not arbitrary type constructors.

The following is not legal syntax:

type 'a person =
  { name : string 'a
  ; age  : int 'a
  }

It is not possible to define such a type where 'a can be replaced with something like option or ref, because you can't apply 'a to other types like string or int. In other words, although int option is a valid type expression, int 'a is not.

The Higher_kinded library makes something similar possible. The above example would be defined like this:

type 'a person =
  { name : (string -> 'a) Higher_kinded.t
  ; age : (int -> 'a) Higher_kinded.t
  }

The fundamental concept of Higher_kinded is that a value of type (a -> ... -> z -> C.higher_kinded) Higher_kinded.t is equivalent to a value of type (a, ..., z) C.t. The only reason it is rendered as a function is that -> is the only right associative type operator, which is useful for reasons that will be explained later.

A signature defining a type constructor can include one of the Higher_kinded.S signatures, and its implementation should use one of the Higher_kinded.Make functors. For example, Option could look something like this:

# module Option : sig
    type 'a t = 'a option

    include Higher_kinded.S with type 'a t := 'a t
  end = struct
    type 'a t = 'a option

    include Higher_kinded.Make (Base.Option)
  end
module Option :
  sig
    type 'a t = 'a option
    type higher_kinded
    val inject : 'a t -> ('a -> higher_kinded) Higher_kinded.t
    val project : ('a -> higher_kinded) Higher_kinded.t -> 'a t
  end

Now it is possible to define values of type (int -> Option.higher_kinded) Higher_kinded.t:

# let a = Option.inject (None : int option)
val a : (int -> Option.higher_kinded) Higher_kinded.t = <abstr>
# let b = Option.inject (Some 42)
val b : (int -> Option.higher_kinded) Higher_kinded.t = <abstr>

Here is how to observe them:

# Option.project b
- : int option = Some 42

Now that Option can be used this way, we can express the person example from earlier:

# let alice = { name = Option.inject (Some "alice doe"); age = Option.inject None }
val alice : Option.higher_kinded person = {name = <abstr>; age = <abstr>}

If we did the same thing with refs:

module Ref : sig
  type 'a t = 'a ref

  include Higher_kinded.S with type 'a t := 'a t
end = struct
  type 'a t = 'a ref

  include Higher_kinded.Make (Base.Ref)
end

we could write:

# let secret_agent =
    { name = Ref.inject (ref "alice"); age = Ref.inject (ref 55) }
val secret_agent : Ref.higher_kinded person = {name = <abstr>; age = <abstr>}

Here's how we could modify the references:

Ref.project secret_agent.name := "Austin Powers";
Ref.project secret_agent.age := 35

The inject and project functions have no runtime cost; they only change the type.

You can also use Higher_kinded for types that have multiple type parameters. Here is an example using Result:

# module Result : sig
    type ('a, 'e) t = ('a, 'e) result

    include Higher_kinded.S2 with type ('a, 'e) t := ('a, 'e) t
  end = struct
    type ('a, 'e) t = ('a, 'e) result

    include Higher_kinded.Make2 (Base.Result)
  end
module Result :
  sig
    type ('a, 'e) t = ('a, 'e) result
    type higher_kinded
    val inject : ('a, 'z) t -> ('a -> 'z -> higher_kinded) Higher_kinded.t
    val project : ('a -> 'z -> higher_kinded) Higher_kinded.t -> ('a, 'z) t
  end

You can even use multi-parameter higher kinded witnesses in positions expecting lower arity types. For example, suppose that you had an error type defined:

type error = 
  | Integer_overflow

Then you could write:

# let immortal =
    { name = Result.inject (Ok "Keanu")
    ; age = Result.inject (Error Integer_overflow)
    }
val immortal : (error -> Result.higher_kinded) person =
  {name = <abstr>; age = <abstr>}

The resulting type uses a partially applied witness (in the above example, error -> Result.higher_kinded) that explains how to fill in the remaining arguments.

This library is a variation on Jeremy Yallop and Leo White's "Lightweight higher-kinded polymorphism".

higher_kinded's People

Contributors

aalekseyev avatar xclerc avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.