Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js interfacing via API #194

Open
fuzzthink opened this issue Jun 25, 2014 · 18 comments

Comments

@fuzzthink
Copy link

commented Jun 25, 2014

It seems the examples in ghcjs-examples does everything in haskell. I'd like use ghcjs so I can write the "core" logic in haskell and leave the frontend stuff in js. How do I go about exposing a haskell module so it's accessible globally in js? How can I call a function say that accepts a list (or json) in this exposed module?

Some simple sample code would be much appreciated.

thanks.
ps. http://weblog.luite.com/wordpress/?p=14 seems to be down.

@luite

This comment has been minimized.

Copy link
Member

commented Jun 26, 2014

This is one of the more frequently asked questions, and I'm afraid there is no perfect answer yet. What I tend to say is use syncCallback / asyncCallback (in GHCJS.Foreign from the ghcjs-base package) to get your JavaScript functions to run your Haskell code. Then use the normal javascript FFI to pass them to the outside JS world. Your main action would then just do these exports.

Unfortunately this is far from ideal, in particular if you build your app as a library. I wonder if something better should be supported before we release GHCJS on hackage. foreign export javascript is mostly complete, the exports are recorded in the object (js_o) files, but nothing is ever done with them.

If you have ideas on what you want to do and how GHCJS should work ideally, input is appreciated. Perhaps you have some ideas of what you'd like to write, whether you need polymorphic types, higher order functions. Some considerations:

  • polymorphic types are tricky to support, for example Ord k => k -> v -> Map k v -> Map k v requires an extra argument for the Ord k dictionary and it's impossible in general to tell from the values whether we have the correct k and v
  • calling functions with standard FFI types and (automatically) converting values using fromJSRef / toJSRef should be doable, but something like (Int -> Bool) -> IO () is already harder (doesn't look completely impossible, but definitely nontrivial)
  • since Haskell can be run in synchronous and asynchronous mode, we'd probably get two calling conventions
  • everything probably needs to be done at link time, since the linker collects all required symbols. exposing a module means adding all exported names to the roots and then collecting all dependencies

What we could to is add command line flags similar to --expose-js=main:Data.MyData, --expose-all-js=main:Control.X to the compiler (with the former creating wrappers for all foreign export javascript in the module and the latter wrapping all exported names with a supported type)

@fuzzthink

This comment has been minimized.

Copy link
Author

commented Jun 26, 2014

Let's list out the typical use cases for a haskell -> js (h2j) solution are:

  1. Use h2j for entire frontend (or even backend)
  2. Use h2j as a standalone element where little to no communication is needed to rest of app. Think of it as using Flash.
  3. Use h2j as a js library.

You'll need to think what is the market for each of these camps and how ghcjs solves their problem better than alternatives.

For me, I am starting to write an app that is cpu intensive and be able to reuse most of the code for both the web and mobile app. For the mobile app side, it needs to perform better than the web version, so it has to be native. Thus I'm using haskell since there's js support and there seems to be native support for ios, android, and it's not one of c++,java,c#,obj-c.

For web app side, I can go with either case 2 or 3 above. Perhaps case 2 is easier when porting it to mobile, but I'm going with case 3 since it's a better solution for the web app for me. So I want my haskell code to be a library and not dealing with the DOM since the DOM ecosystem has much better solutions as in virtual DOM management via React or mithril. It seems solutions like Haste and Fay will serve my use case better since Fay seem to handle most of ghc, has easy to-js interface and produce much smaller js code (68K vs. 900K ghcjs, both uncompressed).

But since ghcjs' aim is to be 100% ghc, maybe the popular use cases are for porting existing haskell code base, which is more of case 1 and 2. So perhaps you needn't worry about case 3 since maybe they are better served with Haste and Fay.

just my 2 cents, thanks again for the help.

@luite

This comment has been minimized.

Copy link
Member

commented Jun 27, 2014

But I do consider calling into Haskell from JavaScript an important use case. In fact my original motivation to start working on GHCJS was such a scenario, I wanted to be able to demonstrate working Haskell code in things like blog posts. Since I've written most of the RTS, it's not hard to do it with the current code for me, but I haven't given it the attention it requires to be a proper end user feature.

So I'm not really looking for what to support or not, but rather for what's the right thing to do when supporting this. I think the main consequence of trying to strive for compatibility with the current Haskell /hackage ecosystem is that we tend to be somewhat conservative with choices and that sometimes things move a little slower due to more situations to support.

For example I'm not too keen on something like Fay's ffi ..., since it introduces new syntax (ffi is not really a function, it can only be applied to a string literal), and i think the runtime marshalling is too brittle, but that doesn't mean that I think that the current foreign import javascript syntax is the final answer. Some of the patches we sent to GHC HQ (included in GHC 7.8) were specifically to make desugaring and typechecking of foreign declarations more flexible, so that we can adapt where necessary.

Code size is a different issue that we'll revisit after #137 is done, but that issue got pushed back a little since more pressing issues for our first hackage release came up.

@luite

This comment has been minimized.

Copy link
Member

commented Jul 11, 2014

After some discussion on IRC, this looks like a reasonable way forward:

  • always include all foreign export javascript for every package in the dependencies, all of the exported functions have a call wrapper that can run both sync and async code.
  • add a command line option to ghcjs to load a file similar to rtsdeps.yaml to get extra symbols to be linked and allow linking a library this way (or to include extra symbols in an executable that does have a main)
  • extend the rtsdeps.yaml format a bit to make it a bit more convenient:
    • wildcard exports
    • an option to have GHCJS generate call wrappers for all or specific symbols
    • for polymorphic functions with a context a way to export a specialized export

both ways should work with the module pattern, where all generated JS code is wapped in a function and all exported functions are assigned to an exports variable.

Automatic marshalling would still be a bit tricky. Ideally something like a FromJSRef a instance should allow a as an argument type for an exported function, but I'm not sure if we have all the required type information, since it's essentially introducing constraints after desugaring. Another approach would be to generate a new Exports module that imports all the to-be-exported things, adding the constraints.

All of the above still exposes only monomorphic code to JS, which can be a tad inconvenient. Perhaps we can also provide a simple way to generate a dispatcher based on the type or value of the arguments.

Also all values mentioned here are JS values, marshalled through either some static marshalling mechanism or FromJSRef / ToJSRef, but it can be useful to get a JavaScript reference to some wrapped Haskell heap value instead. It should be opaque from a JS point of view, but possible to pass as an argument to exported functions, so you can pass Haskell functions, infinite lists etc. This probably means further extending the rtsdeps.yaml file format to allow specifying whether to wrap or marshal an argument or return value. To make this reasonably safe, we should add a Typeable constraint and make the exported value an existential, then check the TypeRep signature when unwrapping.

@luite luite added the feature label Jul 16, 2014
@AnneTheAgile

This comment has been minimized.

Copy link

commented Sep 28, 2014

+1 for the whole project and this ticket is great icing :)

@aspidites

This comment has been minimized.

Copy link

commented Jan 25, 2015

@luite has any progress been made on this issue? If not, is there a working example of the (a)syncCallback approach?

@luite

This comment has been minimized.

Copy link
Member

commented Jan 25, 2015

Lots of progress under the hood, but unfortunately not quite finished, so I can't show an example just yet. Some of the recent changes:

  • GHCJS now links .js_a archives rather than separate .js_o objects for the libraries it's building. These archives allow the compiler to find exported symbols and their dependencies for libraries.
  • When compiled with GHC 7.10, GHCJS supports the StaticPointers extension for Cloud Haskell. The compiler builds a table of exported static values when it links a program. These can be used for remote calls, but with a bit more glue code, they can also be used as a lightweight foreign export alternative.
  • The foreign function interface now supports passing lifted values with the Any type and returning unboxed tuples with the javascript calling convention. This makes implementing low level JS interaction much more convenient (but this is mostly usable for low level library authors)

I'm currently working on a major ghcjs-base update, which simplifies the asyncCallback API and adds a way to export arbitrary values dynamically to JS. For static exports, the architectural changes are already in, it just needs some user interface.

@geraldus

This comment has been minimized.

Copy link

commented Jan 25, 2015

👍

@noteed

This comment has been minimized.

Copy link

commented Feb 3, 2015

@luite Is there any example code for the very first paragraph of your very first comment ? Something similar to Haste's export function ?

@ababkin

This comment has been minimized.

Copy link

commented May 18, 2015

Also interested to see some examples, even minimal

@zcstarr

This comment has been minimized.

Copy link

commented Jul 20, 2015

I'm also stoked to see some examples of this , how far along is this ghcbase update ? Awesome work so far btw!

@jhegedus42

This comment has been minimized.

Copy link

commented Jul 30, 2015

i think this example does call haskell from javascript https://github.com/ghcjs/ghcjs-examples/blob/master/weblog/mouse/mouse.hs since it is reacting to user input (mouse movement)

@jhegedus42

This comment has been minimized.

@jhegedus42

This comment has been minimized.

Copy link

commented Jul 30, 2015

let handler ev = do
x <- pageX ev
y <- pageY ev
sync $ push (x,y)
on handler "mousemove" def =<< selectElement elem

in mouse.hs

on is called that is where js calls into haskell

@jhegedus42

This comment has been minimized.

@aspidites

This comment has been minimized.

Copy link

commented Jul 30, 2015

Thanks @jhegedus42 , I"ll be looking at those. BTW, you can wrap your code in triple backticks to get highlighting.

@mkeeter

This comment has been minimized.

Copy link

commented Oct 13, 2015

Has there been any progress on this feature? The StackOverflow example above doesn't work anymore.

At first, it failed because GHCJS couldn't find setProp. Once I added an import ofJavaScript.Object.Internal; it continued not to work because the API is different from what it was in that example.

@mgsloan

This comment has been minimized.

Copy link
Contributor

commented Oct 13, 2015

Joachim Breitner's interactive proof game is a good example of how to do this:

https://github.com/nomeata/incredible/blob/master/logic/js/JSInterface.hs
https://github.com/nomeata/incredible/blob/master/webui/webui.js

But I'm pretty sure that also uses old-base. Something similar should work with the current master version (a.k.a. improved-base), but you'll need to figure out the APIs. Should involve GHCJS.Foreign.Callback and possibly JavaScript.Object

So yeah, either use old-base to run these examples or figure out how to do it on improved-base.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.