Ideas on type hinting and named parameters
I like that syntax.
I wonder about the runtime semantics of it, though. (I know TypeHint is a TODO, but…) Is there any implication at runtime of a function that returns a String? Is this:
var point = {
x Number,
y Number
};
… the same as this?
var point = {
x: Number(),
y: Number()
};
In which case we would need rules for constructors to always have default parameters, right?
If those annotations are all only for a non-runtime type check, I feel
strange now about having type analysis only for newly defined symbols.
Since this is meant as some kind of pre-runtime assert(symbol instanceof Type)
, we might want to check:
- field types (such as
foo {bar Number}
) - parameter types (such as
Array[Number]
orMap[String, Array[String]]
) - function types (such as
function(String) Number
) - union types (such as
RegExp | null
) - generics, possibly with extends / superclass (such as
function(items Array[T extends Entity], unmap function(Number) T)
)?
Could we typedef types? Could we define covariance and contravariance rules in new types with generics?
On a side-note, we can actually track the full list of methods used for each parameter, and check that each function call respects them. Maybe type inference has been researched enough that we can now use better tools that need no type hints.
quick personal thoughts on this matter, leaving the named arguments part beside (as it is looks kinda redundant/unnecessary effort that could wait)
We've been talking about types for long time in here and the good old
direction was binary data in the form of StructType
const Point2D = new StructType({x:uint32, y:uint32});
let xy = new Point2D({
x: 0,
y: 0
});
Not only this option looks and feel more consistent with typed options we
have already in core such Int32Array
and others, but it will be way
easier to gracefully migrate to such new pattern and fully, or partially,
polyfill for older engines.
Back to typed Array, I think this:
var xyz = new Int32Array([1, 2, 3]);
is better than:
var xyz = [
int32 = 1,
int32 = 2,
int32 = 3
];
or whatever repeated operation we could have per each xyz
like variable
of "that kind", so whatever would eventually work as StructType might be in
my opinion more suitable.
- you recognize upfront what kind of duck you are working with
- you define such type once instead of each time, reducing errors/typos
- when you pass data around you can specify as example just
Point2D
instead ofArray<T Int32>
and friends that are imo not so nice to have in
a scripting language 4. we have already tools capable of bringing in types in similar fashion you proposed ... how about we do something better than some syntax tools friendly, instead of some syntax optimized for developers?
Moreover about classes, you have defined properties that AFAIK are not allowed by current specs. ES6 classes accepts only methods and/or getters and setters so I'm not sure properties should be discussed together with types, maybe worth waiting for better understanding on how properties will be?
That being said, I see why you used space instead of colon, and for literal objects that indeed looks like a better approach.
Best
On Jun 9, 2015, at 1:21 AM, Thaddee Tyl <thaddee.tyl at gmail.com> wrote:
I like that syntax.
I wonder about the runtime semantics of it, though. (I know TypeHint is a TODO, but…) Is there any implication at runtime of a function that returns a String? Is this:
var point = { x Number, y Number };
… the same as this?
var point = { x: Number(), y: Number() };
In which case we would need rules for constructors to always have default parameters, right?
This brings up a good point. I’ll open an issue for it.
Likely built-in types such as String, Number, and Boolean would have special meaning, assuming they are not shadowed. Their default values would be “”, 0, and false. Other types would be undefined by default.
Alternatively it could always be undefined, but that can be problematic, especially with Number.
If those annotations are all only for a non-runtime type check, I feel strange now about having type analysis only for newly defined symbols. Since this is meant as some kind of pre-runtime
assert(symbol instanceof Type)
, we might want to check:
- field types (such as
foo {bar Number}
)- parameter types (such as
Array[Number]
orMap[String, Array[String]]
)- function types (such as
function(String) Number
)- union types (such as
RegExp | null
)- generics, possibly with extends / superclass (such as
function(items Array[T extends Entity], unmap function(Number) T)
)?
Types should probably be checked at runtime. However, having the flexibility to catch errors with a linter before running the code would be extremely valuable. So I’m not sure I would actually take it as far as Array[Number] because you can only enforce that at runtime given that an Array can contain anything.
Adding typed arrays in the language may be more beneficial. For example if you had NumberArray you could enforce the types in the array before they are added:
push(...values Number) {...}
Luke
On Jun 9, 2015, at 1:57 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com<mailto:andrea.giammarchi at gmail.com>> wrote:
quick personal thoughts on this matter, leaving the named arguments part beside (as it is looks kinda redundant/unnecessary effort that could wait)
We've been talking about types for long time in here and the good old direction was binary data in the form of StructType
const Point2D = new StructType({x:uint32, y:uint32});
let xy = new Point2D({
x: 0,
y: 0
});
Not only this option looks and feel more consistent with typed options we have already in core such Int32Array
and others, but it will be way easier to gracefully migrate to such new pattern and fully, or partially, polyfill for older engines.
Back to typed Array, I think this:
var xyz = new Int32Array([1, 2, 3]);
is better than:
var xyz = [
int32 = 1,
int32 = 2,
int32 = 3
];
or whatever repeated operation we could have per each xyz
like variable of "that kind", so whatever would eventually work as StructType might be in my opinion more suitable.
- you recognize upfront what kind of duck you are working with
- you define such type once instead of each time, reducing errors/typos
- when you pass data around you can specify as example just
Point2D
instead ofArray<T Int32>
and friends that are imo not so nice to have in a scripting language - we have already tools capable of bringing in types in similar fashion you proposed ... how about we do something better than some syntax tools friendly, instead of some syntax optimized for developers?
You are talking about new primitive types, right? I’m focusing mostly on class types and inheritance (instanceof checking). String, Number, and Boolean are special cases though.
uint32
is not a reserved word as far as I know. Do we care that they aren’t? If not, I suppose string
could be part of that list of primitive type symbols, right? In which case name string
could be used instead of name String
.
Is StructType passed by value or by reference? If by reference, wouldn’t that be a bit misleading? Structs are usually supposed to be cheaper than objects because of how / where they are allocated.
For structs I was thinking about something along the lines of:
struct Point2D {
x Number
y Number
};
let xy = Point2D(x: 5, y: 10);
…which goes nicely with named arguments :).
Moreover about classes, you have defined properties that AFAIK are not allowed by current specs. ES6 classes accepts only methods and/or getters and setters so I'm not sure properties should be discussed together with types, maybe worth waiting for better understanding on how properties will be?
Class properties are listed in stage 0 here: tc39/ecma262/blob/master/stage0.md, gist.github.com/jeffmo/054df782c05639da2adb
It currently uses =
to define properties. And there is some debate whether or not properties should be initialized in the constructor or be on the prototype.
In the type-hinting repo I show both =
and :
, although I would prefer :
to keep consistent with plain objects.
Thinking about how other proposals could overlap and trying to avoid any unnecessary conflict can be important.
That being said, I see why you used space instead of colon, and for literal objects that indeed looks like a better approach.
Thank you your kind words. I’ll have to admit I was expecting more backlash. The day is still young though :)
Luke
Why can't we use proxy or a new global object call StrictType, or a new property call definePropertiesType on the global Object
Than we could use the syntax style of the language :
Object.definePropertiesType(object,{ age:{type:Number}, name:{type:String}, height:{type:Float} });
All of these additional feature will make the language hard to learn. Adding on these new syntax in hope to please other comming from other programming language. No matter what it'll never be 100% familiar to others that's come from different languages. It becoming bulky in my opinion.... Pretty soon will be in the same boat as w3c.
...it's almost like "who every can make the most specific, understandable, detail proposal will get the feature included in the language."
JS4Lf
On Tue, Jun 9, 2015 at 8:30 AM, Luke Scott <luke at webconnex.com> wrote: [...]
It currently uses
=
to define properties. And there is some debate whether or not properties should be initialized in the constructor or be on the prototype.
There is no debate about whether per-instance state (of whatever form) can be initialized in the constructor. Often, this needs to be initialized to some value dependent on the values of the constructor parameters. Given that we have no choice about supporting initialization in constructors, the debate is only about whether we should also bother to support initialization in the class body outside the constructor. IMO, no. Why add a redundant and less expressive mechanism?
As for properties on the prototype, these are rarely enough motivated that doing it imperatively after class initialization seems fine. I don't recall anyone strongly advocating that we need syntactic support for properties on the prototype, though perhaps I missed it.
Mark, properties on the prototype are in TypeScript, and probably every other OOP based language. Defaults can be easily defined as such, i.e.
class Person { name = 'anonymous'; age = 0; }
Not only, defaults could be optimized upfront by parsers without needing to infer or do extra analysis on the constructor.
Defaults also as static property/state is quite common.
class Admin { PRIVILEGES = 'all'; }
Anyway, I'm curious to know why do you think getters and setters are OK and properties are not. I don't see any technical difference, specially considering get/set for lazy property computation/assignment through the prototype getter anyway.
JS4lf ... you hit an already opened door about strict descriptors, already proposed years ago ;-)
WebReflection/define-strict-properties#define-strict-properties
Best
On Tue, Jun 9, 2015 at 9:13 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Mark, properties on the prototype are in TypeScript, and probably every other OOP based language. Defaults can be easily defined as such, i.e.
class Person { name = 'anonymous'; age = 0; }
At www.typescriptlang.org/Playground, putting this code into the
left pane causes the right pane to show
var Person = (function () { function Person() { this.name = 'anonymous'; this.age = 0; } return Person; })();
which clearly indicates that these are not properties on the prototype. They are per-instance, but merely written with a confusing syntax that misled you into believing that they were properties on the prototype.
Anyway, I'm curious to know why do you think getters and setters are OK and properties are not. I don't see any technical difference, specially considering get/set for lazy property computation/assignment through the prototype getter anyway.
Syntax for putting properties on the prototype was long-ago rejected because of footgun potential. Users will naturally tend to think of them as per-instance, instead of shared, which leads to bugs.
Syntax for putting properties on the prototype was long-ago rejected because of footgun potential.
Correction: data properties. : )
TypeScript was just an example, but being class a different Syntax beast than just prototype (right?) why would that be bad? It will be indeed not-unexpected as Kevin mentioned later on.
Using classes thinking prototypes feel something developers won't do (specially new comers) so TypeScript behavior looks OKish to me, but in ES7 doesn't have to be like that, it can be actually part of the prototype if you prefer, and do a copy of non primitives ( or leave the Kevin's mentioned footgun in, I don't mind, it never hurt me personally :-) )
On Jun 9, 2015, at 9:23 AM, Kevin Smith <zenparsing at gmail.com<mailto:zenparsing at gmail.com>> wrote:
Anyway, I'm curious to know why do you think getters and setters are OK and properties are not. I don't see any technical difference, specially considering get/set for lazy property computation/assignment through the prototype getter anyway.
Syntax for putting properties on the prototype was long-ago rejected because of footgun potential. Users will naturally tend to think of them as per-instance, instead of shared, which leads to bugs.
Another way of eliminating the footgun potential is not allowing non-primitive defaults on properties. Any non-primitive properties would always default to undefined, forcing you to later initialize it yourself in the constructor. Defining the property without a default would just be a way to defining what the type is supposed to be. As far as I can tell this is what other languages do - non-primitive types are not allowed as property defaults. That makes sense to me.
class Person {
name String: “John”
items Array
constructor() {
this.items = [“food"];
}
}
Also static properties are on proto, so it seems a bit strange that instance properties would not also be on .prototype. Somewhat of a consistency issue I suppose.
(My fault for dragging this a bit off topic. TMI in my description of class properties…)
Luke
Also static properties are on proto, so it seems a bit strange that instance properties would not also be on .prototype. Somewhat of a consistency issue I suppose.
Methods declared as "static" in ES6 are defined on the constructor itself. Apparently, "static" data properties in TS are defined on the constructor as well.
how about introducing a well known concept in JS as own
?
class Person {
static name String: "A person name"
own name String: "anonymous"
own items Array: []
constructor(name) {
if (name) this.name = name;
}
}
var anon = new Person,
me = new Person('AG');
anon.items; // []
me.items; // []
me.items === anon.items; // false
Person.name; // "A person name"
anon.name; // "anonymous"
me.name; // AG"
thoughts?
how about introducing a well known concept in JS as
own
?
class Person { static name String: "A person name" own name String: "anonymous" own items Array: []
Let's take a step back and ask: what's the motivation for having these property declarations and initializers outside of the constructor? As Mark pointed out, it's a syntactic feature that is strictly less expressive than the current solution of putting property assignments in the constructor body.
Instance property initializer syntax is really geared toward type systems, where you are expressing type constraints (either structural or nominal) on objects created by the given constructor.
I'm wary of adding syntax whose primary motivation is to express type constraints, when we haven't even defined (or proposed) what typing in JS means.
Inline replies
On Tue, Jun 9, 2015 at 9:01 PM, Kevin Smith <zenparsing at gmail.com> wrote:
Let's take a step back and ask: what's the motivation for having these property declarations and initializers outside of the constructor?
The same motivation of having static
properties definition in it ... it
was just possible to do the same directly through the class name without
needing a new syntax, right?
As Mark pointed out, it's a syntactic feature that is strictly less expressive than the current solution of putting property assignments in the constructor body.
You define a class and basic/default properties per each instance. That's what people coming from other languages expect before even realizing classes methods are not own properties or classes getters and setters are not own descriptors.
It's a common, well known, pattern in the "class world" out there, that's pretty much it. Since class in JS is sugar, why limiting such sugar?
Instance property initializer syntax is really geared toward type systems, where you are expressing type constraints (either structural or nominal) on objects created by the given constructor.
I'm wary of adding syntax whose primary motivation is to express type constraints, when we haven't even defined (or proposed) what typing in JS means.
Agreed we are mixing up two different things that don't necessarily need each other to work.
On 6/9/15 4:01 PM, Kevin Smith wrote:
how about introducing a well known concept in JS as `own` ? ```js class Person { static name String: "A person name" own name String: "anonymous" own items Array: []
Let's take a step back and ask: what's the motivation for having these property declarations and initializers outside of the constructor? As Mark pointed out, it's a syntactic feature that is strictly less expressive than the current solution of putting property assignments in the constructor body.
Instance property initializer syntax is really geared toward type systems, where you are expressing type constraints (either structural or nominal) on objects created by the given constructor.
Hmmm, this is really not the primary motivation of the proposal even if it does dovetail nicely with what type systems need. The proposal grew out of needs from real JS frameworks like React and Ember (see the proposal slides again for references to how these frameworks wish to use this).
Instead, the purpose of initializers outside of the constructor are to increase expressivity in a different sense than what I think you meant about constructor initialization: It allows initialization that isn't based on logic in the constructor to be visually and clearly separate from logic that is. It is strictly less expressive for constructor-injected state patterns, but it is strictly more expressive for other patterns of initialization.
I will accept any syntax that can pass muster (the weird hybrid between colon and C style I came up with is never getting in :P) btw, I do think having generics/template syntax is useful (at one point I was planning on implementing templates in my transpiler, which is why I support the syntax). At least I find it helpful.
However, it did require Yet Another Cover Grammar, so I dunno if it's appropriate here.
Best, Joe
On Wed, Jun 10, 2015 at 8:59 AM, Jeff Morrison <lbljeffmo at gmail.com> wrote:
Instead, the purpose of initializers outside of the constructor are to increase expressivity in a different sense than what I think you meant about constructor initialization: It allows initialization that isn't based on logic in the constructor to be visually and clearly separate from logic that is.
So let's put a visually distinct syntax in the constructor. The constructor is no longer as-if a function in several ways already. In for a penny, in for a pound.
It is strictly less expressive for constructor-injected state patterns,
In case I was not clear, I am not for using the current assignment-in-constructor to pun initializing in constructor, the way Java does. Initialization and assignment should be distinct. Const private instance fields must be initialized but must not be assigned to.
but it is strictly more expressive for other patterns of initialization.
What do you have in mind?
On 6/10/15 1:46 PM, Mark Miller wrote:
On Wed, Jun 10, 2015 at 8:59 AM, Jeff Morrison <lbljeffmo at gmail.com <mailto:lbljeffmo at gmail.com>> wrote:
Instead, the purpose of initializers outside of the constructor are to increase expressivity in a different sense than what I think you meant about constructor initialization: It allows initialization that isn't based on logic in the constructor to be visually and clearly separate from logic that is.
So let's put a visually distinct syntax in the constructor. The constructor is no longer as-if a function in several ways already. In for a penny, in for a pound.
This might work too, but it forgoes the benefits of not having to override a parent constructor just to initialize child-class-specific properties. Writing the initializers at the class's toplevel also seemed more familiar (considering cow paths in JS as well as other langs) to provide this syntax outside of the constructor, though.
Do you have a proposal for something that would be clearly declarative and clearly set the initialization apart from any logic in the constructor?
It is strictly less expressive for constructor-injected state patterns,
In case I was not clear, I am not for using the current assignment-in-constructor to pun initializing in constructor, the way Java does. Initialization and assignment should be distinct. Const private instance fields must be initialized but must not be assigned to.
I have a fuzzy idea of what I think you mean, but mind clarifying with a code example of what you're not interested in vs what you are?
but it is strictly more expressive for other patterns of initialization.
What do you have in mind?
Yehuda had some examples for Ember somewhere, but one place where React would like this is shown here: facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#es7-property-initializers
In this case, React is using classes as a declarative form to describe a
component which doesn't take all of it's parameters via constructor
injection. The constructor is handled by the base class, and it would be
stricly boilerplate to have to write constructor(props, context) { super(props, context); this.state = [...] }
each time one writes a
child class.
Instead, the component writer describes the initial state
property
with a property initializer on the class and is able to avoid the class
constructor altogether. The framework coordinates instantiation of the
component class without having to also plumb non-framework-provided data
through to the instantiation as well (and the child gets to avoid the
boilerplate and potential user-error hazards of overriding the
constructor + the ceremony of calling super(), etc).
Another simple example is one where a class defines the structure of some objects that should acquire some GUID property without imposing the need to write out a full parent constructor override:
var guidCounter = 1;
function getGuid() { return guidCounter++; }
class MyThing { id = getGuid(); }
new MyThing().id; // 1
new MyThing().id; // 2
Hello All,
I wanted to share some ideas with you for type hinting:
lukescott/es-type-hinting
I’m also putting this together for named parameters:
lukescott/es-named-arguments
The two are somewhat related. The type hinting doc uses white-space instead of a colon. And in the named-arguments doc a colon is used for named arguments.
I realize that there may be some strong opinions on a colon vs white-space. Using white-space instead is an attempt to be compatible with existing ES syntax (POJO uses colon already), while allowing for other new features, such as named parameters.
Looking for feedback and any interest on any of the above.
Luke