-
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
generic types - syntax comparison #80
Comments
I've implemented generics in my type checker. I've added experimental support for rtype to the type checker in #89 The syntax in terms of usage is interface TDB {
_values: Object<String, String>,
} The syntax in terms of definition is (not fully supported in the checker yet...) but would look like interface TDB<T> {
_values: Object<String, T>,
get<T>(key: String) => T,
set<T>(key: String, value: T) => void,
keys() => Array<String>
} I've gone for re-using the Generic resolution is done on a per callsite basis, we infer what T should be and then type check both the callsite and implementation of the function |
@Raynos Very cool! Just one problem: We haven't decided on the syntax for generics in rtype. ); Personally, I prefer both the square bracket and spaced notations over angle brackets. Square brackets everywhere. We could make What do you think? |
@ericelliott I do not have strong opinions on syntax. interface TDB[T] {
_values: Object[String, T],
get[T](key: String) => T,
set[T](key: String, value: T) => void,
keys() => Array<String>
} Is fine with me. |
Let's try that and see how it goes. Can you implement it in your type checker and report back if it causes any problems? |
Generics is still a feature branch ( https://github.com/Raynos/jsig2/tree/implement-generics ) that needs more work, once its in master I'll try the square brackets syntax. |
Very cool. Keep me posted. =) |
Whoah, there’s a lot happening here over the last weeks! Sorry for being an outsider, it’s a very intensive time in my life right now. With my second son born last week, emailing and travelling all over in search of a remote job (not that easy in Europe), preparing to move house, preparing the final release of my current big work project, handing projects over before quitting the job, etc, etc, etc, etc. So yeah, sorry – I hope you understand. I’m hoping to jump back in next month. Anyway, just one quick thought that popped into my mind while reading this fantastic issue. @maiermic mentioned that the |
I actually have it already, so I’ll post the proposal. If non-ASCII characters are not an option though, feel free to delete the comment so it doesn’t clutter the discussion. Real Angle Brackets SyntaxType arguments are enclosed in real angle brackets and are separated by commas. This syntax is influenced by C++, Java, C# and TypeScript, but the ASCII brackets they use ( single-line without colonsFunctions
InterfaceGeneric type arguments of methods before method name
Generic type arguments of methods after method name
single-line with colonsFunctions
InterfaceGeneric type arguments of methods before method name
Generic type arguments of methods after method name
multiline with colonsFunctions
InterfaceGeneric type arguments of methods before method name
Generic type arguments of methods after method name
multiline with curly bracketsFunctions
InterfaceGeneric type arguments of methods before method name
Generic type arguments of methods after method name
|
I love how the real angle brackets look (really, they look quite nice.. much, much better than I still appreciate the effort you put into it. |
If only we could use these. @ericelliott is right, it's not realistic sadly. |
Fair enough, so I thought actually. We do use non-ASCII characters in our team, but we all use Fedora Linux which has fantastic keyboard layouts built in. I think Unicode in programming languages is going to flourish, but we still have to wait for efficient ways to input them. |
Getting people in the US to use any non-qwerty keyboard layout is like pulling teeth, sadly. ); |
It is a QWERTY layout. Just on steroids 😄. |
Generics are generally declared locally, inlined directly in the signature: this is what should be expected.
After reviewing your examples I gotta reject brackets Concerning greater/less Concerning whitespace, that's my first choice by elimination. It would probably need some restrictions, like enforcing lowercasing or some other distinction that would make sense—while keeping in mind #64. |
Let me rephrase: Getting people in the US to use any non-default keyboard layout is like pulling teeth, sadly. ); |
I like the workarounds suggested for square bracket ambiguity:
|
@ericelliott I support more expressivity for arrays:
But that would warrant its own issue. There's no conflict between this and that: both could coexist. |
[Number, Function, String]
[Object, ...String]
// so what we currently have
Number[]
// would become
[...Number] I like that. I also think it would be easier to express commonly needed things, like tuples: [Number, String] Somewhere else we talked about using a couple symbols from regex... specifically range(start: Number, end: Number) => Array[Number+] Which means that the returned array will contain 1 or more numbers. And I think range(start: Number, end: Number) => [Number+] We should also pick either one or more or zero or more as the default, so you could potentially shorten that to: range(start: Number, end: Number) => [Number] e.g., // range(start: Number, end: Number) => [Number]
const range = (start, end) => (
Array.from({ length: end - start + 1 }, (x, i) => i + start)
); I believe it should be fairly easy to combine all of this syntax with the square bracket notation. I also believe all of it should make it into the MVP. I'd really like to hear what @Raynos has to say about this. |
@ericelliott check my answer @ #96. Please continue the array types discussion there. I am totally against the usage of |
Does this mean you don't like |
I consider that it will be confused with arrays—even if, in that context, that wouldn't make sense. That's partly why I favor the whitespace option: it's not a separator that can be confused with something else and with proper restrictions it won't hurt the readability. |
@Mouvedia Consistency between the array syntax and the generic syntax is a feature, not an ambiguity. See this example from the array discussion: interface Collection: Object | Array
highPass(cutoff: Number, collection: Collection[...Number]) => numbers: [...Number] See also Generics and Collection Polymorphism from"Programming JavaScript Applications". |
As for your question...
Because the common case is that people will be dealing with The most important design goal is familiarity to JavaScript users:
|
It's not imperative in my opinion. I consider both matters distinct. Conflating #96 with this one won't help. This issue deserves more inputs: we shouldn't rush it. |
I disagree. Collection polymorphism is the most common use of generics in JavaScript. They appear in jQuery (most popular JS library ever), lodash & underscore (most depended on and 4th most depended on npm packages, respectively), collection generics exist in the JS spec itself, and nearly every JavaScript developer who's coded more than 5 minutes have dealt with generics in that context, whether they realize it or not. I'll reiterate: The most important design goal of rtype is familiarity by reusing common JavaScript idioms. In JS, the square bracket array notation is virtually synonymous with collections. As for letting other people chime in on it, we have literally spent months discussing it, and now we have this gorgeous issue listing all the different syntax proposals and all the pros and cons, and Bonus: an implementation is in the works. It's time to call a winner. |
@ericelliott if I didn't intervene it would have lasted for eons. I am trying to smooth things out here. Especially since it took a while to reach this point, one more week won't hurt. Shipping what's consensual and discussing what's not that's a sound strategy. |
OK, let's address the one point you did raise that has some merit. Arrays have automatically number-indexed keys. I think it's clear that when we specify a collection type with square brackets, like Now, for the edge case, Objects can have typed keys. For example, how do we specify that an object's keys must all be of type IMO, for that, we'll need some notation that allows us to address object keys, specifically, in the same way we can address object values with |
I created an proposal #99 |
@ericelliott If |
Using this function P() {
return Promise.resolve([
{ name: 'John' },
{ name: 'Eric' }
]);
} Let me make a best effort.
Correct? |
Spoiler: The
Array[...T] A short version without name [...T]
Promise[T] The actual type you like to pass to the formal type argument Promise[Array[...{name: String}]] or the short version Promise[[...{name: String}]] |
May I propose one more array notation? - |
If we introduce generic rest parameters (see #101), it would be ambiguous. For example: Is Furthermore, arrays of generic elements, e.g. I admit that Even though |
Perfect justification @maiermic |
Closing in favor of Haskell-style typeclasses where syntax looks like: |
Overview and comparison of generic type syntax proposals
This issue lists and compares several syntax proposals that have been proposed and initially discussed in issue #55. It should help to come to a decision which syntax proposal should be used.
Note: The following aspects have not been discussed yet and are not part of this overview/comparison:
Table of Contents
Examples
It might be easier for you to reason about syntax if you see some examples. Therefore, I will describe three function types
Transform
,Filter
andMap
and an interface typeCollection
in different syntaxes. I provide different writing styles of each syntax. For example, you might writeFilter
orMap
with use ofTransform
or using an inline type expression instead.Functions
In this section I describe the signature of the three generic function types. Further, I describe each type argument and give an example of an function implementation in JavaScript with its type as a comment.
Transform
Name: Transform
Type arguments: Input, Output
Signature:
Transform(Input): Output
Description: A function that takes a value of type
Input
and returns a value of typeOutput
.Example: The function
len
takes aString
as parameter and returns its length as typeNumber
. Thus it is of typeTransform
whereInput
isString
andOutput
isNumber
.Filter
Name: Filter
Type arguments: Element
Signature:
Filter(Element => Boolean, Element[]): Element[]
Description: Filter elements of type
Element
in an array. Take a function of typeElement => Boolean
(is equivalent toTransfrom(Element): Boolean
) as first parameter. Takes array of typeElement[]
as second parameter. Returns an array of typeElement[]
.Example: An generic implementation might look like this:
The generic type argument
Element
has to be inferred from the passed arguments of thefilter
call.Map
Name: Map
Type arguments: Input, Output
Signature:
Map(Input => Output, Input[]): Output[]
Description: Filter elements of type
Element
in an array. Take a function of typeElement => Boolean
(is equivalent toTransfrom(Element): Boolean
) as first parameter. Takes array of typeElement[]
as second parameter. Returns an array of typeElement[]
.Example: Map elements of type
Input
to typeOutput
. Take a function of typeInput => Output
(is equivalent toTransfrom(Input): Output
) as first parameter. Takes array of typeInput[]
as second parameter. Returns an array of typeOutput[]
.The generic type arguments
Input
andOutput
have to be inferred from the passed arguments of themap
call.Interface
To keep it simple, I describe an interface
Collection
that is satisfied by most common data structure objects (for example Array).Name: Collection
Type arguments: Element
Signature:
Description: The interface
Collection
takes one type argumentElement
that is visible in the interface body. All elements in the data structure are of this typeElement
. ACollection
has methodsfilter
andmap
. They do the same as the functionsfilter
andmap
I described in the chapter above, but since they are methods, the data structure is (implicitly) passed asthis
argument. Hence, the last argument of the corresponding function is dropped in the method.Example: Let's see some JavaScript code with
Array
asCollection
to see the difference between the functionfilter
and the methodfilter
and the difference between the function
map
and the methodmap
:Syntax proposals
Listed in the subchapters below are type declarations of the example types
Transform
,Filter
,Map
andCollection
written in different variants of each syntax proposal. In all syntax proposals the name of the type is followed by its type arguments followed by its type definition. The difference is how type arguments are written. Are they enclosed in angle or square brackets, parentheses or are they only separated by whitespace. The different variants of a syntax vary in several aspects that might influence readability:Input => Output
vs.(Input) => Output
vs.(Input => Output)
used in a specific contextmap
)Angle Brackets Syntax
Type arguments are enclosed in angle brackets and are separated by commas. This syntax is influenced by C++, Java, C# and TypeScript.
single-line without colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
single-line with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with curly brackets
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
Square Brackets Syntax
Type arguments are enclosed in square brackets and are separated by commas. This syntax is influenced by Scala.
single-line without colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
single-line with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with curly brackets
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
Parentheses Syntax
Type arguments are enclosed in parentheses and are separated by commas. This syntax is influenced by JavaScript function notation. Since a generic type is a type constructor/function that takes types as arguments and returns a concrete type.
single-line without colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
single-line with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with curly brackets
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
Whitespace Syntax
Type arguments are written without backets and are separated by whitespace. This syntax is influenced by Elm and Haskell.
single-line without colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
single-line with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with colons
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
multiline with curly brackets
Functions
Interface
Generic type arguments of methods before method name
Generic type arguments of methods after method name
Comparison of syntax proposals
It is hard to compare syntax impersonal and objectively. Opinions might differ on readability and the question if familiarity of a syntax is an advantage or an disadvantage. Thus, I mainly collect opinions as quotations. I will add opinions that arise in further discussion. Please tell me if any opinion or aspect is missing or if something is not quite appropriate.
Angle Brackets Syntax
Similar to C++ templates and Java, C# and TypeScript generics.
Advantages
Disadvatages
< (Unicode Character 'LESS-THAN SIGN') and > (Unicode Character 'GREATER-THAN SIGN') used as brackets are harder to read, since they are no real brackets. The real angle brackets are 〈 (Unicode Character 'LEFT ANGLE BRACKET') and 〉 (Unicode Character 'RIGHT ANGLE BRACKET').
parsing issues
Square Brackets Syntax
Similar to Scala generics
Advantages
[]
(in rtype) a generic type is describedDisadvatages
[]
in JavaScript an object property or array element is accessed.might be resolved by writting
[Number]
instead ofNumber[]
Parentheses Syntax
Similar to a regular JavaScript function.
Advantages
type constructor are functions
Disadvatages
Whitespace Syntax
Similar to Elm and Haskell.
Advantages
Disadvatages
whitespace syntax introduces currying. Most JavaScript developers are not familiar with this concept of function notation.
The text was updated successfully, but these errors were encountered: