Yhc/Javascript/Users guide
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 and will be updated accordingly.
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 complete.
Build prerequisites
If you don't already have them, install Happy, the HTTP package, and HXT.
Building and installation on Unix
Change from the toplevel directory of the Yhc source tree to the src/translator/js
directory. Edit the Makefile to reflect your system configuration and execute this shell command (parentheses are needed to remain in the same directory after building ycr2js finishes):
(cd src/translator/js; make all install)
This command should finish without error.
Building and installation on Windows
This currently does not work, but will do once the Scons build system that Yhc uses has been integrated.
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.
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
.
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
.
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.
Tools summary
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 =============