Personal tools

Yhc/Javascript/Users guide

From HaskellWiki

Jump to: navigation, search

Note: ycr2js is currently a standalone program distributed within the Yhc source tree. The usage guidelines below are relevant only for the standalone version. If ycr2js gets integrated in Yhc tighter than now, some or all of the following statements may be no longer applicable ansd will be updated accordingly.

Contents

1 Downloading

The ycr2js program along with additional tools is distributed within the Yhc source tree. Download and install Yhc as recommended here and here. Include the core=1 options on the scons command line when building Yhc to generate Core for all Haskell library modules. The yhc and ghc executables should be on the PATH after the installation is coplete.

2 Building and Installation on Unix

Change from the toplevel directory of the Yhc source tree to the src/translator/js directory. Execute commands:

make all
make install
make test

All three commands should finish without error.

3 Building and Installation on Windows

This currently does not work, but will do once the Scons build system that Yhc uses has been integrated.

4 What Is Installed

The Makefile in the ycr2js directory instructs make to place all necessary files relatively to the yhc executable location. After the installation of ycr2js, the directory structure will be as follows (assuming that prefix is the base path of the Yhc installation):

prefix/bin
  ycr2js Core to Javascript Converter
  pgbuild XHTML Page Building Utility
prefix/lib
  haskell
    StdOverlay.hs Standard Javascript Core Overlay
    UnsafeJS.hs Contains unsafeJS, a pseudo-function to inline Javascript code in Haskell functions
  javascript
    Runtime.js Runtime Support Javascript Library, needs to be included with every XHTML page generated
  xhtml
    emptyx.html An Empty XHTML Page template

The structure shown above represents a "bare" installation essential for ycr2js to function properly. Actual installation may include more files than these.

The base location of Yhc installation will be further on referred as prefix unless otherwise stated.

5 Compiling Haskell into Core

In order to obtain a linked Core file for a Haskell source(s), the Yhc compiler should be run like this:

yhc -includes prefix/lib/haskell \
   [-includes other include directories] \
    -linkcore Main.hs [other Haskell source files]

Other yhc options may be used as needed, but specifying the prefix/lib/haskell directory with the -include option is important. The -linkcore option instructs Yhc to link all core files generated together, along with library modules used. It is assumed that the user program's main module is named Main, and its entry point is main. Type signature of Main.main does not matter. The linked core will be output as the file Main.yca.

6 Converting Core into Javascript

In order to generate Javascript out of a linked core file (.yca), run the Core to Javascript converter:

ycr2js Main.yca Main.main >Main.js [2>Main.core]

The converter outputs generated Javascript into its standard output, and visual representation of the Core (after overlay and reachability check are applied, see details below). If redirection of standard error is omitted, visual representation of Core will not be saved.

The converter takes name of the linked Core file as its first command line parameter, and root names as the rest of parameters. The purpose of root names is to specify functions for which another functions they refer to (or, in other words, reachable) will be kept. For example, a module may contain functions like this:

module Main where
 
import UnsafeJS
 
factorial :: Int -> Int
 
factorial 0 = 0
factorial 1 = 1
factorial n | n > 0 = n * (factorial (n - 1))
            | n <= 0 = error "Factorial of a negative value"
 
anyStatus :: a -> a
 
anyStatus a = unsafeJS "window.status = exprEval(a).toString(); return a;"
 
str2 = 'a':'c':'d':"abyrvalg"
 
s5 :: String
s5 = 'g':[]
 
s6 :: String
s6 = 'd':""
 
s7 :: String
s7 = "ertyu"
 
main = anyStatus (["aaa","bbb",s7 ++ (head s7):(head s6):str2])

It can be easily seen that Main.main uses (of this module):

  • anyStatus
  • s7
  • s6
  • str2

and does not use:

  • factorial
  • s5

This means that the generated Javascript file will not contain factorial and s5 just for the reason of size optimization.

It is possible that a script for a Web page contains several parts not linked symbolically, so don't forget what functions are "roots" and specify them all on the command line.

Another important thing to know about Javascript generation is Core Overlay. An overlay is a fake Haskell module containing declarations of functions to be replaced in the output Javascript file.

A good example of this is the Prelude.error function. It is defined in the Standard Prelude as:

error :: String -> a
error s = primError s
 
primError :: String -> a
primError xs = trace (xs++"\n") (unsafePerformIO (exitWith (ExitFailure (negate 1))))

Not only is this definition irrelevant to Javascript execution in a Web browser (indeed, there is no traditional I/O), but it also pulls many other functions with it with the same low level of relevance. In the same time, the Prelude.error function is referred to from every case or if expression translated to Javascript.

The ycr2js program uses the overlay file, prefix/lib/javascript/StdOverlay.hs. Currently, only this overlay file may be used by ycr2js and its path is hardcoded into the program (although in the future, this limitation may be lifted). In an overlay file, names of functions (always qualified) to be replaced are encoded so for example a dot is replaced with prime-underscore sequence (the full set of decoding rules is defined in Yhc.Core.Overlay module in the function decodeString.

So, the Standard Overlay module defines Prelude.error to substitute one from the Prelude:

module StdOverlay where
 
import UnsafeJS
 
-- To substitute Prelude.error
 
global_Prelude'_error :: String -> a
 
global_Prelude'_error a = unsafeJS "alert(a); return undefined;"

which means that in the case of an error, an alert window will appear, and return of an undefined (in Javascript meaning) value will stop further script execution, most likely causing other error messages.

As an alternative, Prelude.error might throw an exception which unless caught would propagate to the top level and cause an error message to appear in Javascript Console, or in the browser's status line.

At this point, the generated Javascript is saved in the file Main.js.

7 Building a XHTML Page

In order to build a XHTML page ready to be loaded in a Web browser, another program that comes together with ycr2js is to be used:

pgbuild -tprefix/lib/xhtml/emptyx.html -o Main.html -T "Main" \
   -e prefix/lib/javascript/Runtime.js -e Main.js \
   --onload="exprEval(Main_46main)"

The pgbuild program, given a XHTML page template, parses it (using the Haskell XML Toolbox package functionality, and embeds Javascript files (or script URLS) provided into the <head> section of the template. Additionally, page title and the onload attribute of page body may be changed.

The -t command line option is used to specify the path to the empty page template. A sample template (usable in many cases) comes with ycr2js and is located in prefix/lib/xhtml/emptyx.html. The -o option specifies where to place the output file. The -T option specifies the page title to override one stored in the template. The -e option instructs that the following file name points to a script to be embedded, and the order of embedding is same as the order of appearance of file names on the command line. The --onload option results in the <body onload="..."> set to the value specified. The latter should be name of a function encoded as follows:

  • all alphanumeric characters remain the same
  • all other characters are replaced with an underscore followed by character's numeric code

so Main.main becomes Main_46main

It is important to set the page body 's onload attribute to exprEval(Main_46main) otherwise the toplevel expression will not be evaluated. It is also possible to specify any other valid Javascript expression here, see the Calling Haskell from Javascript section.

The XHTML Web page is written into the file Main.html and is ready to be loaded into a Web browser.

8 Tools Summary

9 All Together: A Simple Makefile

Here is an example of a simple Makefile containing rules sufficient to compile and build Web pages from Haskell sources. The yhc executable must be on the PATH.

Cutting and pasting this Makefile, don't forget about proper tabs usage.

#============ Begin Simple Makefile for Standalone ycr2js ============
YHC = yhc
YHCBASE = $$(dirname $$(dirname `which $(YHC)`))
YCR2JS =  $(YHCBASE)/bin/ycr2js
PGBUILD = $(YHCBASE)/bin/pgbuild
XMLTMPL = $(YHCBASE)/lib/xhtml/emptyx.html
RUNTIME = $(YHCBASE)/lib/javascript/Runtime.js
HSJSLIB = $(YHCBASE)/lib/haskell
# Insert names of Web pages to build here. General naming rule:
# if the page is built out of a Haskell file Foo.hs which contains
# the main function, the Web page file name will be Foo.html
all: Foo.html
# If Foo.hs imports modules from any location other than current directory,
# add more -includes options to this rule.
%.yca: %.hs
	$(YHC) -includes $(HSJSLIB) -linkcore $<
%.js: %.yca
	$(YCR2JS) $< $*.main > $@ 2> $*.coreovr
# These two rules generate visual representation of Core.
# Include files with names ending with .yc[ra]txt in the list
# of `all' dependencies to build these files: .yca and .js files
# will be deleted by make in the very end.
%.ycatxt: %.yca
	$(YHC) -viewcore $< > $@
%.ycrtxt: %.ycr
	$(YHC) -viewcore $< > $@
# Don't forget that your "root" module Foo must contain the
# `main' function. Its type signature does not matter.
# Use the indirect reference via funIdx for the page entry point
# because functions are now indexed to have shorter names.
%.html: %.js
	$(PGBUILD) -t $(XMLTMPL) -o $@ -T "$*" -e $(RUNTIME) -e $< \
               --onload="exprEval(funIdx['$*.main'])"
#============= End Simple Makefile for Standalone ycr2js =============