graaljs icon indicating copy to clipboard operation
graaljs copied to clipboard

[interop] Unexpected cross-language object printing behavior

Open fniephaus opened this issue 5 years ago • 2 comments

When objects from Python (and possibly other languages) are printed (either directly in the console or via console.log()), the other language's printing behavior is expected. However, Graal.js uses a custom format for printing foreign objects. The same is also true the other way around: JS objects are printed in Python without consulting toString(). This is unexpected behavior from the user's point of view. Maybe the new interop messages (e.g. toDisplayString()) can help to make this more consistent

Repro session

$ polyglot --jvm --shell
GraalVM MultiLanguage Shell 20.1.0
Copyright (c) 2013-2019, Oracle and/or its affiliates
  JavaScript version 20.1.0
  Python version 3.8.2
python> class A:
      +   def __repr__(self):
      +     return "Hello from Python"
      +
python> A()
Hello from Python
python> print(A())
Hello from Python
python> str(A())
'Hello from Python'
python> js>
js> Polyglot.eval("python", "A()")
{} # `Hello from Python` expected
js> console.log(Polyglot.eval("python", "A()"))
{} # `Hello from Python` expected
js> class B { toString() { return "Hello from JS" } }
js> console.log(new B())
Hello from JS
js> python>
python> import polyglot
python> polyglot.eval(language="js", string="new B()")
<foreign '{}'> # `Hello from JS` expected
python> print(polyglot.eval(language="js", string="new B()"))
<foreign object at 0xeabecf5> # `Hello from JS` expected

/cc @timfel @eregon @LeonBein @teresalasarow

fniephaus avatar Jul 08 '20 13:07 fniephaus

As a data point after discussion with @timfel @fniephaus and a few others this is what made sense for Ruby:

  • foreign.to_s returns InteropLibrary#toDisplayString()
  • foreign.inspect tries to format the foreign object as if it was Ruby, notably showing members and their value, without recursion (with extra info to show it's foreign, since inspect is "debugging output")

The current implementation in TruffleRuby is here.

toDisplayString(rubyObject) does rubyObject.inspect (if side effects allowed). Which isn't really consistent with the other direction, but far more useful in practice than calling to_s.

Python's to_s is str()and inspect is repr(). Not sure about JS, does it have only toString()?

One consideration I had (more relevant if a language only has one conversion to string) is if we can't find anything useful to show in inspect then calling toDisplayString might be good. Also played with just always appending toDisplayString() in inspect, but that gets kind of verbose/repetitive, e.g., for an array. "Array" is basically the only clear "interop type" right now, so maybe simply including toDisplayString() except for arrays (which likely don't have much extra info besides the array elements) might be a good compromise.

cc @woess

eregon avatar Jul 09 '20 21:07 eregon

As a note, it's useful for polyglot programmers to both see the foreign's view of an object (to get the full information about the foreign object) and the current language's view of the object (e.g. to find which members are available, integrate better with local objects, etc).

eregon avatar Jul 09 '20 21:07 eregon