TypeScript
TypeScript copied to clipboard
Allow classes to be parametric in other parametric classes
This is a proposal for allowing generics as type parameters. It's currently possible to write specific examples of monads, but in order to write the interface that all monads satisfy, I propose writing
interface Monad<T<~>> {
map<A, B>(f: (a: A) => B): T<A> => T<B>;
lift<A>(a: A): T<A>;
join<A>(tta: T<T<A>>): T<A>;
}
Similarly, it's possible to write specific examples of cartesian functors, but in order to write the interface that all cartesian functors satisfy, I propose writing
interface Cartesian<T<~>> {
all<A>(a: Array<T<A>>): T<Array<A>>;
}
Parametric type parameters can take any number of arguments:
interface Foo<T<~,~>> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}
That is, when a type parameter is followed by a tilde and a natural arity, the type parameter should be allowed to be used as a generic type with the given arity in the rest of the declaration.
Just as is the case now, when implementing such an interface, the generic type parameters should be filled in:
class ArrayMonad<A> implements Monad<Array> {
map<A, B>(f: (a:A) => B): Array<A> => Array<B> {
return (arr: Array<A>) => arr.map(f);
}
lift<A>(a: A): Array<A> { return [a]; }
join<A>(tta: Array<Array<A>>): Array<A> {
return tta.reduce((prev, cur) => prev.concat(cur));
}
}
In addition to directly allowing compositions of generic types in the arguments, I propose that typedefs also support defining generics in this way (see issue 308):
typedef Maybe<Array<~>> Composite<~> ;
class Foo implements Monad<Composite<~>> { ... }
The arities of the definition and the alias must match for the typedef to be valid.
Not to make any rash assumptions, but I believe you're typing it incorrectly. All parameter types require parameter names, so you probably meant to type
map<A, B>(f: (x: A) => B): T<A> => T<B>;
whereas right now map is a function that takes a mapper from type any
(where your parameter name is A
) to B
.
Try using the --noImplicitAny
flag for better results.
Thanks, corrected.
I've updated my comment into a proposal.
:+1: higher kinded type would be a big bonus for functional programming construct, however before that I would prefer to have correct support for higher order function and generic :p
Quasi-approved.
We like this idea a lot, but need a working implementation to try out to understand all the implications and potential edge cases. Having a sample PR that at least tackles the 80% use cases of this would be a really helpful next step.
What are people's opinions on the tilde syntax? An alternative to T~2
would be something like
interface Foo<T<~,~>> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}
that allows direct composition of generics instead of needing type aliases:
interface Foo<T<~,~,~>, U<~>, V<~, ~>> {
bar<A, B, C, D>(a: A, f: (b: B) => C, d: D): T<U<A>, V<B, C>, D>;
}
It's odd to have explicit arity since we don't really do that anywhere else, so
interface Foo<T<~,~>> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}
is a little clearer, though, I know other languages use *
in similar contexts instead of ~
:
interface Foo<T<*,*>> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}
Though taking that point to an extreme, you might get:
interface Foo<T: (*,*) => *> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}
I think T<~,~>
is clearer than T~2
, too. I'll modify the proposal above. I don't care whether we use ~
or *
; it just can't be a JS identifier, so we can't use, say, _
. I don't see what benefit the =>
notation provides; all generics take some input types and return a single output type.
A lighter-weight syntax would be leaving off the arity of the generics entirely; the parser would figure it out from the first use and throw an error if the rest weren't consistent with it.
I'd be happy to start work on implementing this feature. What's the recommended forum for pestering devs about transpiler implementation details?
You can log many new issues for larger questions with more involved code samples, or make a long running issue with a series of questions as you go. Alternatively you can join the chat room here https://gitter.im/Microsoft/TypeScript and we can talk there.
@metaweta any news? If you need any help/discussion I would be glad to brainstorm on this issue. I really want this feature.
No, things at work took over what free time I had to work on it.
bump: is there a chance to see this feature ever considered?
https://github.com/Microsoft/TypeScript/issues/1213#issuecomment-96854288 is still the current state of it. I don't see anything here that would make us change the priority of the feature.
Seems to me like this is useful in far more situations than just importing category theory abstractions. For example, it would be useful to be able to write module factories that take a Promise
implementation (constructor) as an argument, e.g. a Database with a pluggable promise implementation:
interface Database<P<~> extends PromiseLike<~>> {
query<T>(s:string, args:any[]): P<T>
}
Would come in handy here too http://stackoverflow.com/questions/36900619/how-do-i-express-this-in-typescript
:+1:
with HKT's mindsets can be changed, habits broken, lost generations brought back to life, it would the biggest thing since generics and explicit nulls and undefineds, it can change everything
please consider it as a next big feature, stop listen to people who keep asking you for a better horse, give them a f***g ferrari
Yup, Bumped to this the first 15 minutes after trying to add types to existing JS codebase. I am not switching to TS until I see it.
CanI help, actually?
I wonder how this would relate to #7848? They're very similar, although about the other facet of higher order kinds.
@boris-marinov Ryan Cavanaugh’s reply says you can:
Having a sample PR that at least tackles the 80% use cases of this would be a really helpful next step.
Now I have time to implement such a simple PR Hope to get some hints frome core devs, but there are no questions so far - all looks good and understandable. Will track a progess here.
@Artazor Would you like to take a look at cracking #7848 as well? That takes care of the other side of this problem, involving generics, and IMHO this would feel incomplete without it (generic parameters would really simplify a lot of type-level code).
I think this proposal is absolutely wonderful. Having higher kinded types in TypeScript would take it up to a hole new level where we could describe more powerful abstractions than what is currently possible.
However, isn't there something wrong with the examples given in OP? The A
in the line
class ArrayMonad<A> implements Monad<Array> {
isn't used in any of the methods, since they all have their own generic A
.
Also, if implementing functor with map
as a method that uses this
what would it look like? Like this maybe?
interface Functor<T, A> {
map<B>(f: (a: A) => B): T<A> => T<B>;
}
class Maybe<A> implements Functor<Maybe, A> {
...
}
@paldepind Check out #7848. That discussion is about that particular use case, although IMHO this and that one really needs merged into a single PR.
When does this stuff is going to land? That seems like a kind of essential.
Also will it going to make possible such:
interface SomeX<X, T> {
...// some complex definition
some: X<T>
}
interface SomeA<T> extends SomeX<A, T> {
}
?
@whitecolor I think there's bigger fish to fry at the moment, which merit higher priority:
- TypeScript 2.0 RC was released only a little under 2 weeks ago. That'll take up a lot of time in of itself.
-
bind
,call
, andapply
, native JS functions, are untyped. This actually depends on the variadic generics proposal.Object.assign
also needs a similar fix, but variadic generics alone won't solve that. -
Functions like Lodash's
_.pluck
, Backbone models'get
andset
methods, etc. are currently untyped, and fixing this basically makes Backbone usable with TypeScript in a much safer way. It also may have implications for React in the future.
Not that I don't want this feature (I would love for such a feature), I just don't see it as likely coming soon.
@isiahmeadows Thanks for explanation. Yeah 3rd item in the list is very important, waiting for https://github.com/Microsoft/TypeScript/issues/1295 too.
But I hope for current issue maybe in 2.1dev somehow.
I agree. Hopefully it can make it in.
(Rank 2 polymorphism, which this issue wants, is also a necessity for Fantasy Land users, to properly type the various ADTs within that spec. Ramda is a good example of a library that needs this fairly badly.)
On Tue, Sep 6, 2016, 11:00 Alex [email protected] wrote:
@isiahmeadows https://github.com/isiahmeadows Thanks for explanation. Yeah 3rd item in the list is very important, waiting for #1295 https://github.com/Microsoft/TypeScript/issues/1295 too.
But I hope for current issue maybe in 2.1dev somehow.
— You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/1213#issuecomment-244978475, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBMvxBALBe0aaLOp03vEvEyokvxpyks5qnX_8gaJpZM4C99VY .