ericelliott / ericelliott/rtype
Haskell-style Typeclasses
- Dominant language
- JavaScript
- Stars
- 1.1k
- Forks
- 38
- PR merge metrics
- No merged PRs in 30d
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:
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:
```haskell
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?
```js
fmap = (a => b) => Functor(a) => Functor(b)
```
And for Mappables using `this`:
```js
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`.
```js
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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.