I wanted to propose an alternate approach to defining types within ES. I don't
think is actually a realistic proposal for changing ES4 or ES3.1, but more of
as an interesting alternate language mechanism for utilizing the ES4 VM with
ES3 syntax that I have been interested in exploring for a while now, and
wanting to write out in case anyone was interested in the idea. The basic
premise is to define Classes, records, and parameter types with a schema object
that can easily defined with ES3/JSON, rather than using ES4 type annotation
syntax. Type information would be defined with a schema that would act like an
interface, and this could be used in combination with local type inference for
local variables. My examples and proposal are based on using JSON schema[1] for
defining types; my involvement in JSON Schema might preclude objectivity to
some degree, but I do think it is the most reasonable ES3 compatible definition
for types. Expressed in ES3, it is a simple object structure, with a high
degree of correspondence to ES4 types. However, though the proposal is more
about using a external type definition/schema even if an alternate format (like
Cerny [2]) might be better.
This approach could have application even without actual native support.
Development could potentially use this approach to have a codebase that could
easily be adapted (possibly through automated transformation, or type omission)
to various target ES VMs. Also this approach has nothing to do with "classes as
sugar" proposal [3]; it could actually be considered the inverse. Rather than
attempting ES4 class syntax with ES3(.1) semantics, it is about using ES3
syntax to drive ES4 class/record semantics. This proposal is also not a
complete in it's ability to define ES4 semantics with ES3 syntax. There are
definitely plenty of typing forms that this approach can't define, but I would
suggest that the majority of type definitions can be defined or inferred with
this approach.
Motivation
1. The first motivation is that the code could run in ES3 or ES4 VMs. Of course
the ES3 VM doesn't have native typing support, so it would either have to go
without typing, or do code translation.
2. Separation of behavior and typing/structure concerns.
a. One can look at the schema for the strucuture and interface of the a
given Class or data structure separately from looking at the code for a nice
clean, minimal (no annotations) look at the implementation and behavior.
b. One can easily include or exclude typing information for code. Certainly
the biggest benefits of the ES4 type system are at development time, with the
integrity, correctness, organization, and code completion assistance. Once in
production, the majority of web applications spend far more time downloading
JavaScript than they do executing it (especially after DOM deductions).
Applications may be becoming more sophisticated, but sophisticated apps still
increase download time and with ES VMs quickly improving, plus hardware
improvements increasing at a faster rate than bandwidth widens, I think
download times will continue to dominant execution times for quite a while.
Consequently, it seems most likely that performance-savvy developers will
mostly like strip the majority of type annotations off of code during
compression (perhaps retaining some annotations for performance sensitive
hot-spots if VMs prove to benefit from typing information, or retaining it in
situations where correct execution depends on type errors, rather than only
signalling incorrect execution).
c. Existing applications could be retrofitted with type information without
modifying the original code (or minimally modifying). Since type information is
provided through a separate schema, the original code can be kept intact.
3. Class reflection has an obvious reification based on the schema. Using JSON
Schema, type information is reflected as the schema object itself.
4. Language neutral interfaces - JSON Schema has been designed to be a language
agnostic (I realize that might be a little wistful, JSON Schema bears influence
from the primitives of JSON and ES, but JSON has proven pretty cross-language
capable). One could actually use a JSON Schema defined structure or interface
with implementations in various languages. This can even facilitate interaction
through non-language-specific means (like RPCs), and JSON Schema is even being
used for such with Simple Method Description with Dojo [4].
Example:
First we define the schema interface for our WebMail class, this could actually
be in a separate file than the class impl, (note that we are doing it in pure
JSON, except for a few comments, lot's of sugar could easily be applied):
Schema.WebMail = {
description:"Webmail Client",
type:"object",
methods:{ // these are the methods for the Webmail class
send: {parameters:[{$ref:"Msg"}]}, // refers to the Msg type defined below
handleMessage: {returns:{$ref:"Msg"}},
fetch: {returns:{$ref:"Msg"}},
get: {parameters:[{type:"integer",name:"id"}], returns:{$ref:"Msg"}}
},
properties:{
database:{
type:"array",
items:{
id:"Msg",
description: "Email message",
properties:{
to:{
type:"array",
items:{$ref:"Addr"} // use the Addr type
},
from:{
id:"Addr",
description: "Email address",
properties:{
at:[{type:"string"},{type:"string"}],
name:{type:"string"}
}
},
subject:{type:"string"},
body:{type:"string"},
read:{type:"boolean"},
messageId:{type:"integer",unique:true,optional:true}
}
}
}
}
};
Now we define the actual class impl, once again this could be in a separate
file than the schema:
WebMail = Class(Schema.Webmail,{
send:function (msg) { // msg is determined to be a Msg from the schema
msg.messageId = sendToServer(JSON.encode(msg));
// this is typed check, msg and this.database[n] are both known to be
Msgs from the schema
this.database[msg.messageId] = msg;
},
get: function(n) {
if (n in this.database)
// The value of database[n] can be determined from the schema,
// and it properly matches the return type of get
return this.database[n];
// handleMessage also has a return type that matches get, so it is safe
return this.handleMessage(n);
},
showMessage: function(n){
//local type inference can determine the type of msg from the return type
of get from the schema:
var msg = this.get(n);
msg.read = 40; // this could statically be determined to be a type
conversion error
msg.read = true; // this is a correct assignment
messagePane.innerHTML = msg.body; // can statically be determined to be a
string (conversion check not necessary)
},
handleMessage: function(n) {
// this line is tricky, should we auto cast like "wrap"?
var msg = JSON.parse(fetchFromServer(n));
if (msg.result == "no data")
return null;
// if we auto-wrap, the casting can be done on this
// line or the first line depending on local type inferencing
return database[msg.messageId] = msg;
},
database:[]
}
);
This approach creates the class implementation explicitly using the schema,
with the schema reference being in the class constructor. One could also use a
mechanism where the class loader would use a name-based convention for loading
the schemas and connecting them to class constructors. This could be used to
facilitate applying schemas/typing to completely pure unaltered ES3 code/files.
This is example is taken from a comparison of ES4 and ES3 (with typing checks)
code size [3]. This approach provides an implementation that is more compact
than either. Of course, this could be seen as just moving the extra text to
another place, the schema, but this is intentional. The schema acts more as
documentation, a contract for implementations, generous with descriptions and
various other informative (and probably non-normative to the type checker)
annotations. Implementations can benefit from minimal code side, and
documentation/contract information can be extensively detailed and informative.
Note that it is also important that the schema and implementation be
static/declarative (like JSON, no dynamic expressions) in order to be properly
statically analyzed. It would be quite easy to create aliases/consts for parts
of a schema if you didn't expect it to be shared with other languages/systems.
There would certainly be some interesting issues in exactly how to map a schema
to ES4 semantics. I think it would make sense for method-less schemas to
generally map to structural types instead of interfaces. Converting plain
objects (from functions that return untyped objects) to typed variables and
slots would probably require creating a new object with original properties
mixed in (like the old wrap proxy). I am sure there would be other issues to
solve, and some ES4 type semantics that simply couldn't be described.
Anyway, once again this is not so much a proposal for changing ES, but an
exploration of an alternate approach to typing and defining type constraints
for various ES VMs. Perhaps this discussion might be better done on LtU, not
sure...
[1] http://groups.google.com/group/json-schema /
http://groups.google.com/group/json-schema/web/json-schema-proposal---second-draft
[2] http://www.cerny-online.com/cerny.js/documentation/guides/schema
[3] https://mail.mozilla.org/pipermail/es4-discuss/2008-March/002496.html
[4] http://www.sitepen.com/blog/2008/03/19/pluggable-web-services-with-smd/ /
http://groups.google.com/group/json-schema/web/service-mapping-description-proposal
Thanks,
Kris
_______________________________________________
Es4-discuss mailing list
Es4-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es4-discuss