Skip to content

Haskell-style Typeclasses #120

@ericelliott

Description

@ericelliott

When I first started rtype, I was tempted to just steal Haskell's Hindley-Milner type annotations and just start using them, but I saw several problems with that:

  1. Haskell types are all curry all the time. I wanted to be able to express: add2(a: Number, b: Number) => Number.
  2. How do you deal with this?
  3. I wanted to be able to name parameters for clearer documentation. Haskell's types are very expressive and clean-looking, but it's sometimes hard to figure out what the type variables represent in real code.

So, we abandoned Hindley-Milner altogether, and forgot all about typeclasses. Ever since, we've been struggling with the question: How do we represent type polymorphism, and especially, higher order functions like map. Take Haskell's Functor type:

fmap :: Functor f => (a -> b) -> f a -> f b

In this example, a and b are both type variables, and f a and f b mean "functor of a" and "functor of b", respectively.

The Functor f => is called a type constraint. It basically says, "in this definition, f represents the Functor typeclass". Like generic type constructors in other languages, a typeclass takes a type parameter, so f a represents a type that is a member of the Functor typeclass, and f b represents a possibly different type that also a member of the Functor typeclass.

We currently have no way to express this in rtype, and because I do a lot of functional programming, I have been falling back on Haskell's Hindley-Milner types in a lot of my writing.

I'm frustrated with that because as I mentioned already, switching from rtype to Hindley-Milner or vice verse, we lose a lot of valuable information. Haskell's Hindley-Milner notation doesn't express enough about the names and purposes of variables, lacks ...rest, etc..., and also doesn't naturally allow for uncurried multi-arg functions.

If we try to express a lot of higher-order functions in rtype, it falls flat. To complicate matters, I'm building lots of types that implement a bunch of stuff with methods, meaning I need a way to say that map is a method on a Functor type, and takes this as an argument.

We need the best of both worlds.

Proposal

What if we could do this in rtype?

fmap = (a => b) => Functor(a) => Functor(b)

And for Mappables using this:

interface Mappable {
  map: Functor(a) ~> (a => b) => Functor(b)
}

Where Functor(a) is the type of this in the map method. The ~> for this syntax is lifted from the Fantasyland specification.

Instead of specifying a type constraint with a variable name, we'll explicitly say Functor(a) or Functor(b) instead of f a or f b.

updateWhere = Predicate => (a => b) => Functor(a) => Functor(b)

updateWhere() is a curried function that takes a predicate, an updater function from a to b, (where a and b represent any type, and may refer to the same type), and a Functor of a, and returns a new Functor of b, with the matching elements replaced with updated versions.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions