-
Notifications
You must be signed in to change notification settings - Fork 38
Description
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:
- 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 bIn 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.