Object.prototype.inspect ?

Tobie Langel tobie.langel at gmail.com
Wed Mar 18 09:47:36 PDT 2009


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 http://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: http://gist.github.com/78424
[2] Ruby example: http://gist.github.com/78355
[3] in Ruby, the notation Foo#bar means the instance method 'bar' of  
class 'Foo'.
[4] Object#inspect doc: http://www.ruby-doc.org/core/classes/Object.html#M000358
[5] Object#to_s doc: http://www.ruby-doc.org/core/classes/Object.html#M000357
[6] Kernel#p doc: http://www.ruby-doc.org/core/classes/Kernel.html#M006002



More information about the Es-discuss mailing list