Giter Club home page Giter Club logo

Comments (35)

jdfreder avatar jdfreder commented on September 28, 2024

+1

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

I need to experiment further with this idea. The main "con" is that:

> Object.getOwnPropertyNames(util.inspect)
[ 'length',
  'name',
  'arguments',
  'caller',
  'prototype',
  'colors',
  'styles' ]

In the meantime, similar, experimental functionality is already available through $$mimer$$. See:

http://n-riesco.github.io/ijavascript/doc/mimer.ipynb.html

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

inspect is something you define on your own objects though that works in Chrome too, you don't have to use Node's util.inspect.

I must be missing what the con is.

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024
> var obj = { inspect: function(){}}
undefined
> Object.getOwnPropertyNames(obj.inspect)
[ 'length',
  'name',
  'arguments',
  'caller',
  'prototype' ]

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Ah, ok, that makes sense. How do you define these in a way that is natural for a JS programmer? I'm assuming we want these to be bound by the caller and I'm not sure how you do it for nested functions.

function X() {}
X.prototype.inspect.mime = {
  'text/html': function html() {
    // How does this get bound to the future `this`?
  }
}

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024
> X.prototype.inspect.mime['text/html'] = function html(){ return `<b>${this.x}</b>`}
[Function: html]
> new X(12)
ITS AN X: 12
> y = new X(12)
ITS AN X: 12
> y.inspect.mime['text/html']
[Function: html]
> y.inspect.mime['text/html']()
'<b>undefined</b>'

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

@rgbkrk How urgent is this issue for you? I was working on #58 , but if we agree on what to do for #59 , I could work on it right away.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

This is not urgent, working on #58 is great. @jdfreder was interested in the JavaScript kernel and since I saw the thread going along I wanted to experiment.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Currently there's a compare and contrast with tonic dev and IJavascript in Plotly's plotly-notebook-js and I'd like to make IJavascript take off in the same way that IPython did via libraries like pandas.

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

Here's an alternative using a getter:

function X(x) {
    this.x = x;
}

X.prototype.inspect = function inspect() {
    return "X = " + this.x;
};

X.prototype.inspect.mime = {
    get "text/html"() {
        return "<b>X = " + this.x + "</b>";
    },
};

var x = new X(1);

x.inspect.mime["text/html"];  // output: '<b>X = undefined</b>'

The main con of using a getter is that we would give up passing arguments. For example, obj.inspect.mime["image/jpg"]("lossless") (admittedly, not a terribly good example).

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

In Python at least, we never pass arguments to the _repr_html_. It's supposed to be a function that takes nothing and returns a string that we can send over the wire.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Uhhhh, appears that getter doesn't work for us since it comes out undefined. Tested locally as well.

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

Oh well... using X.prototype.inspect.mime has an undesired side-effect. this gets assigned to X.prototype.inspect.mime instead of the X instance:

function X(x) {
    this.x = x;
}

X.prototype.inspect = function inspect() {
    return "X = " + this.x;
}

X.prototype.inspect.mime = {};

Object.defineProperties(X.prototype.inspect.mime, {
    "text/html": { get: function html() {
        return this === X.prototype.inspect.mime;
        },
    },
});

var x = new X(1);
x;  // output: X = 1
Object.getOwnPropertyNames(x.inspect.mime); // output: [ 'text/html' ]
x.inspect.mime["text/html"];  // output: true

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

Something like the following would be more in the spirit of the global $$html$$:

function X(x) {
    this.x = x;
}

X.prototype.inspect = function inspect() {
    return "X = " + this.x;
}

Object.defineProperty(X.prototype, "$$html$$", {
    get: function html() {
        return "<b>" + this.inspect() + "</b>";
    },
});

var x = new X(1);
x;  // output: X = 1
x.$$html$$;  // output:<b>X = 1</b>

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Considering how you have to send these across the wire, what if the interface is just:

X.prototype.inspectAsMimeBundle = function inspectAsMimeBundle() {
  return {
    'text/html': `<b>${this.x}</b>`,
    'text/plain': this.inspect(),
  }
}

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Alternatively, or in addition to (since these are for your own use in calling from ijavascript) they can implement inspectAsMimetype:

X.prototype.inspectAsMimetype = function inspectAsMimetype(mimetype) {
  switch(mimetype) {
    case 'text/html':
      return `<b>X = ${this.x}</b>`
    case 'text/plain':
    default:
      return this.inspect();
  }
}

var x = new X(1);
console.log(x.inspectAsMimetype('text/html')); 

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

In IJavascript, we also have the global $$mime$$:

Object.defineProperty(X.prototype, "$$mime$$", {
    get: function mime() {
        return {
            "text/plain": this.inspect(),
            "text/html": this.$$html$$,
        };
    },
});

x.$$mime$$;  //output: { 'text/plain': 'X = 1', 'text/html': '<b>X = 1</b>' }

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

The main reason I'm aiming for something similar to inspect is that it's a built in. My big repulsion to the global $$html$$ is that it's on the user to run (or be detected by a library) and I wouldn't know how to use it in an asynchronous context.

from ijavascript.

jdfreder avatar jdfreder commented on September 28, 2024

Alternatively we could model the API closer to IPython's. The way this is done in IPython isvia reprs. Python itself uses x.__repr__ for the string representation of an object. IPython extends this by also looking for x._repr_*_, i.e. x._repr_html_...

In Javascript the equivalent of __repr__ is toString, right? So why not toHTML, toJavascript etc. Or toMimeBundle?

from ijavascript.

jdfreder avatar jdfreder commented on September 28, 2024
function A() {}
A.prototype.toString = function() { return 'b'; }
a = new A();
String(a)
"b"

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

At least in node and v8 (which are what this kernel is), inspect is the equivalent to __repr__ (the analogue to toString is __str__). inspect has been around at least as far as 0.8 (was easy to launch a docker image with it):

$ docker run -it node:0.8
> X = function(x) { this.x = x; }
[Function]
> X.prototype.inspect = function inspect(){ return 'x = ' + this.x; }
[Function: inspect]
> trial = new X(23)
x = 23
> trial
x = 23

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

I've put together a document with all the display conventions mentioned here. I think it'll help understand the current conventions in IJavascript:

Display Conventions  
1. Javascript `toString()`  
2. Node.js `inspect()`  
3. Python `__repr__()`  
4. IPython `__repr_*__()`  
5. IJavascript  
5.1. Using `util.inspect()` (except for functions)  
5.2. Using globals: `$$mime$$`, `$$html$$`, ...  
5.3. Extensions:  
5.3.1. Using global `$$mimer$$()`  
5.3.2. Using Node.js `inspect()`  
5.3.3. Using IPython's `__repr_*__()`  
5.3.4. Using getters: `$$mime$$`, `$$html$$`, ...  

Display Conventions

Javascript toString()

In Javascript, a class can define its string representation by defining the method toString().

$ node
> function F() {}
undefined
> F.prototype.toString = function toString() { return "World!"; };
[Function: toString]
> "Hello, " + new F()
'Hello, World!'

See documentation here.

Node.js inspect()

Node.js introduced another convention. The string representation of an object in a Node.js shell is determined by the method inspect([depth]).

$ node
> function F() {}
undefined
> F.prototype.inspect = function inspect() { return "Hello, World!"; };
[Function: inspect]
> new F()
Hello, World!

See documentation here.

Python __repr__()

Similarly to Javascript, Python uses the method __repr__() to determine the string representation of an object.

See documentation here.

IPython __repr_*__()

And similarly to Node.js, the IPython shell introduces the methods __repr_*__() to define rich representations of an object.

See documentation here.

IJavascript

The sections below describe how IJavascript displays objects, and discuss potential extensions.

Using util.inspect() (except for functions)

With the exception of functions, IJavascript displays objects like Node.js does; i.e. by invoking util.inspect(), which, in turn, invokes the method inspect() of the object.

Functions, however, are displayed using the method toString(). See the difference:

$ node
> function f() { return "Hello, World!"; }
undefined
> f.toString()
'function f() { return "Hello, World!"; }'
> util.inspect(f)
'[Function: f]'

Thus, in IJavascript, it is possible to define an alternative string representation of an object by defining the method inspect() (or the method toString() in case of functions).

Using globals: $$mime$$, $$html$$, ...

The Jupyter notebook accepts object representations in a number of MIME formats. To produce these MIME representations, IJavascript defines a convention in terms of the global variables: $$html$$, $$svg$$, $$png$$, $$jpeg$$ and $$mime$$.

See documentation here.

The choice of names was guided by:

  • convenience: use of short names
  • avoid collisions with global variables defined by other libraries: mangle names with $$

Extensions:

In this thread, we're discussing new conventions so that it is possible to customise an object representation based on its class.

Using global $$mimer$$

The global $$mimer$$ was experimentally introduced in [email protected].

$$mimer$$ is the function that IJavascript uses to generate the MIME representation of an object. By making this function accessible, a library can redefine $$mimer$$ to produce a custom representation.

See here for some examples of use.

I want to deprecate this convention because of its cons:

  • it isn't robust: a buggy custom $$mimer$$ can break the custom $$mimer$$ of other packages
  • it is inefficient: it creates a chain of $$mimer$$, each checking whether an object is an instance of a given class.

Using Node.js inspect()

All the ideas we've discussed so far that extend the inspect() convention have serious drawbacks:

  • X.prototype.inspect["text/html"]:
    • this is set to X.prototype.inspect
    • by default X.prototype.inspect defines property names other than "text/html"
  • X.prototype.mime["text/html"]
    • this is set to X.prototype.inspect

Using getters: $$mime$$, $$html$$, ...

From the experience with X.prototype.inspect["text/html"] and X.prototype.mime["text/html"], it is clear that for this to be properly defined a method or a getter must be used to generate a rich representation of an object. Once this is agreed, the issue becomes a naming issue. The first choice that came to my mind was to use the names already used in the global variables. The pros are:

  • this isn't a new convention; it's already used in the global variables.
  • convenience: use of short names
  • avoid naming collisions

Using IPython __repr_*__()

Jonathan suggested an alternative naming convention, inspired by IPython. The advantage I see is that:

  • it is a convention familiar to IPython/Jupyter developers

The cons are:

  • that to be consistent, we should rename the global variables too
  • the names are long

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Thank you so much for putting this together @n-riesco, this helped me understand the whole picture quite a bit more.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

The major con to me of using _repr_*_ is that it should appeal to and be idiomatic for the kernel's language.

It's worth pointing out that the global variables do have an analogue to IPython as well, IPython.display.display(X) and helpers like IPython.display.HTML('<b>test</b>').

from ijavascript.

mdtusz avatar mdtusz commented on September 28, 2024

This may be a silly question, but what's the issue with just using a prototype method that takes an argument?

$ node
> function X(n){
    this.y = n; return this; 
  }
> X.prototype.inspectMime = function(t){ 
    var types = {
      'text/html': '<h1>' + this.y + '</h1>'
    };
    return types[t];
  }
> var x = new X(100);
undefined
> x
X { y: 100 }
> x.inspectMime('text/html')
<h1>100</h1>

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

I like inspectMime

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

@mdtusz Kyle suggested something very similar here.

I feel wary of using unmangled names like inspectAs*(). inspect() is fine because it's a Node.js convention.

I think it's easier for IJavascript to introduce a convention with mangled names, that are unlikely to clash with other naming conventions.

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

I'm preparing a new release in the async-stdio branch, here and here.

Apart from improving the handling of asynchronous output, it also includes an initial implementation of a convention to fix this issue: Class.prototype.inspect, Class.prototype._toMime, Class.prototype._toHtml, Class.prototype._toSvg, Class.prototype._toPng and Class.prototype._toJpg.

Here's an example:

In[1]:
function F(f) {
    this.f = f;
}

F.prototype.inspect = function inspect() {
    return "F { f: " + this.f + " }";
}

F.prototype._toHtml = function toHtml() {
    return "<div style='background-color:yellow'>" + this.inspect() + "</div>";
}

new F("Hello, World!");

Out[1]:
{ mime: 
   { 'text/plain': 'F { f: Hello, World! }',
     'text/html': '<div style=\'background-color:yellow\'>F { f: Hello, World! }</div>' } }

from ijavascript.

mdtusz avatar mdtusz commented on September 28, 2024

Nice!

So to clarify, one of the mime properties will be displayed as output then - presumably defaulting to text/html if provided?

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

@mdtusz My understanding is that the frontend chooses amongst all the available. For example, the notebook prefers text/html over text/plain.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

The frontend chooses once it gets the display_data or execute_reply message.

Example from Pandas:

screenshot 2016-03-04 13 39 24

The raw messages that the kernel (JavaScript in this case) sends over look like this:

{ 
  content:
   { data:
      { 'text/plain': '                   A         B         C         D\n2000-01-01  1.153881 -0.462802 -1.304602 -0.112967\n2000-01-02  2.041548  0.193145 -2.154538 -0.026075\n2000-01-03  2.277393 -0.990363 -1.676727  0.463335\n2000-01-04  2.472088  0.376239 -2.691538 -0.329923\n2000-01-05  3.426450 -0.052412 -3.275203 -1.422521',
        'text/html': '<div style="max-height:1000px;max-width:1500px;overflow:auto;">\n<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>A</th>\n      <th>B</th>\n      <th>C</th>\n      <th>D</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>2000-01-01</th>\n      <td> 1.153881</td>\n      <td>-0.462802</td>\n      <td>-1.304602</td>\n      <td>-0.112967</td>\n    </tr>\n    <tr>\n      <th>2000-01-02</th>\n      <td> 2.041548</td>\n      <td> 0.193145</td>\n      <td>-2.154538</td>\n      <td>-0.026075</td>\n    </tr>\n    <tr>\n      <th>2000-01-03</th>\n      <td> 2.277393</td>\n      <td>-0.990363</td>\n      <td>-1.676727</td>\n      <td> 0.463335</td>\n    </tr>\n    <tr>\n      <th>2000-01-04</th>\n      <td> 2.472088</td>\n      <td> 0.376239</td>\n      <td>-2.691538</td>\n      <td>-0.329923</td>\n    </tr>\n    <tr>\n      <th>2000-01-05</th>\n      <td> 3.426450</td>\n      <td>-0.052412</td>\n      <td>-3.275203</td>\n      <td>-1.422521</td>\n    </tr>\n  </tbody>\n</table>\n</div>' },
     execution_count: 21,
     metadata: {} },
  header:
   { username: 'kyle6475',
     msg_id: 'e697b50a-9aa0-4d7a-a825-27ac99479679',
     msg_type: 'execute_result',
     version: '5.0',
     date: '2015-07-26T15:52:59.473133',
     session: 'c297c1c0-89bb-4ded-9720-d298c33b2595' },
  parentHeader:
   { username: 'kyle6475',
     msg_id: '841e2314-0af6-46c8-8083-d4b544f91e0e',
     version: '5.0',
     session: 'b4e75019-83e8-4bd4-99f5-e462d5203886',
     date: '2015-07-26T15:52:59.468333',
     msg_type: 'execute_request' },
  metadata: {},
  signature: '6c41e0ecbbb50010c26d8e04e8ff2c634816761f610b30ab1765539c216be591',
  blobs: []
}

and a frontend will choose how to wrap those up accordingly

screenshot 2016-03-04 13 41 02

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

I've made a pre-release with the changes in the async-stdio branch.

I will keep this issue open until I make the release.

If you have any more feedback that I can address before the release, please, keep posting here.

from ijavascript.

n-riesco avatar n-riesco commented on September 28, 2024

I've just released [email protected], which uses [email protected], and fixes this issue.

from ijavascript.

rgbkrk avatar rgbkrk commented on September 28, 2024

Awesome, I look forward to installing and using this.

from ijavascript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.