
I've been working on this for some month and I think now I'm ready to share the results. http://github.com/sviperll/ghcjs Haskell to Javascript translator ================================ Project aims to provide solution to * compile modern Haskell libraries to Javascript files and use them in Ajax applications or * develop entire Ajax application in Haskell language Building -------- Code builds as standard haskell package $ runghc Setup configure $ runghc Setup build $ runghc Setup install Usage ----- To compile Haskell module to Javascript use `ghcjs` command. $ ghcjs Test.hs This command is merely equivalent to the following $ ghc --make Test.hs but it compiles to Javascript instead of native code. See examples folder for an example of loading and running haskell code from browser. Status ------ The code is in alpha stage. Feel free to experiment with it as you wish. Implementation -------------- Compiler is implemented as [GHC](http://www.haskell.org/ghc/) backend using GHC API. And been tested with GHC 6.12.1. -- Victor Nazarov

On Thu, Oct 21, 2010 at 6:35 AM, Richard O'Keefe
On 21/10/2010, at 12:01 PM, Victor Nazarov wrote:
I've been working on this for some month and I think now I'm ready to share the results.
Given that this is alpha code, what's the performance like?
I don't have any numbers, yet. But can you suggest any benchmarks that can be used to measure performance. Preferably, benchmarks shouldn't use any low level stuff and Haskell IO. -- Victor Nazarov

On 21 October 2010 01:01, Victor Nazarov
I've been working on this for some month and I think now I'm ready to share the results.
This is very cool, as a web developer I have a lot of interest in this. All GHC's extensions is a fantastic incentive to use Haskell with target language of JavaScript. I'm testing it out now. Seems to compile okay with 6.12.3. Don't have time to figure out how to run it right now, but I'll check back later!

On 21-10-10 01:01, Victor Nazarov wrote:
I've been working on this for some month and I think now I'm ready to share the results. Great stuff! I've been looking for something like this for a long time.
If you add "|| transport.status == 0" to line 90 of examples/rts-common.js, it also works on a local file system. I played around with it a bit to see how easy it was to call JavaScript from Haskell, and it turned out to be straightforward. With a little guessing, I constructed a JavaScript representation of a thunk, which evaluates its string argument as a JavaScript expression and returns the resulting string to Haskell. This thunk can be passed to the JavaScript-compiled Haskell function. To pass it around in Haskell and force its evaluation, I constructed a simple monad. Now, on your web page, you can do something like: <input type="text" onkeyup="execHaskell('validate')" id="inputField"/> and in a Haskell module: validate :: JS () validate = do { inputValue <- eval "document.getElementById('inputField').value" ; exec $ "document.getElementById('inputField').style.backgroundColor="++ color inputValue } where color str = if and (map isDigit str) then "'white'" else "'red'" This example creates a text field that turns red if it contains any non-digit characters. It is on-line at http://tryout.oblomov.com/ghcjs/ghcjs.html (Note: I only tested it on Firefox on a Mac) All used files are in a zip file at http://tryout.oblomov.com/ghcjs/ghcjs.zip (validate is in Test.hs, the JS monad in JS.hs, and the JavaScript for execHaskell in util.js) Cheers, Martijn Schrage -- Oblomov Systems

I ran into a problem with line 31 of examples/rts-common.js when running on Chrome:
var word16addCarry = function function(a, b, c) {
Shouldn't that be:
var word16addCarry = function(a, b, c) {
With that change it runs fine for me (GHC 6.12.3). Chrome also wasn't happy loading from the local file system (Cross origin requests are only supported for HTTP), so I just used happstack to test:
import Happstack.Server main = simpleHTTP nullConf (fileServe [] "./")

On Wed, Oct 27, 2010 at 5:25 PM, Ryan Yates
I ran into a problem with line 31 of examples/rts-common.js when running on Chrome:
var word16addCarry = function function(a, b, c) { Shouldn't that be: var word16addCarry = function(a, b, c) {
Yes, that's my fault. I'll fix it.
With that change it runs fine for me (GHC 6.12.3). Chrome also wasn't happy loading from the local file system (Cross origin requests are only supported for HTTP), so I just used happstack to test:
import Happstack.Server main = simpleHTTP nullConf (fileServe [] "./")
I wasn't able to run it in Chrome without dedicated HTTP-server, seems like it is the only way... -- Victor Nazarov

I wasn't able to run it in Chrome without dedicated HTTP-server, seems like it is the only way...
It looks like running Chrome with the flag: -allow-file-access-from-files lets it work. Thanks for making this project, I'm looking forward to more! Ryan

On Wed, Oct 27, 2010 at 4:30 PM, Martijn Schrage
On 21-10-10 01:01, Victor Nazarov wrote:
I've been working on this for some month and I think now I'm ready to share the results.
Great stuff! I've been looking for something like this for a long time.
If you add "|| transport.status == 0" to line 90 of examples/rts-common.js, it also works on a local file system.
Thank you, I'll fix it.
I played around with it a bit to see how easy it was to call JavaScript from Haskell, and it turned out to be straightforward. With a little guessing, I constructed a JavaScript representation of a thunk, which evaluates its string argument as a JavaScript expression and returns the resulting string to Haskell. This thunk can be passed to the JavaScript-compiled Haskell function. To pass it around in Haskell and force its evaluation, I constructed a simple monad.
Very cool. I'll incorporate your changes, If you don't mind. However, I have some minor remarks. You shouldn't override hscall function, or you may break partial application implementation. And you shouldn't overide properties of evalFn, I wonder that this doesn't break your example... var evalFn = new $hs.Func(1); evalFn.evaluate(arg) = function(arg) { var argStr = $hs.fromHaskellString(arg); var res = eval(argStr); return $hs.toHaskellString(arg); // This function should be added to $hs object/namespace }
Now, on your web page, you can do something like:
<input type="text" onkeyup="execHaskell('validate')" id="inputField"/>
and in a Haskell module:
validate :: JS () validate = do { inputValue <- eval "document.getElementById('inputField').value" ; exec $ "document.getElementById('inputField').style.backgroundColor="++ color inputValue } where color str = if and (map isDigit str) then "'white'" else "'red'"
I think we should do something like this: data JsObject = ... -- Should be made abstract and eval :: String -> [JsObject] -> JS JsObject So we can pass around javascript-objects in haskell program and bind them back into javascript calls. And use some conversion functions in case when we need data: jsObjectToString :: JsObject -> JS String jsObjectToInt :: JsObject -> JS Int -- Victor Nazarov

Very cool. I'll incorporate your changes, If you don't mind. Not at all. However, I have some minor remarks. You shouldn't override hscall function, or you may break partial application implementation. And you shouldn't overide properties of evalFn, I wonder that this doesn't break your example... Ah, yes, that was the guessing part. I just hacked around a bit and
On 27-10-10 16:20, Victor Nazarov wrote: thought I'd leave the correct definition to someone who actually knows what they're doing :-)
var evalFn = new $hs.Func(1); evalFn.evaluate(arg) = function(arg) { var argStr = $hs.fromHaskellString(arg); var res = eval(argStr); return $hs.toHaskellString(arg); // This function should be added to $hs object/namespace } This works without any problems (after changing the second line to: "evalFn.evaluate = function(arg) {") I think we should do something like this:
data JsObject = ... -- Should be made abstract
and
eval :: String -> [JsObject] -> JS JsObject
So we can pass around javascript-objects in haskell program and bind them back into javascript calls. And use some conversion functions in case when we need data:
jsObjectToString :: JsObject -> JS String jsObjectToInt :: JsObject -> JS Int Yes, that seems logical. The JsObjects can then be treated similar to IORefs.
What are your plans with the package? In my opinion, this work could be extremely useful for building Ajax apps, and it doesn't seem to be that far from being usable already. Some interesting near-future work I can think of: - Make it work on all major browsers - A faster and more robust module loader (now it loses a lot of time on 404 errors, trying to access modules in the wrong package dir) - Basic type checking for the top-level Haskell functions - Marshaling between Haskell and JavaScript values - JQuery support - Nice ways to build JavaScript (and JQuery) expressions in Haskell - Support more libraries and packages (Parsec would be interesting) I can find some spare time to work on this. I'm sure there will be others as well. Cheers, Martijn

On Wed, Oct 27, 2010 at 7:46 PM, Martijn Schrage
On 27-10-10 16:20, Victor Nazarov wrote:
Very cool. I'll incorporate your changes, If you don't mind.
Not at all.
However, I have some minor remarks. You shouldn't override hscall function, or you may break partial application implementation. And you shouldn't overide properties of evalFn, I wonder that this doesn't break your example...
Ah, yes, that was the guessing part. I just hacked around a bit and thought I'd leave the correct definition to someone who actually knows what they're doing :-)
var evalFn = new $hs.Func(1); evalFn.evaluate(arg) = function(arg) { var argStr = $hs.fromHaskellString(arg); var res = eval(argStr); return $hs.toHaskellString(arg); // This function should be added to $hs object/namespace }
This works without any problems (after changing the second line to: "evalFn.evaluate = function(arg) {")
I think we should do something like this:
data JsObject = ... -- Should be made abstract
and
eval :: String -> [JsObject] -> JS JsObject
So we can pass around javascript-objects in haskell program and bind them back into javascript calls. And use some conversion functions in case when we need data:
jsObjectToString :: JsObject -> JS String jsObjectToInt :: JsObject -> JS Int
Yes, that seems logical. The JsObjects can then be treated similar to IORefs.
What are your plans with the package? In my opinion, this work could be extremely useful for building Ajax apps, and it doesn't seem to be that far from being usable already.
Some interesting near-future work I can think of:
- Make it work on all major browsers This shouldn't be a problem...
- A faster and more robust module loader (now it loses a lot of time on 404 errors, trying to access modules in the wrong package dir) I agree that this is important. I'll look into it...
- Basic type checking for the top-level Haskell functions I think this should be implemented via FFI export implementation.
- Marshaling between Haskell and JavaScript values Again It's mostly implementation of Haskell FFI
- JQuery support - Nice ways to build JavaScript (and JQuery) expressions in Haskell Dmitry Golubovski had a code generator from WebIDL wich he used to support HTML5 DOM in YHC.
- Support more libraries and packages (Parsec would be interesting)
I think Parsec should work right now out of the box. But I don't have time to investigate it...
I can find some spare time to work on this. I'm sure there will be others as well.
I want to make code generator more smart and generate more optimized code. To archive this I need to rewrite it in Monad or ApplicativeFunctor style. I need Reader monad there to pass some environment. I think ghc-packages support is rather trivial to add. So module loader will always know the one true location of module. I want to implement continuation passing style calling convention for Haskell-values as an alternative to two existing ones: plain and trampoline. This will even allow multithreading emulation (with setTimer as a scheduling mechanism...) And last, I want to dig into FFI implementation. I think GHC doesn't allow extensible FFI. So you won't be able to write "javascript" calling convention in Haskell source file. But I think we can overcome this using "ccall" for example... I don't know what tasks are of most priority. I do what I feel like doing :) I'll be very interested to see what other people can do with all this. And I think that we should switch to application driven development some time with something like Yampa-based game running in web-browser... -- Victor Nazarov

On 27 October 2010 13:30, Martijn Schrage
On 21-10-10 01:01, Victor Nazarov wrote:
This example creates a text field that turns red if it contains any
non-digit characters. It is on-line at http://tryout.oblomov.com/ghcjs/ghcjs.html (Note: I only tested it on Firefox on a Mac)
All used files are in a zip file at http://tryout.oblomov.com/ghcjs/ghcjs.zip (validate is in Test.hs, the JS monad in JS.hs, and the JavaScript for execHaskell in util.js)
What browser are you using, IE8, IE9, FF and Chrome on Windows throw up errors, both locally and to your above code. Aaron

On 29-10-10 22:20, Aaron Gray wrote:
On 27 October 2010 13:30, Martijn Schrage
mailto:martijn@oblomov.com> wrote: On 21-10-10 01:01, Victor Nazarov wrote:
This example creates a text field that turns red if it contains any non-digit characters. It is on-line at http://tryout.oblomov.com/ghcjs/ghcjs.html (Note: I only tested it on Firefox on a Mac)
All used files are in a zip file at http://tryout.oblomov.com/ghcjs/ghcjs.zip (validate is in Test.hs, the JS monad in JS.hs, and the JavaScript for execHaskell in util.js)
What browser are you using, IE8, IE9, FF and Chrome on Windows throw up errors, both locally and to your above code. That's weird, since http://tryout.oblomov.com/ghcjs/ghcjs.html works fine for me with Firefox both on Mac and Windows (XP & 7). There is only a small bug that causes a key event to get lost when the Haskell JavaScript modules are loaded.
What error messages do you get from Firefox? -- Martijn
participants (6)
-
Aaron Gray
-
Christopher Done
-
Martijn Schrage
-
Richard O'Keefe
-
Ryan Yates
-
Victor Nazarov