-->

Tuesday, July 10, 2012

Hello, World

The first thing we need on our journey is a compiler that translates out haskell to javascript. On this page: The JavaScript Problem you can find a list of compilers that are able to do that. I will focus on UHC and haste. GHCJS also seems promising, and I might try it later.

So we want to write a little hello world program. The equivalent in javascript would be

document.write("Hello, World!");

which replaces the contents of the document with "Hello, World!". Let us get started.

UHC

The Utrecht Haskell Compiler has a backend allowing to compile haskell to javascript. This page links all the information about the JavaScript backend: UHC-JS

Installing UHC

First we have to install UHC. You can find the code for UHC here UHC GitHub. The build instructions can be found in the EHC sub directory.

But first some dependencies are needed. On ubuntu linux I install them with apt-get:

apt-get install ghc cabal-install build-essentials libtool uuagc

We also need to install a few haskell packages via cabal:

cabal update
cabal install network uulib syb fgl

To install UHC, first clone the repository and change into the EHC directory. Then build and install UHC.

git clone git://github.com/UU-ComputerScience/uhc.git
cd uhc/EHC
./configure
make
sudo make install

In blogs it is common to suggesting getting a cup of coffee at a moment like this, because the make command may take a while. So get a cup of coffee!

If everthing worked out, UHC should be installed and you can compile to javascript via

uhc -tjs Main.hs

(Or however your haskell file is called).

Hello World with UHC

We need to do two things:

  • Get the document.
  • Call write on the document with "Hello, World!" as parameter.

The FFI (ForeignFunctionInterface) of the js backend of UHC is described here Improving UHC js For our purposes, it works like this:

foreign import js "jscommand" haskellName :: Type

Where "jscommand" is the command in javascript, "haskellName" the name of the haskell function we want to define and "Type" the type of the haskell function. "jscommand" may contain "%N" where N is a number refering to the N-th parameter of the haskell function.

So to get the document we first define a type for it and then import a corresponding javascript command

data Document
foreign import js "document" getDocument :: IO Document

To get the document we just have to call "document" in javascript. This returns us the document in the IO monad. The document is in the IO monad because we a calling a foreign function which might have side effects.

Now we want to call "write" on a document.

foreign import js "%1.write(%2)" write :: Document -> JSString -> IO ()

Because the "%1" is in front of the ".write", the first argument to haskell function "write" (which is the document) is the object write is called on (this can all be found in Improving UHC js.

Note that the second argument is of type JSString and not of String. This is because a string in haskell is not the same as a string in javascript. We have to convert a haskell string to a javascript string

type JSString = PackedString
foreign import prim "primStringToPackedString" toJS :: String -> JSString

Now we are ready to write our hello world:

type JSString = PackedString
foreign import prim "primStringToPackedString" toJS :: String -> JSString

data Document
foreign import js "document" getDocument :: IO Document
foreign import js "%1.write(%2)" write :: Document -> JSString -> IO ()

main = do
  doc <- getDocument
  write doc $ toJS "Hello, World!"

Haste

Installing haste

Haste can be found here: Haste GitHub. Instructions for installation can also be found on that page (just read the Building section). It requires installation of fursuit, which can be found here: Fursuit GitHub.

Hello World with haste

Again, we need a way to import a function to get the document and to write into the contents of the document. Information on how to import javascript functions can be found in the doc subdirectory of the haste repository.

Update: The way FFI functions have to writting with haste has changed since the blog post has original been written. At that time returning values from javascript to haskell was a little bit more cumbersome. I have updated this blog post to reflect the new way of doing it. I hope I did not forget something in the process. So if you find an error, please comment.

Haste is not as flexible as UHC when importing JavaScript functions. It does not allow placing the parameters of the haskell function in the javascript code with "%N". It also does not allow the custom type "Document" to be used as a parameter or return type. Instead "JSAny" must be used.

So we create a file "helpers.hs" with out Javascript helper functions:

function objWrite(obj, text) {
    obj.write(text); // Call the method
    return; // Return ()
}

function getDocument() {
    return document;
}

This now allows us to write hello world in Haste:

import Haste
import Haste.Prim

type Document = JSAny

foreign import ccall "objWrite" write :: Document -> JSString -> IO ()
foreign import ccall "getDocument" getDocument :: IO Document

main = do
  doc <- getDocument
  write doc $ toJSStr "Hello World!"

Compile this with:

hastec Main.hs --with-js=helpers.js

Now all we need to do is embed this file in a html page:

<!DOCTYPE html><html><head><title>Main</title>
<script type="text/javascript" src="Main.js"></script>
</head>
<body>
</body>
</html>

Open this with a browser of your choice (I only tried chromium) and it should work.

Edit: Trying it out

As suggested in a comment, I have uploaded the compiled javascript file (the haste version because the UHC version has several dependencies) here: Hello, World!

Download it and this html file into the same directory. Than open the html with a browser of your choice (tested on ubuntu linux, chromium).

Edit: Rewrote with pandoc

Creative Commons License
Writing JavaScript games in Haskell by Nathan Hüsken is licensed under a Creative Commons Attribution 3.0 Germany License.

5 comments:

  1. Interesting post. It would be nice if you included a link to a web page including the compiled java script, so we could test out the results without having to do all that heavy lifting :)

    ReplyDelete
    Replies
    1. That would be nice of me, would it not? :)
      I edited the post and added a link to the compiled file.

      Delete
  2. That looks extremely interesting. Thank you for bringing it to my attention.
    It should be on the The Java Script Problem page, should it not?

    I wondering what the limits of fay are ...
    It says class and instance declarations are not supported, but they are only relevant to the type checker and on that level they are supported, am I correct?

    I think I will add fay to the lists of compilers I am testing to see what the limits are.

    ReplyDelete
  3. You might be interested in http://chrisdone.com/fay/

    ReplyDelete