Object.prototype.inspect ?
On Sat, Mar 7, 2009 at 9:18 PM, Tobie Langel <tobie.langel at gmail.com> wrote:
The lack of a more developer-friendly representation of objects than the one obtained through their toString method (or by calling Object.prototype.toString) made me long for an ES equivalent of Ruby's inspect instance method.
You could use JSON.stringify(obj), which is on track to be standardized in ES3.1.
(SpiderMonkey has the nonstandard uneval, as well as Object.prototype.toSource, which I think are a bit closer to what you want.)
It is very useful to be able to specify the debugging representation
of an object distinctively from it's toJSON or toString representation.
We've been using that to great effect in Prototype, to help out, for
example, with the debugging representation of DOM Elements:
<div id="foo" class="foo bar">
is a much more useful representation than:
[object HTMLDivElement]
which you get in Safari/Firefox/Chrome or worse:
[object Object]
which you get in IE.
Indeed, Object.prototype.toSource seems closer to what I was
interested in. Unfortunately, it's
- non standardized, and
- apparently used internally by FF[1].
To be useful for debugging, such a method needs to provide a good
default value and be easily overridden for custom objects. I wouldn't
be too keen on overriding a method which the browser vendor claims to
use internally.
Best,
Tobie
[1] developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/toSource
On Mar 11, 2009, at 4:20 AM, Tobie Langel wrote:
It is very useful to be able to specify the debugging representation
of an object distinctively from it's toJSON or toString
representation.We've been using that to great effect in Prototype, to help out, for
example, with the debugging representation of DOM Elements:<div id="foo" class="foo bar">
is a much more useful representation than:
[object HTMLDivElement]
True, although you have to ampersand-escape it if it flows into markup.
Indeed, Object.prototype.toSource seems closer to what I was
interested in. Unfortunately, it's
- non standardized, and
- apparently used internally by FF[1].
To be useful for debugging, such a method needs to provide a good
default value and be easily overridden for custom objects. I
wouldn't be too keen on overriding a method which the browser vendor
claims to use internally.
What text in the link you give made you think anything of the sort? I
see no use of "internally" or "internal" in that wiki page. You can
override toSource just as you can override toString. Doing so in a
content window cannot possibly affect the operation of the browser's
user interface ("chrome") windows.
On Wed, Mar 11, 2009 at 9:42 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Mar 11, 2009, at 4:20 AM, Tobie Langel wrote:
It is very useful to be able to specify the debugging representation of an object distinctively from it's toJSON or toString representation.
We've been using that to great effect in Prototype, to help out, for example, with the debugging representation of DOM Elements:
<div id="foo" class="foo bar">
is a much more useful representation than:
[object HTMLDivElement]
True, although you have to ampersand-escape it if it flows into markup.
Indeed, Object.prototype.toSource seems closer to what I was interested in. Unfortunately, it's
- non standardized, and
- apparently used internally by FF[1].
To be useful for debugging, such a method needs to provide a good default value and be easily overridden for custom objects. I wouldn't be too keen on overriding a method which the browser vendor claims to use internally.
What text in the link you give made you think anything of the sort? I see no use of "internally" or "internal" in that wiki page. You can override toSource just as you can override toString. Doing so in a content window cannot possibly affect the operation of the browser's user interface ("chrome") windows.
I prefer to use the term "shadow" to override. In the example below, C.prototype.toSource shadows Object.prototype.toSource;
function C(){ this.name = "c1"; }
C.prototype = { name : "c", title : "a C object", toSource : function() { return this.name + "::C\n" + Object.prototype.toSource.call(this); } };
Result: c1::C ({name:"c1"})
Perhaps you would like to use a for-in loop to debug properties; for(var p in this) { // build a string. }
? Garrett
On Mar 11, 2009, at 10:33 AM, Garrett Smith wrote:
[overlong cited text cut -- please do that when replying. /be]
What text in the link you give made you think anything of the sort?
I see no use of "internally" or "internal" in that wiki page. You can override toSource just as you can override toString. Doing so in a content
window cannot possibly affect the operation of the browser's user interface ("chrome") windows.I prefer to use the term "shadow" to override. In the example below, C.prototype.toSource shadows Object.prototype.toSource;
Ok, but what does this have to do with the price of eggs?
function C(){ this.name = "c1"; }
C.prototype = { name : "c", title : "a C object", toSource : function() { return this.name + "::C\n" + Object.prototype.toSource.call(this); } };
Result: c1::C ({name:"c1"})
Perhaps you would like to use a for-in loop to debug properties; for(var p in this) { // build a string. }
?
If you mean that overriding or shadowing by assignment makes an
enumerable property, even for a method that should not be enumerated,
then ES3.1's Object.defineProperty allows binding a non-enumerable
property.
But I still can't tell what your point is. It seems to be unrelated to
my question asking Tobie what the problem with overriding toSource (as
opposed to toString, and without dragging in any unrelated problem in
common to do with enumerability, which is irrelevant!) might be.
To be useful for debugging, such a method needs to provide a good
default value and be easily overridden for custom objects. I
wouldn't be too keen on overriding a method which the browser
vendor claims to use internally.What text in the link you give made you think anything of the sort?
I see no use of "internally" or "internal" in that wiki page. You
can override toSource just as you can override toString. Doing so in
a content window cannot possibly affect the operation of the
browser's user interface ("chrome") windows.
Turns out this page was modified... today. See the previous revision
here: developer.mozilla.org/index.php?title=En/Core_JavaScript_1.5_Reference/Global_Objects/Object/ToSource&revision=8
On Mar 11, 2009, at 18:57 , Brendan Eich wrote:
But I still can't tell what your point is. It seems to be unrelated
to my question asking Tobie what the problem with overriding
toSource (as opposed to toString, and without dragging in any
unrelated problem in common to do with enumerability, which is
irrelevant!) might be.
Now that the question of the "overrideability" of toSource has been
cleared out, it still remains that it isn't specified :(
On Mar 11, 2009, at 1:22 PM, Tobie Langel wrote:
On Mar 11, 2009, at 18:57 , Brendan Eich wrote:
But I still can't tell what your point is. It seems to be unrelated
to my question asking Tobie what the problem with overriding
toSource (as opposed to toString, and without dragging in any
unrelated problem in common to do with enumerability, which is
irrelevant!) might be.Now that the question of the "overrideability" of toSource has been
cleared out, it still remains that it isn't specified :(
That's right. It was rejected during ES3 after I made a brief pitch.
Rok Yu of Microsoft in particular was convinced that serialization is
domain-specific and there's no point in a 90% solution (I'm
paraphrasing from memory).
Code is spec and there are bugs in all code (and in all specs), but
this does not mean it can't be standardized. Since you are asking for
something new, someone is going to have to do work. Want to help?
Your idea for a DOM node giving its representation as HTML or XML
reminds me of E4X. SpiderMonkey can already pretty-print XML data as
markup source.
On Mar 11, 2009, at 1:00 PM, Tobie Langel wrote:
To be useful for debugging, such a method needs to provide a good
default value and be easily overridden for custom objects. I
wouldn't be too keen on overriding a method which the browser
vendor claims to use internally.What text in the link you give made you think anything of the sort?
I see no use of "internally" or "internal" in that wiki page. You
can override toSource just as you can override toString. Doing so
in a content window cannot possibly affect the operation of the
browser's user interface ("chrome") windows.Turns out this page was modified... today. See the previous revision
here: developer.mozilla.org/index.php?title=En/Core_JavaScript_1.5_Reference/Global_Objects/Object/ToSource&revision=8
That pair of sentences:
This method is usually called internally by JavaScript and not
explicitly in code. You can call toSource while debugging to examine
the contents of an object.
did not say what you implied it said. Probably it was trying to say
that uneval(v) is the user interface to use, not (v != null) ?
v.toSource() : "" + v. But it's not a big deal, and the wiki-elves
have already fixed it.
On Wed, Mar 11, 2009 at 10:57 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Mar 11, 2009, at 10:33 AM, Garrett Smith wrote:
[overlong cited text cut -- please do that when replying. /be]
OK.
What text in the link you give made you think anything of the sort? I see no use of "internally" or "internal" in that wiki page. You can override toSource just as you can override toString. Doing so in a content window cannot possibly affect the operation of the browser's user interface ("chrome") windows.
I prefer to use the term "shadow" to override. In the example below, C.prototype.toSource shadows Object.prototype.toSource;
Ok, but what does this have to do with the price of eggs?
What?
On Mar 11, 2009, at 21:32 , Brendan Eich wrote:
Want to help?
Sure. How?
Tobie
Apologies, I thought I sent this yesterday but never got around to it:
On Wed, Mar 11, 2009 at 6:20 AM, Tobie Langel <tobie.langel at gmail.com> wrote:
It is very useful to be able to specify the debugging representation of an object distinctively from it's toJSON or toString representation.
No doubt. I've implemented similar functionality on every serious JavaScript project I've done.
Indeed, Object.prototype.toSource seems closer to what I was interested in. Unfortunately, it's
- non standardized, and
- apparently used internally by FF[1].
To be useful for debugging, such a method needs to provide a good default value and be easily overridden for custom objects. I wouldn't be too keen on overriding a method which the browser vendor claims to use internally.
I think you have found some wiki nonsense. We use toSource internally (a) when generating error messages (b) in the default toSource methods themselves, when recursively toSourcing the contents of an Array or Object. I'll fix the wiki. [UPDATE: I edited the page, as you noticed...]
I agree this would be useful to standardize and has plenty of precedent in other languages--in Python it's called repr()--but I doubt anyone wants to hold up 3.1 to get it in. There are a few details to nail down (regarding e.g. Arrays that contain themselves--try that in Firefox to see something else curious, borrowed from Lisp I think...). Perhaps in ES4?
On 2009-03-12, at 14:28EDT, Jason Orendorff wrote:
On Wed, Mar 11, 2009 at 6:20 AM, Tobie Langel
<tobie.langel at gmail.com> wrote:It is very useful to be able to specify the debugging
representation of an object distinctively from it's toJSON or toString representation.No doubt. I've implemented similar functionality on every serious JavaScript project I've done.
Same here, but I bet I did it differently. Which makes me think this
is not an area for standardization. As long as there is a standard
way to enumerate the properties of an Object and a standard way to
determine an Object's 'class' (i.e., constructor, which you can only
do in ES3 if you annotate each object yourself), you can write your
own inspect and this is a dandy place to allow innovation, IMO.
toSource or repr can learn from Lisp, but if you really want to do it
right, we have along way to go. There is a big difference between
being able to inspect/debug an object for human legibility and
expecting that you can convert an object to a source expression that
when evaluated will be "equal" to the original object (for some
definition of equal!).
On Mar 12, 2009, at 19:28 , Jason Orendorff wrote:
No doubt. I've implemented similar functionality on every serious JavaScript project I've done.
Anything you would be able to share ?
There are a few details to nail down (regarding e.g. Arrays that
contain themselves--try that in Firefox to see something else curious, borrowed from Lisp I think...).
Indeed:
var a = [1,2, 3, 4]; a.push(a); a.toSource(); // -> "#1=[1, 2, 3, 4, #1#]"
On Mar 12, 2009, at 19:57 , P T Withington wrote:
As long as there is a standard way to enumerate the properties of an
Object and a standard way to determine an Object's 'class' (i.e.,
constructor, which you can only do in ES3 if you annotate each
object yourself), you can write your own inspect and this is a dandy
place to allow innovation, IMO.
Well, it doesn't seem like the later is standardized either. There is:
function getClass(obj) { return (Object.prototype.toString.call(obj) || '').slice(8, -1); }
for built-in objects (and host objects in some implementations).
Possibly something like this for custom objects (pending specification
of Function.prototype.name) or, in the meantime, function
decompilation (also unspecified and with issues of its own):
getConstructorName(obj) { return obj.constructor.name; }
There is a big difference between being able to inspect/debug an
object for human legibility and expecting that you can convert an
object to a source expression that when evaluated will be "equal" to
the original object (for some definition of equal!).
Agreed. My request was - and still is - for the former.
On Wed, Mar 11, 2009 at 9:57 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Mar 11, 2009, at 10:33 AM, Garrett Smith wrote:
If you mean that overriding or shadowing by assignment makes an enumerable property, even for a method that should not be enumerated, then ES3.1's Object.defineProperty allows binding a non-enumerable property.
But I still can't tell what your point is. It seems to be unrelated to my
The point is to get the terminology correct.
The term "override" is ambiguous here. It does not differentiate "replace X value" with "shadow X in prototype chain".
If poor terminology is used, it can be a good idea to clarify that with better terminology. There doesn't seem to be a good reason to take offense to that.
question asking Tobie what the problem with overriding toSource (as opposed to toString, and without dragging in any unrelated problem in common to do with enumerability, which is irrelevant!) might be.
You did not specify whether you meant "replace the value of Object.prototype.toSource" or create a toSource on a user-defined object. The former would result in incompatible behavior between implementations, including enumeration.
/be
Garrett
[...]
On Mar 12, 2009, at 20:49 , Garrett Smith wrote:
You did not specify whether you meant "replace the value of Object.prototype.toSource" or create a toSource on a user-defined object. The former would result in incompatible behavior between implementations, including enumeration.
The latter. I'm sorry, I thought that was clear from my initial message.
On Mar 12, 2009, at 12:49 PM, Garrett Smith wrote:
On Wed, Mar 11, 2009 at 9:57 AM, Brendan Eich <brendan at mozilla.com>
wrote:If you mean that overriding or shadowing by assignment makes an
enumerable property, even for a method that should not be enumerated, then
ES3.1's Object.defineProperty allows binding a non-enumerable property.But I still can't tell what your point is. It seems to be unrelated
to myThe point is to get the terminology correct.
The term "override" is ambiguous here. It does not differentiate "replace X value" with "shadow X in prototype chain".
Point taken, but you really were interjecting this distinction without
a difference into a direct question from me to Tobie about where in
the (updated before I read it, but after Tobie read it)
developer.mozilla.org wiki-page Tobie had seen a warning about
toSource being used internally.
Which made me wonder what your point was, other than pointing out a
difference between shadowing and replacing that was not relevant. Yes,
I know all about the distinction. Yes, I know assignment makes an
enumerable property, this is considered harmful, etc. etc. I think
everyone participating on es-discuss knows these things.
I don't take personal offense, but I do think this is a waste of
everyone's time on the list. Can we move on now?
On Mar 12, 2009, at 11:57 AM, P T Withington wrote:
On 2009-03-12, at 14:28EDT, Jason Orendorff wrote:
On Wed, Mar 11, 2009 at 6:20 AM, Tobie Langel
<tobie.langel at gmail.com> wrote:It is very useful to be able to specify the debugging
representation of an object distinctively from it's toJSON or toString representation.No doubt. I've implemented similar functionality on every serious JavaScript project I've done.
Same here, but I bet I did it differently. Which makes me think
this is not an area for standardization. As long as there is a
standard way to enumerate the properties of an Object and a standard
way to determine an Object's 'class' (i.e., constructor, which you
can only do in ES3 if you annotate each object yourself), you can
write your own inspect and this is a dandy place to allow
innovation, IMO.
This was Rok's argument against standardizing toSource/uneval in ES3
timeframe, and it's a good one if the intention is to serialize and
deserialize all cases preserving private state, non-enumerable
properties, etc.
On the other hand, if the intention is to provide an easily inspected
string representation that gives obvious or overt property values, e.g.,
uneval([0,1,[2,3]]) => "[0, 1, [2, 3]]" instead of the muddled
toString result "0,1,2,3", then there's benefit in a common standard.
toSource or repr can learn from Lisp, but if you really want to do
it right, we have along way to go. There is a big difference
between being able to inspect/debug an object for human legibility
and expecting that you can convert an object to a source expression
that when evaluated will be "equal" to the original object (for some
definition of equal!).
Indeed. I borrowed sharp variables to handle cycles and join points in
object graphs from Common Lisp. But I did not follow through on native
or host object "private data", or non-enumerable properties. If object
initialisers could express such things portably, perhaps it would be
worth doing.
For non-deletable and non-writable properties, ES4 had at one point
obj = {var foo: "hi", const bar: 42};
It's a bit obscure that var means non-deletable. Probably brute force
syntax is better, but I'm not going to make some up right now. It will
be verbose, keyword-y. I'd argue the keywords (plural) need to go near
the :, not before the identifer, and in some kind of brackets (if more
than one, or always).
For native functions one does not want to give away code addresses,
however. You could imagine using a crypto-hash to uniquely identify
natives. Then one could round-trip references to native functions made
from object properties.
On Mar 12, 2009, at 3:54 AM, Tobie Langel wrote:
On Mar 11, 2009, at 21:32 , Brendan Eich wrote:
Want to help?
Sure. How?
The discussion here should focus on use-cases, which will help us
write down requirements and anti-requirements. If we only care about
diagnostic inspection, not round-tripping equivalent object graphs
(the two are not always cleanly separable), we should say what does
round trip, what does not (native data including method references,
e.g.).
On 2009-03-12, at 16:31EDT, Brendan Eich wrote:
On Mar 12, 2009, at 11:57 AM, P T Withington wrote:
[...]
Same here, but I bet I did it differently. Which makes me think
this is not an area for standardization. As long as there is a
standard way to enumerate the properties of an Object and a
standard way to determine an Object's 'class' (i.e., constructor,
which you can only do in ES3 if you annotate each object yourself),
you can write your own inspect and this is a dandy place to allow
innovation, IMO.This was Rok's argument against standardizing toSource/uneval in ES3
timeframe, and it's a good one if the intention is to serialize and
deserialize all cases preserving private state, non-enumerable
properties, etc.
I think we are a long way from needing serialize/deserialize arbitrary
Objects. Having JSON should suffice.
On the other hand, if the intention is to provide an easily
inspected string representation that gives obvious or overt property
values, e.g., uneval([0,1,[2,3]]) => "[0, 1, [2, 3]]" instead of the muddled
toString result "0,1,2,3", then there's benefit in a common standard.
And I agree that toString is next to useless as a debugging tool.
Which is why I wrote my own. In my experience of writing my own, I
have found that I've changed what I want the 'representation' of an
object to be, and continue to change that. (Since my output is
intended to help the human programmer, and not be eval-ed, I continue
to adjust how attributes of an object are sorted, labelled,
abbreviated, hidden/revealed, re-inspected, etc. Since my users are
programming in a language above Javascript, I'm starting to change the
representation to be more like the high-level language they write in,
rather than display the Javascript "assembly language".) Those are
some of the reasons I'm not in a rush to standardize what inspecting
an object means.
But, I would very much like to see a standard way to discover an
Object's constructor, and a way to enumerate all the properties of
an object. I know there is a tension between security and
introspection. I don't know if this is something that can be handled
by the presumably already-overloaded strict mode.
On Mar 12, 2009, at 1:54 PM, P T Withington wrote:
But, I would very much like to see a standard way to discover an
Object's constructor, and a way to enumerate all the properties of
an object. I know there is a tension between security and
introspection. I don't know if this is something that can be
handled by the presumably already-overloaded strict mode.
ES3.1 has Object.getOwnPropertyNames and
Object.getOwnPropertyDescriptor, so you can go to town with those.
Identifying the constructor is harder. Having to slice a substring out
of Object.prototype.toString.call(obj) is disgusting, but it kind of
works. Not sure about all the various non-standard class names
formatted that way (IE calls DOM classes "Object"?!).
On 2009-03-12, at 17:04EDT, Brendan Eich wrote:
On Mar 12, 2009, at 1:54 PM, P T Withington wrote:
But, I would very much like to see a standard way to discover an
Object's constructor, and a way to enumerate all the properties
of an object. I know there is a tension between security and
introspection. I don't know if this is something that can be
handled by the presumably already-overloaded strict mode.ES3.1 has Object.getOwnPropertyNames and
Object.getOwnPropertyDescriptor, so you can go to town with those.
Excellent. I almost guessed correctly, I called it
objectOwnProperties: tinyurl.com/atcasp
Identifying the constructor is harder. Having to slice a substring
out of Object.prototype.toString.call(obj) is disgusting, but it
kind of works. Not sure about all the various non-standard class
names formatted that way (IE calls DOM classes "Object"?!).
So, why can't we standardize the constructor
property, that seems to
exist in some implementations, to actually return the constructor of
the object (and not the constructor of the object's prototype, which
is useless)?
On Mar 12, 2009, at 21:54 , P T Withington wrote:
I think we are a long way from needing serialize/deserialize
arbitrary Objects. Having JSON should suffice.
I agree.
Since my users are programming in a language above Javascript, I'm
starting to change the representation to be more like the high-level
language they write in, rather than display the Javascript "assembly
language".)
This seems similar to the needs of the objective-j folks (as expressed
in the "name property for built-in functions??" thread).
But, I would very much like to see a standard way to discover an
Object's constructor, and a way to enumerate all the properties of
an object.
Again, I'm very interested in the former. And doing away with the
Object.prototype.toString.call(obj) trick for built-in types would be
a good thing.
Could we possibly imagine having a non-[[Writable]] and non-
[[Configurable]] 'name' property of built-in constructors whose value
would be [[Class]] ?
Tobie
On Mar 12, 2009, at 1:33 PM, Brendan Eich wrote:
On Mar 12, 2009, at 3:54 AM, Tobie Langel wrote:
On Mar 11, 2009, at 21:32 , Brendan Eich wrote:
Want to help?
Sure. How?
The discussion here should focus on use-cases, which will help us
write down requirements and anti-requirements. If we only care about
diagnostic inspection, not round-tripping equivalent object graphs
(the two are not always cleanly separable), we should say what does
round trip, what does not (native data including method references,
e.g.).
So [0,1,[2,3]] => "[0, 1, [2, 3]]" is good for both hi-fi serialize/
deserialize value recovery, and (I claim) for human inspection. But a
really long and/or deep array structure would be unreadable, so
ellipses might be appropriate for the human in the loop. IMHO we
should borrow from Python (again) on pragmatic stuff like eliding
overlong repr strings.
Native functions converting to strings as in
js> Array.prototype.toString
function toString() { [native code] }
is an ancient convention that's not standardized. It's unfortunate in
that it throws 13 characters plus braces and spaces at telling the
user "no source here", but at least it can't compile into a valid
program to be mis-executed. With anything like a standardized
toSource, we can do better. Again the pragmatic detailing is worth
getting right. Start by citing precedent in Python and other
languages, maybe? I'd find that helpful.
On Mar 12, 2009, at 2:23 PM, P T Withington wrote:
Identifying the constructor is harder. Having to slice a substring
out of Object.prototype.toString.call(obj) is disgusting, but it
kind of works. Not sure about all the various non-standard class
names formatted that way (IE calls DOM classes "Object"?!).So, why can't we standardize the
constructor
property, that seems
to exist in some implementations, to actually return the constructor
of the object (and not the constructor of the object's prototype,
which is useless)?
The constructor property is in ES1-3.1. For a constructor function F,
F.prototype.constructor === F, but constructor is writable and
configurable.
The case you are complaining about is not exactly as your words
describe it:
js> function F(){}
js> F.constructor
function Function() { [native code] } js> o = new F [object Object] js> o.constructor
function F() { }
That works as expected, F.prototype.constructor === F therefore
o.constructor === F (and F.constructor === Function). Can you show the
case (it involves two levels of prototyping, I believe) where you
don't get the right default behavior, and have to shadow (thanks
Garrett!) the prototype's constructor property?
I don't think we can change this, anyway -- it's an incompatible
change. More, since constructor can be deleted or overwritten,
counting on it for inspection is probably a bad idea.
On Mar 12, 2009, at 2:25 PM, Tobie Langel wrote:
Could we possibly imagine having a non-[[Writable]] and non- [[Configurable]] 'name' property of built-in constructors whose
value would be [[Class]] ?
So a string-valued property, e.g. f.constructor.name === "Function"
for function f(){}, d.constructor.name === "Date" for (d = new Date),
etc.?
Works for me, already implemented in SpiderMonkey. Note constructor
being writable and configurable as I mentioned in reply to Tucker just
now. If we want to avoid that hazard, we would need a separate,
optionally locked-down method, say Object.getClassName(o).
On 2009-03-12, at 19:25EDT, Brendan Eich wrote:
On Mar 12, 2009, at 2:23 PM, P T Withington wrote:
[...]
So, why can't we standardize the
constructor
property, that seems
to exist in some implementations, to actually return the
constructor of the object (and not the constructor of the object's
prototype, which is useless)?The constructor property is in ES1-3.1. For a constructor function
F, F.prototype.constructor === F, but constructor is writable and
configurable.The case you are complaining about is not exactly as your words
describe it:js> function F(){} js> F.constructor function Function() { [native code] } js> o = new F [object Object] js> o.constructor function F() { }
That works as expected, F.prototype.constructor === F therefore
o.constructor === F (and F.constructor === Function). Can you show
the case (it involves two levels of prototyping, I believe) where
you don't get the right default behavior, and have to shadow (thanks
Garrett!) the prototype's constructor property?
Exactly. If you use prototype's to implement inheritance, you say:
js> function mysuper () {};
js> function mysub () {};
js> mysub.prototype = new mysuper; [object Object] js> var o = new mysub;
js> o instanceof mysuper
true js> o instanceof mysub
true js> o.constructor
function mysuper() { }
Not the droids I was looking for. My work-around is to always say:
function subclass () { this.constructor = arguments.callee; }
I don't want to smash mysub.prototype.constructor, because that is how
I implement 'super': constructor.prototype.constructor.
I don't think we can change this, anyway -- it's an incompatible
change. More, since constructor can be deleted or overwritten,
counting on it for inspection is probably a bad idea.
Yeah, I'm sure you are right. And in my case, since I control the
Javascript that is getting written, I can live with it. But, I can't
for the life of me think of a use case for the existing way that
constructor works.
On 2009-03-12, at 18:49EDT, Brendan Eich wrote:
On Mar 12, 2009, at 1:33 PM, Brendan Eich wrote:
On Mar 12, 2009, at 3:54 AM, Tobie Langel wrote:
On Mar 11, 2009, at 21:32 , Brendan Eich wrote:
Want to help?
Sure. How?
The discussion here should focus on use-cases, which will help us
write down requirements and anti-requirements. If we only care
about diagnostic inspection, not round-tripping equivalent object
graphs (the two are not always cleanly separable), we should say
what does round trip, what does not (native data including method
references, e.g.).So [0,1,[2,3]] => "[0, 1, [2, 3]]" is good for both hi-fi serialize/ deserialize value recovery, and (I claim) for human inspection. But
a really long and/or deep array structure would be unreadable, so
ellipses might be appropriate for the human in the loop. IMHO we
should borrow from Python (again) on pragmatic stuff like eliding
overlong repr strings.
I invite you to play around with the OpenLaszlo Debugger, which has
borrow heavily from Lisp (skipping the Python filter :). You can see
some examples in the documentation of the formatted output '%w'
directive: xrl.us/bejep3 or you can get down to the nitty-
gritty and read the source of the LzDebugService/__String method: xrl.us/bejeqd
Since our classes mostly implement a DOM-like structure, we use a
pseudo-xpath syntax to abbreviate objects that are contained in other
objects (although I haven't worried about circular objects yet). For
example, here's what the typical user might see if they are inspecting
a <button id="mybutton"> object's subnode array:
lzx> Debug.inspect(«Array(6)| [#myButton/@_outerbezel, #myButton…»)
«Array(6)#48| [#myButton/@_outerbezel, #myButton/@_innerbezel…» {
0: #myButton/@_outerbezel
1: #myButton/@_innerbezel
2: #myButton/@_face
3: #myButton/@_innerbezelbottom
4: #myButton/@_outerbezelbottom
5: #myButton/@_title
}
«Array(6)#48| [#myButton/@_outerbezel, #myButton/@_innerbezel,
#myButton/@_face, #myButton/@_innerbezelbottom, #myButton/
@_outerbezelbottom, #myButton/@_title]»
lzx>
On Fri, Mar 13, 2009 at 11:14 AM, P T Withington <ptw at pobox.com> wrote: <<snip>>
function subclass () { this.constructor = arguments.callee; }
I don't want to smash mysub.prototype.constructor, because that is how I implement 'super': constructor.prototype.constructor.
I don't think we can change this, anyway -- it's an incompatible change. More, since constructor can be deleted or overwritten, counting on it for inspection is probably a bad idea.
Yeah, I'm sure you are right. And in my case, since I control the Javascript that is getting written, I can live with it. But, I can't for the life of me think of a use case for the existing way that constructor works.
Es-discuss mailing list Es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
So is there a reason the workaround is intolerable? You could go further with this workaround in es3.1 and define it as a readonly property, and also shadow its toSource to output a call to the constructor function, with appropriate values. This puts the responsibility for the housekeeping on those who care about the accuracy of the constructor, without burdening those of us who use strategies that don't depend on the value being accurate.
As long as we're talking roundtripping, might I just point out that creating a full roundtripping toSource() for a function value is a minefield? As has already been pointed out, there's the issues with callable host objects, and private data. But let's not forget closures, and enumerable (or even non enumerable) function properties. functions are objects too! I cannot think of a particularly tidy way to serialize either of those into a single expression.
What about arrays with defined properties? You can convert them to the object notation, but how do you deserialize it to have the array prototype? (well, you could have proto defined I suppose)
It would be nice if this all worked, and there was syntax for it, but I have the feeling that roundtripping has got to be a non goal. the tc39 crew may have time for one or two improvements, but going all the way is a big- maybe even impossible job.
On Mar 12, 2009, at 6:30 PM, Breton Slivka wrote:
So is there a reason the workaround is intolerable? You could go further with this workaround in es3.1 and define it as a readonly property, and also shadow its toSource to output a call to the constructor function, with appropriate values. This puts the responsibility for the housekeeping on those who care about the accuracy of the constructor, without burdening those of us who use strategies that don't depend on the value being accurate.
That's the rationale in a nutshell for the de minimus ES1 (pre-ES1, I
had it in Netscape 3 if not 2) setting of f.prototype.constructor to f
when creating f.prototype on demand.
It could be argued it's too implemnetation-oriented but we had a hard
time figuring out how to elaborate it without imposing a per-instance
cost (as Tucker chose to do, but that was his choice) on all objects.
I remember talking through this in ES1 days, early 1997 probably.
As long as we're talking roundtripping, might I just point out that creating a full roundtripping toSource() for a function value is a minefield? As has already been pointed out, there's the issues with callable host objects, and private data. But let's not forget closures, and enumerable (or even non enumerable) function properties. functions are objects too! I cannot think of a particularly tidy way to serialize either of those into a single expression.
No one is trying for this. It's an anti-goal.
What about arrays with defined properties? You can convert them to the object notation, but how do you deserialize it to have the array prototype? (well, you could have proto defined I suppose)
Back in ES3 era we played with a generalized object initialiser syntax
that allowed you to specify the class constructor. Syntax proposals
circled around the low-energy well close to Java's anonymous inner
classes:
var arrayWithNamedProps = new Array {0:a, 1:b, 2:c, foo:"hi", bar:42};
but automatic semicolon insertion bites this hard.
It would be nice if this all worked, and there was syntax for it, but I have the feeling that roundtripping has got to be a non goal. the tc39 crew may have time for one or two improvements, but going all the way is a big- maybe even impossible job.
Yes, and on this point Rok Yu was right. But it was the wrong goal.
Can we have an inspect-like facility, as Tobie requests, without
sliding down this slope? I think so.
On Mar 13, 2009, at 00:27 , Brendan Eich wrote:
So a string-valued property, e.g. f.constructor.name === "Function"
for function f(){}, d.constructor.name === "Date" for (d = new
Date), etc.?
Yes. That would also help solve cross-frame issues for which
instanceof Foo and constructor === Foo are useless.
Works for me, already implemented in SpiderMonkey. Note constructor
being writable and configurable as I mentioned in reply to Tucker
just now. If we want to avoid that hazard, we would need a separate,
optionally locked-down method, say Object.getClassName(o).
Right, that makes sense.
Current behaviour in Firebug:
function Parent() {} var parent = new Parent(); parent.constructor;
Parent()
Object.prototype.toString.call(parent); "[object Object]"
function Child() {} Child.prototype = new Parent(); var child = new Child(); child.constructor;
Parent()
Object.prototype.toString.call(child); "[object Object]"
Desired behaviour:
Object.prototype.toString.call(child); "[object Child]"
Object.prototype.toString.call(parent); "[object Parent]"
And ideally:
Child.name; "Child"
Parent.name; "Parent"
A very naïve specification would boil down to modifying step 2 and
inserting a step 3 inside of 13.2.2 of the 23feb09 draft:
13.2.2 [[Construct]]
When the [[Construct]] property for a Function object F is called with
a possibly empty list of
arguments, the following steps are taken:
- Let obj be a newly created native ECMAScript object.
- if the name property of F is a string value set the [[Class]]
internal property of obj to the name property of F. - if the name property of F is not a string value set the [[Class]]
internal property of obj to "Object". ...
There's probably a lot of issues I haven't foreseen here (on top of
the yet unspecified name property of functions), but it hopefully
helps to clarify what I was thinking about. It also avoids touching
the behaviour of the constructor property of the prototype object.
Given that spec change, I suspect Object.getClassName(o) could be
desugared to Object.prototype.toString.call(object).slice(8, -1).
Best,
Tobie
On Mar 12, 2009, at 23:49 , Brendan Eich wrote:
Start by citing precedent in Python and other languages, maybe? I'd
find that helpful.
In ruby: gist.github.com/78355
On Mar 12, 2009, at 6:45 PM, Tobie Langel wrote:
Right, that makes sense.
Current behaviour in Firebug:
function Parent() {} var parent = new Parent(); parent.constructor; Parent()
Firebug slices from toString's result, it seems.
Object.prototype.toString.call(parent); "[object Object]"
The [[Class]] of parent is "Object", per ES1-3.1.
Desired behaviour:
Object.prototype.toString.call(child); "[object Child]"
Object.prototype.toString.call(parent); "[object Parent]"
User-defined constructor functions are not the same as native class
constructor functions, alas. This duality is a botch of some order,
one we've lived with for a long time.
Your proposal does not completely "fix" it, though, since the
[[Class]] of Parent.prototype is "Object". It was not constructed by
Parent. Maybe this is ok.
Outside of built-in classes, [[Class]] is used only for
Object.prototype.toString. In TC39 we've argued about what [[Class]]
means, and I've maintained it's a token corresponding in concrete
implementation terms (vtable or equivalent) to a pile of code
implementing a specific set of internal methods (like Array's custom
[[Put]]).
So changing [[Class]] just to get a differnt
Object.prototype.toString.call(obj) result is arguably wrong. Another
option here is to add a different internal property, [[ClassName]].
And ideally:
Child.name; "Child"
Parent.name; "Parent"
This WFM:
js> function Parent(){}
js> Parent.name
Parent
A very naïve specification would boil down to modifying step 2 and
inserting a step 3 inside of 13.2.2 of the 23feb09 draft:13.2.2 [[Construct]] When the [[Construct]] property for a Function object F is called
with a possibly empty list of arguments, the following steps are taken:
- Let obj be a newly created native ECMAScript object.
- if the name property of F is a string value set the [[Class]]
internal property of obj to the name property of F.- if the name property of F is not a string value set the [[Class]]
internal property of obj to "Object".
From the point of view of implementors, setting the "vtable" for an
instance is not going to be done based on a string value in a property
of F. In either case (step 2 or 3) the same implementation code for
generic Object behavior, appropriate to [[Class]] "Object" in ES1-3,
will be chosen. The only difference that matters (correct me if I'm
wrong) is the output of Object.prototype.toString.call(obj).
There's probably a lot of issues I haven't foreseen here (on top of
the yet unspecified name property of functions), but it hopefully
helps to clarify what I was thinking about. It also avoids touching
the behaviour of the constructor property of the prototype object.Given that spec change, I suspect Object.getClassName(o) could be
desugared to Object.prototype.toString.call(object).slice(8, -1).
No, since outside of strict mode, Object is a writable, deletable
property of the global object, and toString is writable/deletable in
Object.prototype, and call can be shadowed, etc. etc.
Desugaring has to go from derived and optional to primitive and
inviolable.
On Mar 12, 2009, at 7:13 PM, Brendan Eich wrote:
On Mar 12, 2009, at 6:45 PM, Tobie Langel wrote:
A very naïve specification would boil down to modifying step 2 and
inserting a step 3 inside of 13.2.2 of the 23feb09 draft:13.2.2 [[Construct]] When the [[Construct]] property for a Function object F is called
with a possibly empty list of arguments, the following steps are taken:
- Let obj be a newly created native ECMAScript object.
- if the name property of F is a string value set the [[Class]]
internal property of obj to the name property of F.- if the name property of F is not a string value set the
[[Class]] internal property of obj to "Object".From the point of view of implementors, setting the "vtable" for an
instance is not going to be done based on a string value in a
property of F. In either case (step 2 or 3) the same implementation
code for generic Object behavior, appropriate to [[Class]] "Object"
in ES1-3, will be chosen. The only difference that matters (correct
me if I'm wrong) is the output of Object.prototype.toString.call(obj).
One more point representing implementors everywhere ;-).
The steps in 13.2.2 do not require storing a link to the constructor
function directly in the new instance. This is the point I mentioned
going over in 1997 during ES1 standardization. Generally while objects
have a fixed number of fields to set on creation, including
[[Prototype]] and [[Class]], they do not have [[Constructor]] or
[[ClassName]]. We don't want to add to the mandatory short-list (which
is [[Prototype]] and [[Class]], [[Scope]] too for function and certain
DOM objects).
What you want is for Object.prototype.toString.call(obj) to return
F.name. That could be specified by changing Object.prototype.toString,
instead of 13.2.2. I don't have time to try it out and see how it
works, but I thought I'd suggest it. The constructor property is
writable and deletable, which indeed creates a hazard. But it may be
tolerable.
On Mar 13, 2009, at 02:59 , Tobie Langel wrote:
On Mar 12, 2009, at 23:49 , Brendan Eich wrote:
Start by citing precedent in Python and other languages, maybe? I'd
find that helpful.In ruby: gist.github.com/78355
and in Python: gist.github.com/78424
On Mar 13, 2009, at 03:13 , Brendan Eich wrote:
Your proposal does not completely "fix" it, though, since the
[[Class]] of Parent.prototype is "Object". It was not constructed by
Parent. Maybe this is ok.
That actually didn't bother me that much (I was thinking in classical
rather than prototypal inheritance terms). Now that I've got my head
set straight again I do see how this is an issue.
It breaks the equality between the instance's and the prototype's
"[[Class]]" so that:
Object.getClassName(Parent.prototype) !== Object.getClassName(new
Parent());
Turns out that there is a precedent (still in Firebug):
Object.prototype.toString.call(document.createElement('div')) "[object HTMLDivElement]"
Object.prototype.toString.call(HTMLDivElement.prototype) "[object XPC_WN_ModsAllowed_NoCall_Proto_JSClass]"
And yes, I know this is a host object.
Outside of built-in classes, [[Class]] is used only for
Object.prototype.toString. In TC39 we've argued about what [[Class]]
means, and I've maintained it's a token corresponding in concrete
implementation terms (vtable or equivalent) to a pile of code
implementing a specific set of internal methods (like Array's custom
[[Put]]).So changing [[Class]] just to get a differnt
Object.prototype.toString.call(obj) result is arguably wrong.
Another option here is to add a different internal property,
[[ClassName]].
That felt dirty. Now I know why.
From the point of view of implementors, setting the "vtable" for an
instance is not going to be done based on a string value in a
property of F. In either case (step 2 or 3) the same implementation
code for generic Object behavior, appropriate to [[Class]] "Object"
in ES1-3, will be chosen.
Understood. Same issue as above.
The only difference that matters (correct me if I'm wrong) is the
output of Object.prototype.toString.call(obj).
That's correct. However your earlier comment about the broken equality
between the "[[Class]]" of Parent.prototype and a Parent "instance"
opened up my eyes. Is there a way around that ? Or is it a huge can of
worms ?
There's probably a lot of issues I haven't foreseen here (on top of
the yet unspecified name property of functions), but it hopefully
helps to clarify what I was thinking about. It also avoids touching
the behaviour of the constructor property of the prototype object.Given that spec change, I suspect Object.getClassName(o) could be
desugared to Object.prototype.toString.call(object).slice(8, -1).No, since outside of strict mode, Object is a writable, deletable
property of the global object, and toString is writable/deletable in
Object.prototype, and call can be shadowed, etc. etc.
Right. The fact that I wouldn't touch Object.prototype doesn't imply
someone else couldn't. :) And thank you for the precise definition of
desugaring!
Tobie
On Mar 13, 2009, at 03:28 , Brendan Eich wrote:
What you want is for Object.prototype.toString.call(obj) to return
F.name.
Right.
That could be specified by changing Object.prototype.toString,
instead of 13.2.2.
Right. Though it might be a good thing to keep accessing [[Class]] and
F.name separately given the information you provided earlier.
I don't have time to try it out and see how it works, but I thought
I'd suggest it. The constructor property is writable and deletable,
which indeed creates a hazard. But it may be tolerable.
If it's for debugging purposes only, definitely.
On Mar 12, 2009, at 8:28 PM, P T Withington wrote:
On 2009-03-12, at 21:38EDT, Brendan Eich wrote:
That's the rationale in a nutshell for the de minimus ES1 (pre-ES1,
I had it in Netscape 3 if not 2) setting of f.prototype.constructor
to f when creating f.prototype on demand.It could be argued it's too implemnetation-oriented but we had a
hard time figuring out how to elaborate it without imposing a per- instance cost (as Tucker chose to do, but that was his choice) on
all objects. I remember talking through this in ES1 days, early
1997 probably.Ideally, I could have what I want with a per-constructor cost,
rather than a per-instance cost. Since we know each instance
already has a [[Class]] and a [[Prototype]] property and we don't
want to add another slot of overhead, we could expand one of those.
As an aside, you might think we could (ignoring tricks to make
[[Class]] implicit by allocating all objects of the same class from a
given page or larger aligned chunk of memory) eliminate [[Class]] and
have only [[Prototype]], with internal methods such as [[Get]] and
[[ThrowingPut]] defined so as to be unnameable by script on the
prototype object.
This would have nice properties for composing, e.g., custom extensions
to native instances, e.g., Date instances. Date methods such as
Date.prototype.toString would find the internal state, named
[[Private]], say, by looking up an unnameable-by-script identifier
starting from the FancyDate instance, then looking in its true Date
prototype:
js> function FancyDate() {}
js> FancyDate.prototype = new Date(0)
Wed Dec 31 1969 16:00:00 GMT-0800 (PST)
Alas it doesn't work:
js> fd = new FancyDate
Date.prototype.toString called on incompatible Object
From ES3 15.9.5:
''In following descriptions of functions that are properties of the
Date prototype object, the phrase “this Date object” refers to the
object that is the this value for the invocation of the function. None
of these functions are generic; a TypeError exception is thrown if the
this value is not an object for which the value of the internal
[[Class]] property is "Date".''
Behold nominal typing in the built-ins, separate from prototype-based
delegation. I regret this impurity, but it's deep and it reflects
implementation biases of the time, still present today.
The [[Prototype]] property is already per-constructor, but it's an
Object itself, and its semantics mean I can't do what I want there.
Along your line of thought that the [[Class]] value is not really a
string (because you can only get to the specified string with the
toString 'accessor') why can't the [[Class]] value be per- constructor, and have a slot that takes you back to the constructor,
via some suitably-named accessor? (Too badconstructor
is already
used.)Perhaps this all becomes moot when I have real classes in es4, but
until that time...
ES4 is no more, its sound parts survive in Harmony. The "classes as
sugar" idea lives in Harmony too, and such classes should be able to
model or even implement the built-in nominal types, I think.
To your point, which I addressed in different terms: we could make
Object.prototype.toString look for [[Class]].[[Constructor]].name
where [[Constructor]] is unnameable-by-script and references the
class's constructor function. This would satisfy Tobie's wish while
avoiding the mutation hazards inherent in using constructor.name.
Either solution wants the function name property to be standard.
On 2009-03-13, at 01:04EDT, Brendan Eich wrote:
To your point, which I addressed in different terms: we could make
Object.prototype.toString look for [[Class]].[[Constructor]].name
where [[Constructor]] is unnameable-by-script and references the
class's constructor function. This would satisfy Tobie's wish while
avoiding the mutation hazards inherent in using constructor.name.
Either solution wants the function name property to be standard.
Can we go one step further? I want (something like)
Object.getOwnConstructor() to return Object.[[Class]].
[[Constructor]]. I want to be able to get a handle on the constructor
of the object, not just the name of that constructor (since in some
cases that name might be 'anonymous' which doesn't help me). Is that
a possibility?
On Mar 13, 2009, at 6:22 AM, P T Withington wrote:
On 2009-03-13, at 01:04EDT, Brendan Eich wrote:
To your point, which I addressed in different terms: we could make
Object.prototype.toString look for [[Class]].[[Constructor]].name
where [[Constructor]] is unnameable-by-script and references the
class's constructor function. This would satisfy Tobie's wish while
avoiding the mutation hazards inherent in using constructor.name.
Either solution wants the function name property to be standard.Can we go one step further? I want (something like)
Object.getOwnConstructor() to return Object.[[Class]]. [[Constructor]]. I want to be able to get a handle on the
constructor of the object, not just the name of that constructor
(since in some cases that name might be 'anonymous' which doesn't
help me). Is that a possibility?
For Harmony, sure. It would be Object.getConstructor, I think, since
there is no connotation of "get property from this object or an object
on its prototype chain", but Allen may have a different thought.
For ES3.1 the time to propose this was last year when the spec was in
flux, freely available for review, and not being frozen for
implementation.
Sure, we could "sneak" Object.getConstructor in, but that is a good
way to be late and bloated if done for N such "easy additions", N
growing quite large as we go. Maybe we can agree to hold the line
"just this once". I'm skeptical; a precedent often takes on a life of
its own.
I don't understand what is being proposed. In ES3.1 terms, what would Object.getConstructor do? [[Class]].[[Constructor]].name makes no sense, since the value of [[Class]] is a string.
On Mar 13, 2009, at 3:04 PM, Mark S. Miller wrote:
I don't understand what is being proposed. In ES3.1 terms, what would Object.getConstructor do? [[Class]].[[Constructor]].name makes no sense, since the value of [[Class]] is a string.
Yes, that sketch was based on an evolved notion of [[Class]],
hypothetical of course.
There is little point in an Object.getConstructorName that is merely a
sweeter and higher-integrity form of
Object.prototype.toString.call(obj).slice(8, -1). Tucker argued for a
way to get the true constructor object from a given instance. With the
'name' property of named functions proposal recently discussed, it's
then trivial to get the original constructor name, for both native and
user-defined functions.
Tobie showed user-defined constructor functions Parent and Child,
arranged with prototype-based inheritance as the names imply. One goal
not met by anything in ES3.1 is to get the name "Parent" or "Child",
as appropriate, given an instance or Parent or Child. Meeting that
goal requires something other than [[Class]] as spec'ed. It also needs
something less configurable and shadowable than the existing
'constructor' property of the constructor function's 'prototype' object.
Thus the idea of a [[Constructor]] internal property was skirted, but
we do not want to specify another mandatory internal property for all
objects (even if it could be optimized away as an induction variable;
the spec should avoid such redundant internal properties).
Wherefore the idea of [[Class]].[[Constructor]] based on making
[[Class]] something other than string-valued.
An obvious extension is to make [[Class]] a String instance, with an
ad-hoc non-configurable 'constructor' property, and everywhere the
spec compares [[Class]] to a string such as "Object", use ToString
or .toString (or even == -- impure! ;-) to get the right comparison
result.
This is all just a thought experiment, and actually doing the work to
spec Object.getConstructor in ES3.1 terms, so that it satisfies the
goal of returning a reference to the native or user-defined
constructor that was invoked to construct obj, might lead to a better
solution.
In any event, this issue is separate from the 'name' property of
function instances, which Allen is deferring from ES3.1, but we're
continuing to hash out for Harmony.
So if, as appears likely, we don't do either 'name' or
'Object.getConstructor' for ES3.1, we may find a better Harmony-
targeted spec formalism and particular solutions couched in it. But I
suspect it's worth working out the minimal solution now in ES3.1 terms.
Does this help?
On Fri, Mar 13, 2009 at 3:29 PM, Brendan Eich <brendan at mozilla.com> wrote:
Does this help?
It helped tremendously, thanks. I agree that we should postpone any such feature -- especially one that would need additional semantic state to explain -- till after ES3.1.
Added Ruby and Python debug string rendering of methods.
Ruby: gist.github.com/78355
Python: gist.github.com/78424
Please let me know if you would like those resources included in the
mailing list of if external references are fine.
Best,
Tobie
On Mar 15, 2009, at 10:13 AM, Tobie Langel wrote:
Added Ruby and Python debug string rendering of methods.
Ruby: gist.github.com/78355
Python: gist.github.com/78424
Please let me know if you would like those resources included in the
mailing list of if external references are fine.
Links work for me. But here is the uneval/toSource equivalent, inline,
using the same notation (single quotes around string results, although
all results are string results, in a one-line comment starting //->
after the repr call):
function repr(v) { print(uneval(v)) }
repr(false) //-> 'false'
repr(true) //-> 'true'
repr(null) //-> 'null'
repr(undefined) //-> '(void 0)'
repr('foo') //-> '"foo"'
repr(123) //-> '123'
repr(123.4) //-> '123.4'
r = new RegExp('foo') repr(r) //-> '/foo/'
repr(new Date) //-> '(new Date(1237141453430))'
repr([0, 1, [2, 3]]) //-> '[0, 1, [2, 3]]'
repr({foo: 123}) //-> '({foo:123})'
a = [1, 2, 3] a.push(a) repr(a) //-> '#1=[1, 2, 3, #1#]'
h = {foo: 123} h.bar = h repr(h) //-> '#1={foo:123, bar:#1#}'
function Foo() { var self = {} self.init = function () { self.foo = 123 self.bar = 456 } return self } repr(Foo()) //-> '({init:(function () {self.foo = 123;self.bar = 456;})})'
repr(Foo)
//-> 'function Foo() {var self = {};self.init = function ()
{self.foo = 123;self.bar = 456;};return self;}'
repr([].reverse) //-> 'function reverse() {[native code]}'
Since undefined was added as a global property after ES1, when there
was too much risk of a web page defining a same-named global, it's
read/write and deletable, so uneval(undefined) produces '(void 0)'.
Note also that unlike toString, the function toSource results squish
code onto one line. This can get ugly, but it was considered
convenient for short functions. Perhaps it was a mistake to do this,
though.
Sharp variables handle cycles and join points but they are overkill
for an inspect-like utility, and they require aggressive memoization
to avoid divergence calling non-idempotent getters or otherwise
dealing with object graphs that modify themselves (via SpiderMonkey's
internal metaprogramming hooks) as they are crawled to find the join
points.
Comments welcome.
On Mar 15, 2009, at 23:30 , Brendan Eich wrote:
But here is the uneval/toSource equivalent, inline, using the same
notation (single quotes around string results, although all results
are string results, in a one-line comment starting //-> after the
repr call):
Thank you. Added it here gist.github.com/81118 for easy
comparison (and syntax coloring :P).
Also, removed the single quotes on the Python examples[1] and cleaned
up the Ruby examples[2] to use p(obj) instead of obj.inspect.
Since undefined was added as a global property after ES1, when there
was too much risk of a web page defining a same-named global, it's
read/write and deletable, so uneval(undefined) produces '(void 0)'.Note also that unlike toString, the function toSource results squish
code onto one line. This can get ugly, but it was considered
convenient for short functions. Perhaps it was a mistake to do this,
though.Sharp variables handle cycles and join points but they are overkill
for an inspect-like utility, and they require aggressive memoization
to avoid divergence calling non-idempotent getters or otherwise
dealing with object graphs that modify themselves (via
SpiderMonkey's internal metaprogramming hooks) as they are crawled
to find the join points.Comments welcome.
From an end-user's perspective, the main difference between
SpiderMonkey's uneval and Python's repr or Ruby's Object#inspect [3]
is that the former looks like a meta-programming tool while both
Python's and Ruby's implementation are just debugging helpers.
I would argue that fulfilling both needs with the same API is not a
good thing. For debugging, the most useful representation of an object
is often domain- or even context-specific and that is not necessarily
it's source code.
I have little knowledge of Python's repr implementation but can
explain briefly how ruby's Object#inspect [4] works.
In Ruby, every object (and in Ruby, nil is an object too) has an
inspect method, which is either defined in the object's class or
inherited from Object#inspect. When not overridden Object#inspect
delegates to the object's to_s [5] method.
Object#to_s returns the object‘s class and an encoding of the object's
id and is overridden for most common types (String, Number, Array,
etc.).
Printing the object's debug representation to the program's standard
output is then as simple as calling Kernel#p [6] with the object
passed as it's first argument:
p(123) # or p 123, brackets are optional
> 123
Since inspect is an instance method, it allows classes to define their
own debugging representation. Thus:
class Foo
def inspect
return 'I am an instance of Foo'
end
end
p(Foo.new)
> I am an instance of Foo
Here's a pseudo-implementation in ES 3 (please bear with the obvious
issues such as breaking for...in loops, etc.). Obviously, naming the
EcmaScript counterpart of Ruby's Object#inspect
Object.prototype.inspect wouldn't fit with the rest of the EcmaScript
API, I would suggest something like Object.prototype.toDebugString().
Object.prototype.toDebugString = function() {
return this.toString();
};
function p(obj) {
if (typeof obj === 'undefined') {
return 'undefined';
}
if (obj === null) {
return 'null';
}
return obj.toDebugString();
}
A program could then specify custom debugging representations like so:
function Foo() {}
Foo.prototype.toDebugString = function() {
return 'I am an instance of Foo';
}
p(new Foo());
//-> I am an instance of Foo
Native objects would need to define their own prototype.toDebugString
methods.
We've been using something similar in Prototype for a long time to
great effect (although we do not extend Object.prototype).
In summary:
- uneval and toDebugString serve two different purposes and should be
treated separately, and - Object.prototype.toDebugString could be roughly mapped to Ruby's
Object#inspect implementation.
This would allow for easy customization of debugging output (which
answers concerns expressed about domain-specificity of serialization)
and avoids complex implementation and specification issues related to
returning an "evaluatable" source representation.
Best,
Tobie
--
[1] Python example: gist.github.com/78424
[2] Ruby example: gist.github.com/78355
[3] in Ruby, the notation Foo#bar means the instance method 'bar' of
class 'Foo'.
[4] Object#inspect doc: www.ruby-doc.org/core/classes/Object.html#M000358
[5] Object#to_s doc: www.ruby-doc.org/core/classes/Object.html#M000357
[6] Kernel#p doc: www.ruby-doc.org/core/classes/Kernel.html#M006002
I've been working on a unit test framework recently.
The lack of a more developer-friendly representation of objects than
the one obtained through their toString method (or by calling
Object.prototype.toString) made me long for an ES equivalent of Ruby's
inspect instance method.
I searched the wiki for a discussion on the topic and couldn't find
anything. The mailing-list search function seems down right now, so no
luck there either.
Has this been discussed before?
Best,
Tobie