Haskell-style Typeclasses
See original GitHub issueWhen 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:
- Haskell types are all curry all the time. I wanted to be able to express:
add2(a: Number, b: Number) => Number
. - How do you deal with
this
? - 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.
Issue Analytics
- State:
- Created 6 years ago
- Comments:14 (11 by maintainers)
Top GitHub Comments
Note to self: Update the syntax, clarify any lingering questions, create a PR.
I’m already using higher kinded types in lots of documentation.
Updated original proposal description. I’ve been using this syntax for quite a while now and testing it in mentorship sessions. Mentees seem to pick up what’s going on quickly, and people familiar with Haskell should be able to use what they know about Haskell to learn the ropes.