robertkrimen / otto

A JavaScript interpreter in Go (golang)
http://godoc.org/github.com/robertkrimen/otto
MIT License
8.11k stars 586 forks source link

user-defined type conversion #199

Open cvillecsteele opened 8 years ago

cvillecsteele commented 8 years ago

In runtime.go, convertCallParameter() does a decent job of doing what it can. But sometimes I need more. I don't want to write silly wrapper functions, say, to convert a string into an io.Writer. Let me provide the runtime with a user-defined conversion function, which convertCallParameter() can fall back to if it can't figure out what to do. #

deoxxa commented 8 years ago

100% agree. I've been wanting to put something like this together for a while. Any ideas as to how you'd expect the interface to look?

Imagine a type like so:

type Magician struct { Card string }
func NewMagician(card string) { return Magician{Card: card} }
func (m Magician) PickACard() string { return m.card }

I'd love it if this could be accessible as new Magician("ace of spades"), and if PickACard could be accessed as pickACard.

And then there are non-struct types:

type Counter int
func (c *Counter) Add(n int) { *c += n }

For this, we probably want valueOf to return the numeric value. And how do we make "Add" available as "add"?

I realise that this isn't exactly what you were talking about, but there are lots of open (and related!) API questions here.

cvillecsteele commented 8 years ago

I really like what you posted above. Thinking out loud here, so caveat emptor. More focused (at least at the moment, given my lack of sleep) on my particular problem, not so much on your more generalized solution...

rt := otto.New();
rt.SetUserCallConverter(func(v Value, t reflect.Type) *reflect.Value {
    // nifty type conversion code here
});

Then at the end of convertCallParameter():

if self.UserCallConverter {
    if result := self.UserCallConverter(v, t); result != nil {
        return *result;
    }
}

But your thinking is mighty interesting. I'd like that too... There's a lot of ceremony right now for accomplishing what you've suggested, and it's really just boilerplate, so I think a good candidate for a "real" Otto API.

cvillecsteele commented 8 years ago

Related... one thing that has been a source of irritation using Otto is that golang does not support package-level introspection at runtime. It can be done statically / with generators, and that approach has been used fairly extensively in some of the golang SDKs I've worked with, but the approach is tiresome (to me at least). I have wanted to be able to point at a golang package (say, filepath) and tell Otto (in effect), "export all that to JS-land". No easy way to do that now.

Reluctantly, I observe that the path of least resistance forward in this area seems to point to some sort of IDL. (Blech.)

(See here: https://groups.google.com/forum/#!topic/golang-nuts/M0ORoEU115o)

gen0cide commented 6 years ago

@cvillecsteele @deoxxa This is something I'd be very interested in as well. I worked around the issue you described in my project (github.com/gen0cide/gscript/compiler) which essentially "links" Go packages into JS land and instruments a custom otto VM with all the glue.

I'd be interested in understanding how we might be able to marry some of these efforts together.

I don't think there's any issue linking pkg.NewMagician("foo") into the runtime as is. My bigger issue was more around instantiation of new object types. I defined a Create() function inside of my runtime with pointers to my NativeType definition which includes a cumbersome, but working Factory to initialize a new object in memory.

REF: https://github.com/gen0cide/gscript/blob/master/engine/native_package.go#L55

Is there a roadmap or sketch around what's next for Otto? Curious how this work might fit in.

Cheers!