On 14.09.2010 20:19, Mark S. Miller wrote:
On Tue, Sep 14, 2010 at 6:37 AM, Dmitry A. Soshnikov
<[email protected] <mailto:[email protected]>> wrote:
On 14.09.2010 1:03, Mark S. Miller wrote:
On Mon, Sep 13, 2010 at 2:02 PM, Mark S. Miller
<[email protected] <mailto:[email protected]>> wrote:
On Mon, Sep 13, 2010 at 1:18 PM, Dmitry A. Soshnikov
<[email protected]
<mailto:[email protected]>> wrote:
I didn't finished a detailed reading yet, but from the
brief scanning, syntactically, I think /=>/ and /trait
class/ are not needed.
* I used "trait class" rather than "trait" for two reasons:
1) Syntactic ambiguity fear. "trait" is not one of the
identifiers reserved by ES5 or ES5/strict, and so I am not
proposing that it be an ES-Harmony keyword.
This keyword would look interesting though. Traits/mixins concept
is a weighty thing and if to reproduce it, then in a good way --
easy to use/write and syntactically minimalistic. By the way,
JFTR, PHP also now providing traits
(http://wiki.php.net/rfc/horizontalreuse) with using this /trait
TraitName {}/ syntax; Scala too has such syntax -- i.e. without
(trait class, class trait) referencing to parser issues.
JFTR?
Sorry -- "just for the record", as already Dave has mentioned.
PHP and Scala can tolerate a much higher migration tax than JavaScript
because of the differences in how they're deployed. I hesitate to
propose any new keywords beyond those reserved by ES5/strict. All the
proposals on
<http://wiki.ecmascript.org/doku.php?id=harmony:proposals> and the
current traits strawman are upwards compatible from ES5/strict,
allowing a conforming implementation to provide them with no further
opt-in than "use strict". Not all the strawman have this virtue, some
with good reason. But to ease incremental prototyping and adoption, we
should preserve this virtue when we can.
Yeah, yeah, I know, "backward compats". Forget that they are rule.
2) More importantly, the object binds to the name it
declares is not a trait but a function for making traits.
Oops. Should be "the object bound to ..."
* I introduced "=>" to disambiguate whether the "{" following
the (trait) class head begins a block or a literal.
Yeah, but maybe somehow it (ambiguous) may be avoided.
Somehow sure. Suggestions appreciated.
/trait class EnumerableTrait() => {/
instead better:
trait Enumerable {
Enumerable is a function for making traits. Your syntax
suggests that Enumerable is itself a trait.
Hm, "function for making trait", "trait itself". Too many things.
It may bring complexity. Yes, i thought (and see in other
languages, the same Ruby) that Enumerable is a trait itself, and
not a special (non-needed to me) function. A user needed just a
trait, not a function + trait.
Please check out <http://traitsjs.org/>. The notion of traits there
and in this strawman is the "stateful traits" approach that Tom first
explained at
<http://prog.vub.ac.be/Publications/2009/vub-prog-tr-09-04.pdf>. Until
I saw this paper, I had written traits off as a dead end.
It seems something interesting there, thanks, I'll take a look on the
paper (as well as on all examples on the site). However, I'm aware that
concept of a "trait" in contrast of a "mixin" has no state to avoid
conflicts.
Without the "=>", how do we know that the "{" begins a
literal rather than block? (See the second TraitBody production).
/class Interval(min, max) => {/
instead better:
class Interval(min, max) {
or even (with initialize method)
class Interval {
function initialize(min, max) {}
}
Superfluous symbols are not needed.
Initialize methods are superfluous as well. Also, they
introduce namespace confusion and re-initialization hazards.
Yep, but how then you propose to initialize an instance? I mean,
not a state, but the initial code. In the class body?
In the "Program" portion of the second TraitBody production. This can
declare new variables that are captured by the following TraitLiteral.
The lexical variables captured by each TraitLiteral is the state of
that trait. The union of the states of the traits that a class
composes together is the state of an instance.
With a state it's OK. But usually, an initialization process may include
internal methods calling (which of course also just create a state
properties, however, there is a difference -- conveniently to call three
methods, which will set 10 properties, or to define these 10 properties
manually in the body of a class).
And still, don't forget about the (vertical) inheritance,
i.e. class Foo may /extends/ Bar. If there will be no
(delegation based) inheritance, such classes won't be
needed, because they will be just casual
pattern-factories. In addition, mixins/traits, being a
horizontal inheritance (the augmentation of the vertical
code reuse) are also good to have as delegation based. I
think that you (and Tom) used statically augmented (a
method per instance) object not because you like it
ideologically, but because it's not possible to implement
it cross-browser in nowadays.
I'm not sure what you mean. Could you please expand and
clarify the question? Thanks.
I mentioned "classes-as-sugar" proposal, where a class cannot
inherit (making a vertical inheritance chain).
A -> B -> C
In addition, if any class may mixin several traits/mixins, we have
a horizontal inheritance:
A -> B -> C
| |
| |----------- m1 -> m3
|
|--m1 -> m2
i.e. at every link, first the whole chain of mixins is considered
and only after that we go to the next (vertical) link. Sure, the
normalized inheritance chain for resolution is linear:
A -> m1 -> m2 -> B -> m1 -> m3 -> C
I.e. an instance always inherits methods/properties from mixins or
classes, but not has own (as in your implementation of classes and
traits). The thing is not only in optimizing VM, but in dynamics
of a language. I.e. if we modify somehow methods in the class or
trait, all instances via delegation will have a new version.
That what Ruby uses:
module MyTrait
def my_method
print(@items)
end
end
class MyCollection
# mixin MyTrait to the class
include MyTrait
# and also some built-in mixin
include Enumerable
def initialize(data)
@data = data
end
# method required by the
# Enumerable trait/mixin
def each
@data.each { |i| yield(i) }
end
end
# an intance
things = MyCollection.new(['x','yz','defgh','ij','klmno'])
thigns.my_method # from MyTrait
# available from Enumerable trait
print(things.min) #=> "defgh"
print(things.max) #=> "yz"
# aka Array#map in JS
print(things.collect { |i| i.upcase }) #=> ["X", "YZ", "DEFGH",
"IJ", "KLMNO"]
# modules/traits are open
module Enumerable
def new_method
print(@items, " from new method")
end
end
# a new method is available
# for the instance via delegation
things.new_method #=> ['x','yz','defgh','ij','klmno'] from new method
I'm still not sure I understand you correctly. But if I do, Override
is your vertical composition and Compose is your horizontal
composition. Via Object.create, one can also mix traits with vertical
composition using the JavaScript prototype chain. But then unbound
inherited methods become observable, leading to all the normal
confusions. By using only Override for vertical composition, only
bound methods are observable. (This is like the old "binding on
extraction" property of old ES4 classes.)
Yes, regarding the end state of an object (via override/composition) you
understand it correctly. But I meant exactly delegation based
inheritance and asked -- why do you have (propose) a scheme where an
object will have /own/ properties but not inherited? In Ruby as I
showed, if we change Enumerable module (read Enumerable trait/mixin) by
adding a new method "new_method" then the instance "things" of the class
"MyCollection" automatically via delegation has access to the new method.
And regarding classes, I think it's not acceptable not to have
inheritance and to create also own properties. Even if will be a good VM
optimization, the dynamics of the languages is lost -- if we add a new
method to a class -- will all instances see this new method? That
exactly I'm asking.
P.S.: Why am I asking? -- just see that you everywhere use
static/const and frozen things. Want to know, do you propose JS to
be a static language without opened classes/protos/traits/etc,
"monkey-patching", etc.?
Not at all. As previously discussed, JavaScript will lose none of its
support for monkey patching, low integrity patterns, unfrozen objects,
etc. None of these proposals take any of those away.
Just to clarify and not to confuse terminology/meanings, do I understand
correctly: "low integrity " -- non-|this|-bounded inherited functions,
and "high integrity" -- vice-versa -- statically bound |this| and own
properties? Thanks.
Further, trait classes can be instantiated with Object.create, so
traits even enhance the expressiveness of low integrity patterns and
vertical composition using prototypes if that's what you wish.
Adapting a previous example:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = Object.create(
SuperPoint.prototype,
StatelessPointTrait());
Yeah, I see. But again, instantiation of a trait creates own properties
of an instance, but not just adds additional link for delegation.
By the way, the phrase "a trait instantiate" in general case contradicts
to traits concept. Because in many (all?) implementations and some
general definitions of traits -- traits in contrast with classes cannot
instantiate objects. They are just additional modules (to add small
traits for the instance).
Dmitry.
--
Cheers,
--MarkM
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss