-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Haskell-style Typeclasses #120
Comments
[#120] Add description for type variables.
Perhaps Sanctuary can provide some guidance. Here's the definition of S.map = def('map', {f: [Z.Functor]}, [Fn(a, b), f(a), f(b)], Z.map); String representation: S.map.toString();
// => 'map :: Functor f => (a -> b) -> f a -> f b' Possible type errors: S.map(x => x, 'XXX');
// ! TypeError: Type-class constraint violation
//
// map :: Functor f => (a -> b) -> f a -> f b
// ^^^^^^^^^ ^^^
// 1
//
// 1) "XXX" :: String
//
// ‘map’ requires ‘f’ to satisfy the Functor type-class constraint; the value at position 1 does not. S.map(n => n < 0 ? null : Math.sqrt(n), [9, 4, 1, 0, -1]);
// ! TypeError: Type-variable constraint violation
//
// map :: Functor f => (a -> b) -> f a -> f b
// ^
// 1
//
// 1) 3 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber
// 2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber
// 1 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber
// 0 :: Number, FiniteNumber, Integer, ValidNumber
// null :: Null
//
// Since there is no type of which all the above values are members, the type-variable constraint has been violated. Sanctuary's checking happens at run time, though, so the ideas may not translate to this project. |
Thanks for your input, @davidchambers -- very interesting. I started working on rtype because I felt a need for a standard way to describe JavaScript function signatures and object interfaces that projects like TypeScript, Flow, and Haskell's signatures don't solve. Haskell's signatures seem to come the closest to what I was looking for, but I also wanted to include parameter names in the signature, describe default values, etc... My thinking about how to express those concepts was heavily influenced by JavaScript itself. I wanted a signature notation that would be easy for JavaScript developers to read, even if they were not familiar with rtype signatures. I use rtype signatures in documentation, blog posts, and books, the same way you'd use a Haskell signature to describe a function's type before expanding on it in a prose description. We do want to build runtime type checking and static analysis tools that can benefit from Rtype, but those efforts haven't had much attention, yet. |
Clarifying the Meaning of
|
Further, we can eliminate the clarifying names and only include necessary type information. The question is, does it make it more readable, or less? mixin(a) => b: a & e
composeMixins(...mixins: [...mixin]) => (
o = {},
(...mixins) => o => m: o & e
) => m |
Does this mean that |
In the last (shortest) example, Within the Similar to function scope in JS, type variables defined inside the definition of a signature are only in scope for that signature, so |
I just wanna point out that
This is primordial. |
@Mouvedia The "chained" arrow functions are just currying. Anybody familiar with arrow function currying in JavaScript, or Haskell type notation should recognize it immediately. Related: "Familiarity Bias is Holding You Back: It's Time to Embrace Arrow Functions" |
Define:
|
In this comment, you are inferring numerous types; by bi-directional I meant that you will have to scan in both direction to find the reason. The "originator" either has a type explicitly set or a predefined type and does not depend on another "type variable". |
If a type variable has not been defined, a new type variable is created automatically in the scope of the signature. You're correct in saying that we can infer things about the type based on how it's used, but generally speaking, that's a concern for static analysis tools. Developers can just use the types and not worry about how the inference works. |
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. |
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:
add2(a: Number, b: Number) => Number
.this
?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:
In this example,
a
andb
are both type variables, andf a
andf b
mean "functor ofa
" and "functor ofb
", respectively.The
Functor f =>
is called a type constraint. It basically says, "in this definition, f represents theFunctor
typeclass". Like generic type constructors in other languages, a typeclass takes a type parameter, sof a
represents a type that is a member of theFunctor
typeclass, andf b
represents a possibly different type that also a member of theFunctor
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 aFunctor
type, and takesthis
as an argument.We need the best of both worlds.
Proposal
What if we could do this in rtype?
And for Mappables using
this
:Where
Functor(a)
is the type ofthis
in the map method. The~>
forthis
syntax is lifted from the Fantasyland specification.Instead of specifying a type constraint with a variable name, we'll explicitly say
Functor(a)
orFunctor(b)
instead off a
orf b
.updateWhere()
is a curried function that takes a predicate, an updater function froma
tob
, (wherea
andb
represent any type, and may refer to the same type), and aFunctor
ofa
, and returns a newFunctor
ofb
, with the matching elements replaced with updated versions.The text was updated successfully, but these errors were encountered: