Open metaweta opened 9 years ago
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:
bind
, call
, and apply
, 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._.pluck
, Backbone models' get
and set
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 notifications@github.com 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 .
Seems that this feature would helps us a lot to define react forms. For example, you have struct:
interface Model {
field1: string,
field2: number,
field3?: Model
}
I have a handler, which defined as:
interface Handler<T> {
readonly value: T;
onChange: (newValue: T) => void;
}
this handler passed as a props to React components. Also I have a function, which takes struct and returns same struct, but with Handlers instead of values:
function makeForm(value: Model): {
field1: Handler<string>,
field2: Handler<number>,
field3: Handler<Model>,
}
As for now I can't type that function properly, because TS can't produce type based on structure of other type.
Cow I could type makeForm
with HKT?
Hm, interesting.
Maybe something like this may be possible:
//Just a container
interface Id <A> {
value: A
}
interface Model <T> {
field1: T<string>,
field2: T<number>,
field3?: T<Model>
}
makeForm (Model<Id>): Model<Handler>
@boris-marinov The most interesting point is this line:
interface Model<T> {
//...
field3?: T<Model> // <- Model itself is generic.
// Normally typescript will error here, requiring generic type parameter.
}
might be worth mentioning that HKT could have been an answer to so called partial types (https://github.com/Microsoft/TypeScript/issues/4889#issuecomment-247721155):
type MyDataProto<K<~>> = {
one: K<number>;
another: K<string>;
yetAnother: K<boolean>;
}
type Identical<a> = a;
type Optional<a> = a?; // or should i say: a | undefined;
type GettableAndSettable<a> = { get(): a; set(value: a): void }
type MyData = MyDataProto<Identical>; // the basic type itself
type MyDataPartial = MyDataProto<Optional>; // "partial" type or whatever you call it
type MyDataProxy = MyDataProto<GettableAndSettable>; // a proxy type over MyData
// ... etc
Not quite. {x: number?}
isn't assignable to {x?: number}
, because one
is guaranteed to exist, while the other isn't.
On Tue, Oct 11, 2016, 09:16 Aleksey Bykov notifications@github.com wrote:
might be worth mentioning that HKT could have been an answer to so called partial types (#4889 (comment) https://github.com/Microsoft/TypeScript/issues/4889#issuecomment-247721155 ):
type MyDataProto<K<~>> = {
one: K
— 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-252913109, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBNFYFfiW01MT99xv7UE2skQ3qiPMks5qy4wRgaJpZM4C99VY .
@isiahmeadows you are right, at the moment there is no way/syntax to make a property truely optional based solely on its type, and thats a shame
Yet another one: it would be good if property can be made readonly
. Seems some kind of macros feature required.
Just throwing this out there... I prefer the *
syntax over the ~
syntax. Something about ~ just seems so far out of the way from a keyboard layout perspective. Also, I'm not sure why, but I think * seems a bit more readable/distinguishable with all the angle brackets that are in the mix. Not to mention, people familiar with other languages like Haskell might immediately associate the syntax to HKT. Seems a bit more natural.
I'd have to agree with the *
syntax. First, it is more distinguishable,
and second, it better represents an "any type works" type.
Isiah Meadows me@isiahmeadows.com
On Sun, Nov 6, 2016 at 12:10 AM, Landon Poch notifications@github.com wrote:
Just throwing this out there... I prefer the * syntax over the ~ syntax. Something about ~ just seems so far out of the way from a keyboard layout perspective. Also, I'm not sure why, but I think * seems a bit more readable/distinguishable with all the angle brackets that are in the mix. Not to mention, people familiar with other languages like Haskell might immediately associate the syntax to HKT. Seems a bit more natural.
— 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-258659277, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBHQ4SYeIiptB8lhxEAJGOYaxwCkiks5q7VMvgaJpZM4C99VY .
Milestone: community
? What is current state of this issue/feature?
@whitecolor the status is DIY (do it yourself)
The issue has Accepting PRs
label. this means that pull requests to implement this feature are welcomed. See https://github.com/Microsoft/TypeScript/wiki/FAQ#what-do-the-labels-on-these-issues-mean for more details.
Also please see https://github.com/Microsoft/TypeScript/issues/1213#issuecomment-96854288
Ok, I see the labels, just have doubts that if non-TS team is capable of accomplishing it.
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 Do you have any luck with this?
@raveclassic - it turned to be more difficult than it seemed, however I still hope to move forward. Syntactically it is obvious, but the typechecking rules/phases are not as clear to me as I want -)
Lets try to revive my activity -)
Just tracking a progress, and the path of the idea development. I've considered three options how to implement this feature.
I've planned to enrich a TypeParameterDeclaration
with optional higherShape
property
export interface TypeParameterDeclaration extends Declaration {
kind: SyntaxKind.TypeParameter;
name: Identifier;
higherShape?: HigherShape // For Higher-Kinded Types <--- this one
constraint?: TypeNode;
// For error recovery purposes.
expression?: Expression;
}
and have considered three options how HigherShape
could beimplemented
type HigherShape = number
it corresponds to the following usage:
class Demo<Wrap<*>, WrapTwo<*,*>> { // 1 and 2
str: Wrap<string>;
num: Wrap<number>;
both: WrapTwo<number, string>;
}
in this simplest case, looks like that the number
type would be sufficient. Nevertheless, we should be able to determine an actual higherShape for every given type to be sure we can use it as a type argument for the specific shape requirements. And here we're facing a problem: the higher shape of the Demo
class itself is not expressible as a number. If it would, then it should be represented as 2
- since it has two type parameters,
and it would be possible to write
var x: Demo<Array, Demo>
and then battling with the deferred type-checking problem with property .both
. Thus the number
type is not sufficient (i believe);
in fact type Demo
has the following high order shape:
(* => *, (*,*) => *) => *
Then I've investigated the opposite, most full representation of the higher shapes, that would allow representing such shapes as aforementioned one, and even worse:
(* => (*,*)) => ((*,*) => *)
The data structure for this is straightforward, but it does not interplay well with the TypeScript type system. If we would allow such higher-order types then we will never know whether *
means the ground type, that could be used for the typing of values. Besides, I even did not manage to find an appropriate syntax how to express such a monstrous higher order constraints.
The main idea - type expression (even with actual type arguments) always results in a ground type - that can be used to type a variable. On the other hand, each type parameter can have its own detailed type parameters in the same format that is used elsewhere.
This was my final decision that I would try to advocate.
type HigherShape = NodeArray<TypeParameterDeclaration>;
example:
class A {x: number}
class A2 extends A { y: number }
class Z<T> { z: T; }
class SomeClass<T1<M extends A> extends Z<M>, T2<*,*<*>>, T3<* extends string>> {
var a: T1<A2>; // checked early
var b: T2<string, T1>; // second argument of T2 should be generic with one type parameter
var c: T3<"A"|"B">; // not very clever but it is checked
// ...
test() {
this.a.z.y = 123 // OK
// nothing meaningful can be done with this.b and this.c
}
}
Here I want to note, that M
is local for T1<M extends A> extends Z<M>
and exists in a deeper visibility scope than T1. Thus M
is not available in the SomeClass
body.
And *
means simply a fresh identifier (anonymous type) that never clash with anything (and could be implemented at later stage)
Thus the final signature of the TypeParameterDeclaration
export interface TypeParameterDeclaration extends Declaration {
kind: SyntaxKind.TypeParameter;
name: Identifier;
typeParameters?: NodeArray<TypeParameterDeclaration> // !!!
constraint?: TypeNode;
// For error recovery purposes.
expression?: Expression;
}
Want to hear any opinion of @DanielRosenwasser, @aleksey-bykov, @isiahmeadows and others -)
Sounds okay to me, but I know very little about the internal structure of TypeScript's code base.
Would like to add my voice to the choir requesting this and to cheer you on, Artazor! :)
This feature would be useful to me in my implementation of making Redux type-safe.
@michaeltontchev What issues are you having making Redux typesafe?
In case you're interested, I recently published https://github.com/bcherny/tdux and https://github.com/bcherny/typed-rx-emitter, which build on ideas from Redux and EventEmitter.
Now looks, need to rebase to the @rbuckton branch #13487 with default generic parameters. In other case we will conflict largely.
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.