-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Allow classes to be parametric in other parametric classes #1213
Comments
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 Try using the |
Thanks, corrected. |
I've updated my comment into a proposal. |
👍 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 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 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 |
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? |
#1213 (comment) 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 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 |
I made some progress on this issue on the user end of things : free-types I focused my implementation on general-purpose type-level programming, so I included things like:
I didn't do an amazing job at providing an overview of the functionalities in the readme, because there are just so many things you can do with something like that, but there is a good guide which explains how the library works, how it's designed and the limitations it has. I used it in the project type-lenses to enable reaching the values of arbitrary types or manipulating them with arbitrary type-level functions, and in ts-spec to implement equality. I'm also using it in a project that aims at converting a class to a collection of curried functions with all the types correctly wired, which could be a way to easily turn a fantasy-land compliant class into a static-land-like collection of functions, but also any arbitrary class, with some more work from the part of the implementer. I need advice regarding performance: stacking combinators and compositions on top of each other adds up, but I don't have a solid way of evaluating my design decisions to make the building blocks as lightweight as possible. The value of reusing types to an extreme is questionable but I find it to be an interesting question ;) There are also limitations and pain points which may have workarounds or even solutions I don't know about. I don't know if this approach is a dead end but I definitely hit a wall at some point and I don't imagine a full featured and performant solution can be implemented solely on the user end. |
Alternative syntax proposal: Using the type A<X> = X;
type M = [A<infer Intermediate>, A<Intermediate>]; This toy example obviously simplifies to type M<X> = x[]; but that doesn't matter. |
"2014" 😔 |
About the syntax - I think arity can be inferred from usage. More important would be
For example
Most of the type checking could be deferred to the instance call
I think it is more clear for the reader and probably more clear to implement. |
@craigphicks You could go one simpler: ditch the
The hard part of implementing this isn't the syntax though - it's the semantics. Retrofitting higher-kinded types into a language with type inference almost always requires at least a partial rewrite of the type checker as it mucks with inference a lot (and can rapidly slow it down). |
Note: I originally posted it as a separate proposal but was redirected here as my proposal was claimed to be a duplicate. What I am proposing imposes no extra logic on the type checker - because the meta type is never evaluated until all it's parameters have been supplied, at which time it rendered to a type. The original proposal 1213 is a lot more powerful - but also much harder to implement. I am starting from a very simple simple problem:
For each That is the simplest version, the result of which has no unresolved generic parameters, so obviously no inference is involved. The next level of complexity would be to implement resolving just
which would be equivalent to
and that would only be a current TypeScript generic, so it would not impose any extra requirements on the inference logic. |
@PhilippDehler I don't see how your solution addresses the problem of passing generics to generics, since the return types are fixed and not generic. |
I dream of functional programming with TS types - I really wish that there was a chance that at some point this could happen. |
@IARI did you have a look at Effect-TS? It uses a lot of functional patterns with a very pragmatic approach. The job they are doing is incredible. Its implementation of HKT is sufficient for its needs, with a fixed number of parameters of a specific variance. On my end I am working on improving free-types API and behaviour but I am convinced that you need some framework to do FP in TS comfortably, or you need to be prepared for basically rewriting it yourself. |
if anyone want inspiration for HKT with any number of parameters, https://github.com/ecyrbe/typeskell example for defining a monad : export interface Of<F extends Kind> {
/**
* of :: `a -> F a`
*/
of: <A>(a: A) => $<F, [A, ...Tail<ToDefaultParam<F['signature']>>]>;
}
export interface Functor<F extends Kind> {
/**
* map :: `(a -> b) -> F a -> F b`
*/
map: TypeSkell<'(a -> b) -> F a ..e -> F b ..e', { F: F }>;
}
export interface Applicative<F extends Kind> extends Functor<F>, Of<F> {
/**
* ap :: `F a -> F (a -> b) -> F b`
*/
ap: TypeSkell<'F a ..x -> F (a -> b) ..y -> F b ..xy', { F: F }>;
}
export interface Monad<M extends Kind> extends Applicative<M> {
/**
* flatMap :: `(a -> M b) -> M a -> M b`
*/
flatMap: TypeSkell<'(a -> M b ..x) -> M a ..y -> M b ..xy', { M: M }>;;
} |
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
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
Parametric type parameters can take any number of arguments:
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:
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):
The arities of the definition and the alias must match for the typedef to be valid.
The text was updated successfully, but these errors were encountered: