Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1022 lines (855 sloc) 48.989 kb
%TODO: Add pretty code example
%TODO: Add explanation of how we handle types
%%[prelude
%include IdentColors.fmt
%include Javascript.fmt
%format a_1
%format a_i
%format a_j
%format a_n
%format MANY = "\!^{*}"
%format ::= = "::="
%format \ = "{\lambda}"
% defs etc
\newcommand{\TODO}[1]{{\color{red}#1}}
\newcommand{\js}{JavaScript\xspace}
\newcommand{\uhcjscript}{\textit{uhc-jscript}\xspace}
\def\brunch{\verb+Brunch+\xspace}
\def\coffeescript{\verb+coffeescript+\xspace}
\def\jq{jQuery\xspace}
\def\jqui{jQuery UI\xspace}
%%]
%%[abstract
We introduce the Utrecht Haskell Compiler (UHC) JavaScript backend, which
allows one to cross-compile Haskell to JavaScript, so it can be run in the
browser. To interface with JavaScript and overcome part of the impedance
mismatch between the two languages, we introduce the Foreign Expression
Language; a small subset of JavaScript for use in Foreign Function Interface
(FFI) imports. Finally we discuss the implementation of a JavaScript
application, completely in Haskell, with which we show that it is now possible
to develop JavaScript applications completely in Haskell.
%%]
%%[introduction
When developing interactive web applications, JavaScript is often the language
of choice due to every major browser supporting it natively. In contrast to
other client-side programming languages, no plugins are needed to execute
JavaScript. Unfortunately, JavaScript is currently the \textit{only}
client-side programming language that is supported by all major browsers.
People wishing to use other programming languages or paradigms have to rely on
using existing plugins such as Flash or Java Applets, writing custom browser
plugins, or modifying the browsers themselves. None of these options are ideal,
since they either require a lot of work, or force the use of strict, imperative
programming languages. Instead of choosing between the aforementioned options,
we use the Utrecht Haskell Compiler (UHC) \cite{dijkstra09uhc-arch,www09uhc} to
compile Haskell code to JavaScript, effectively using JavaScript as a
high-level byte-code, and allowing us to side-step the problems identified with
the other approaches.
In this paper, we introduce the UHC JavaScript backend, a compiler backend that
allows one to compile Haskell to JavaScript, while keeping Haskell's lazy
semantics. We assume at least some familiarity with the Haskell Foreign
Function Interface (FFI) and JavaScript. To overcome the impedance-mismatch
between Haskell and JavaScript, we have extended UHC's FFI with a small
JavaScript-like expression language we call the Foreign Expression Language
(FEL). With these enhancements to the FFI, we claim that it is now possible to
write complete JavaScript applications using only Haskell. We back this claim
up by porting a web-based Prolog ``proof assistant'' from JavaScript to
Haskell. While this paper focusses on Haskell, the ideas should be relatively
easy to implement in similar languages. Additionally, we provide a library
containing bindings to the JavaScript standard functionality and bindings to
several other commonly used JavaScript libraries.
With this paper, we make the following contributions:
\begin{itemize}
\item We introduce the UHC JavaScript backend; a compiler backend that allows
one to compile any Haskell code supported by the UHC to JavaScript and
execute it in the browser, maintaining Haskell's lazy semantics.
\item We introduce the Foreign Expression Language (FEL), which allows for a
more natural way of interfacing with object-oriented languages via the FFI.
\item We show that it is now possible to write complete JavaScript
applications using only Haskell.
\item We provide a basic library with bindings to common JavaScript APIs.
\end{itemize}
The rest of this paper is structured as follows: section~\ref{hs2js} introduces
the UHC JavaScript runtime system (RTS). Section~\ref{ffi} covers the FFI with
our additions, after which section~\ref{jcu} shows the implementation of a
complete JavaScript application in Haskell. Sections~\ref{future}
and~\ref{related} discuss future and related work respectively, after which
section~\ref{conclusion} concludes.
%%]
%%[HaskellToJavaScript
\label{hs2js}
\subsection{\label{rts}Runtime System}
There exists an obvious mismatch between Haskell and Object-Oriented (OO)
languages, such as JavaScript, which has been addressed in various ways over
time (Section~\ref{related}):
\begin{itemize}
\item Mapping the runtime machinery required for Haskell to an imperative
language has to deal with the lazy evaluation strategy imposed by Haskell
(rest of this section).
\item Use of OO language mechanisms as available in JavaScript, in particular
prototype based objects; we only mention this topic in passing.
\item Use of available JavaScript libraries; we deal with this in the next
section by exploiting the freedom offered by Haskell's Foreign Function
Interface (FFI)
\end{itemize}
The design of any backend for a lazy functional languages needs to deal with
functions, their (lazy) application to arguments, and reducing such
applications to Weak Head Normal Form (WHNF). The design should also cater for
under- and over saturated function applications as well as tail recursion.
In UHC's JavaScript backend, functions and their applications are both
represented straightforwardly by objects:
\begin{spec}
Fun.prototype = {
applyN : function ( args ) ...
needsNrArgs : function() ...
}
function Fun( fun ) { ... }
\end{spec}
We omit implementation details and only expose the programmatic interface as
used by the runtime system. The actual implementation can be found in the UHC
git repository\cite{uhc-git}. A |Fun| object wraps a JavaScript function so
that it can be used as a Haskell function. The |applyN| field is only used when
function applications are being evaluated (forced); only then it is necessary
to know the |needsNrArgs| number of arguments which must be passed. For the
time being it stays unevaluated as a |Fun| object wrapped inside an |App| or
|AppLT| closure object.
Similarly, closures stemming from partially applied (and thus undersaturated)
functions need to store already passed arguments and how many arguments are
still missing. An |AppLT| (|LT| stand for \emph{less than}) object encodes this
and again we provide its programmatic interface first:
\begin{spec}
AppLT.prototype = {
applyN : function ( args ) ...
needsNrArgs : function() ...
}
function AppLT( fun, args ) { ... }
\end{spec}
An |AppLT| only wraps other |AppLT| objects or |Fun| objects.
Finally, for all remaining saturation cases an |App| object is used, knowledge
about the degree of saturation is delegated to the encapsulated function
object, which may be another |App|, |AppLT|, or |Fun|.
\begin{spec}
App.prototype = {
applyN : function ( args ) ...
}
function App( fun, args ) { ... }
\end{spec}
With this interface we now can embed Haskell functions; for example the
function |\x -> id ^ (id x)| is, assuming an elementary JavaScript
representation of the Haskell function |id| is available, by:
\begin{spec}
new Fun( function(x) {
return new App(id, [new App(id,[x])]);
} )
\end{spec}
Evaluation is forced by a separate function |eval| which assumes the presence
of an |eOrV| (evaluator Or Value) field in all Haskell runtime values, which
tells us whether the JavaScript object represents a Haskell non-WHNF value
which needs further evaluation or not; in the former case it will be a \js
function of arity 0, which can be called. A Haskell function or application
object does not evaluate itself since the entailed tail recursion will cause
the stack of the underlying JavaScript engine to flow over. The separate
external function |eval| doing the evaluation allows non WHNF values to be
returned, thus implementing a trampoline mechanism:
\begin{spec}
function eval( x ) {
while ( x && x.eOrV ) {
if ( typeof x.eOrV == 'function' ) {
x = x.eOrV() ;
} else {
x = x.eOrV ;
} }
return x ;
}
\end{spec}
Even normal \js values can be thrown at |eval|, provided they do not
(accidentally) contain an |eOrV| field. The actual |eval|\footnote{called |_e_|
in the implementation.} function is somewhat more involved as it provides some
protection against null values and also updates the |eOrV| field for all
intermediate non WHNF objects computed in the evaluation loop.
As usual the evaluation is driven by the need to pattern-match on a value, e.g.
as the result of a case expression or by a built-in \js primitive which is
strict in the corresponding argument such as in the wrapper of the primitive
multiplication function, which contains the actual multiplication (|*|):
\begin{spec}
new Fun( function(a,b) {
return eval(a) * eval(b) ;
} )
\end{spec}
Depending on the number of arguments provided, either an undersatured closure
is built, or the function is directly invoked using JavaScript's |apply|. In
case too many arguments are provided, a JavaScript closure is constructed,
which subsequently is evaluated in the evaluation loop of |eval|. The
implementation of |AppLT| is similar to that of |Fun|. |App|'s implementation
of |applyN| simply delegates to |applyN| of the function it applies to. Also
omitted are the encodings of nullary applications, used for unevaluated
constants (CAF, Constant Applicative Form) and indirection nodes required for
mutual recursive definitions. Data types and tuples are straightforwardly
mapped onto JavaScript objects with fields for the constructor tag and its
fields. If available, record field names of the corresponding Haskell data type
are used. We map |Int|, |Double|, |Float|, |Integer|, and |PackedString| values
to \js objects, shown in table~\ref{table:typemap}. Despite the mapping to \js
objects, the expressions of these types are lazy.
\begin{table}[ht]
\centering
\setlength{\tabcolsep}{10pt}
\begin{tabular}{l l}
\toprule
\textbf{Haskell} & \textbf{\js}\\
\midrule
|Int|, |Double|, |Float| & |Number|\\
|Integer| & |BigInt|\\
|PackedString| & |String|\\
otherwise & RTS representation\\
\bottomrule
\end{tabular}
\caption{Mapping from Haskell Types to native \js types}
\label{table:typemap}
\end{table}
\section{\label{ffi}JavaScript Foreign Function Interface}
We have extended the FFI with the Foreign Expression Language (FEL), a small
JavaScript-like language that greatly simplifies interfacing with the
JavaScript world from Haskell. The FEL allows one to number and reorder the
function arguments, explicitly use them as arguments to JavaScript functions,
or use them as objects. Functions in these objects can be called in the FEL by
using the dot, just like in JavaScript. Other features include hardcoding of
literals, accessing array indices, and a built-in mechanism for converting
data-types to JavaScript objects. The new grammar for importing functions is
shown in figure~\ref{jsimpent}.
\begin{figure}[ht]
\hrule
\begin{spec}
exp ::= '{}' ^ -- Haskell constructor to JS object
| (arg | ident) post MANY ^ -- JS expression
post ::= '.' ident ^ -- object field
| '[' exp ']' ^ -- array indexing
| '(' args ')' ^ -- function call
args ::= epsilon | arg (, arg) MANY ^ -- possible arguments
arg ::= '%' ('*' | int) ^ -- all arguments, or a specific one
| '"' str '"' ^ -- literal text
ident ::= a valid JavaScript identifier
int ::= any integer
str ::= any string
\end{spec}
\hrule
\caption{Import entity notation for the JS calling convention}
\label{jsimpent}
\end{figure}
Common FFI features, such as the \emph{dynamic} and
\emph{wrapper}\cite{Haskell98} imports, work as expected, allowing one to use
higher-order JavaScript functions in the same way as C function pointers.
As an example of how one would use the FEL to import a JavaScript function,
suppose we want to import the \texttt{subString} method from the JavaScript
String class, where \texttt{someStr} is a concrete JavaScript string object:
\begin{verbatim}
someStr.subString(start, length);
\end{verbatim}
This method is called on a JavaScript string object, and returns
a substring, based on the integer value for a start offset and an integer value
for the length of the substring, both of which are passed as arguments to the
method. Importing this method is shows the FEL's added value in several
ways: the method is called on a JavaScript \textit{object}, it takes multiple
\textit{arguments}, and it requires conversion from a Haskell |String| type to
a JavaScript string type\footnote{Where |JSString| is a type synonym for
|PackedString|}. The import is shown below:
\begin{spec}
foreign import js "%1.subString(%2, %3)"
subString :: JSString -> Int -> Int -> JSString
\end{spec}
In addition to the |js| calling convention, the other noticeable difference
with, for example, a C import, is the import definition in the string. Rather
than having the FFI place all arguments in one position, we number the
arguments and allow them to be placed in different positions in the imported
method. With the first argument, indicated by \texttt{\%1}, before the dot, it
is treated as an object in the generated JavaScript code. The number of the
argument corresponds with the position of the arguments in the type signature.
The two remaining arguments are placed between parentheses, so that they become
arguments in the method call in the generated JavaScript code.
An alternative way of writing this import is show below, where we replace the
explicit argument positions with a wildcard. This says that all remaining
arguments should be placed where the wildcard is. Using a wildcard has the
advantage that it becomes easy to import methods with an overloaded number of
arguments.
\begin{spec}
foreign import js "%1.subString(%*)"
subString :: JSString -> Int -> Int -> JSString
\end{spec}
Exporting a function does not make use of the FEL, so it is not much different
from exporting a function for the C FFI. The only concerns to keep in mind are
using the |js| calling convention, and specifying a JavaScript-compatible type
in the type signature.
\subsection{\label{uhc-js}The UHC-JavaScript library}
We provide a library\cite{uhcjs}, simply called the UHC-JavaScript library, to
streamline the development of \js applications with UHC. It contains bindings
to standard ECMAScript\cite{ecmascript}, the formal standard behind JavaScript,
as well as bindings to the jQuery library\cite{www11jquery}. The library aims
to provide a bare-metal interface that is consistent with the JavaScript
functions. Eventually, this library should form the basis on which more
(functional) abstractions are built. We shall make use of this library in the
rest of this paper.
\subsection{\label{object-rts}Creating, manipulating and querying objects}
Being a purely functional programming language, Haskell has no notion of
objects. JavaScript, however, does. Objects come in two flavours: anonymous and
named objects. The former is denoted in \js as |{}|, while the latter is
created by defining a constructor function of which the name starts with an
uppercase letter, as in: |function MyObj() {}|. Objects can then be
instantiated with the |new| keyword: |new MyObj()|. Each function also has a
prototype object. This prototype enables defining values and functions within
the scope of the object. New object instances will automatically have the same
values and functions as the prototype.
%UHC now offers a functional interface for creating, instantiating and
%manipulating objects and prototypes in \js during runtime. We can convert
%datatypes to objects, or we can define objects from scratch.
UHC offers support for creating, manipulating and querying objects, using
several new primitive functions in the runtime-system (RTS). Instead of showing
the rather uninteresting function definitions in JavaScript, the code below
shows the Haskell type signatures which need to be used when importing these
primitives with the FFI:
\begin{code}
primMkCtor :: JSString -> IO ()
primMkObj :: JSString -> IO (JSPtr c)
primMkAnonObj :: IO (JSPtr c)
primGetAttr :: JSString -> JSPtr c -> IO a
primSetAttr :: JSString -> a -> JSPtr c -> IO (JSPtr c)
primModAttr :: JSString -> (a -> b) -> JSPtr c -> IO (JSPtr c)
primGetProtoAttr :: JSString -> JSString -> IO a
primSetProtoAttr :: JSString -> a -> JSString -> IO ()
primModProtoAttr :: JSString -> (a -> b) -> JSString -> IO ()
\end{code}
|JSString| is a type synonym for |PackedString|, the builtin type corresponding
to JavaScript strings. The |primMkCtor| function creates a new constructor
function if it does not yet exist in the |window| scope where |window| is the
variable containing everything pertaining the current window or tab. This
function is usually only called from within the other functions listed above.
The |primMkAnonObj| function creates an anonymous object |{}|, while the
|primMkObj| accepts a string with the class name of the object to be created.
If the class does not exist yet, it is created using an empty constructor.
The other functions manipulate objects and prototypes, using a mechanism
inspired by
lenses\cite{Meijer:1991:FPB:645420.652535,Hofmann:2011:SL:1925844.1926428,Kagawa:1997:CRS:258949.258969};
an abstraction over accessors and mutators. The first argument is always the
name of the object attribute of interest passed as a |String|. In case of the
|set|-functions, the second argument is the value that needs to be set. Since
\js is a loosely typed language, this can be any type, even when interfacing
with it from the Haskell world. The |mod|-functions take as second parameter a
function which modifies the attribute specified in the first argument.
Modifying an attribute may change its type, hence the |a -> b| type for the
function. Finally, the last argument is either a reference to an object, or the
name of a class as a |String|, in case of prototypes.
These functions can be used by importing them as primitives:
\begin{code}
foreign import prim "primGetAttr"
_getAttr :: JSString -> JSPtr p -> IO a
\end{code}
Objects are represented in the UHC-JavaScript library by a |JSPtr a| type,
which has no constructors, so they can't be instantiated directly. The only way
an object can be obtained is by getting it via the FFI. A |JSPtr a| takes one
phantom type as a parameter, which specifies the type of the \js object. This
should again be a type without constructor. Suppose we want a pointer to a
|Book| object, we define it as follows:
\begin{code}
data JSPtr a
data BookPtr
type Book = JSPtr BookPtr
\end{code}
We can now define functions on the |Book| type, giving us a type-safe way to
deal with \js objects. A similar approach as is often taken in GHC's C FFI to
deal with pointer types.
We offer the |Language.UHC.JS.Primitives| module in the UHC-JavaScript library,
which defines primitive imports and abstracts away from |JSString|. Using these
functions we can now create, manipulate and query an object:
\begin{code}
main = do
o <- mkObj "Book"
setAttr "pages" 123 o
modAttr "pages" (+1) o
p <- getAttr "pages" o
print p -- Prints 124
\end{code}
While defining objects as shown in the previous example works fine, the process
is rather verbose and tedious, especially when dealing with several object
attributes. It would therefore be ideal if we could use Haskell datatypes to
achieve the same results. In some ways, datatypes and \js objects have a lot in
common, especially when the datatype has record selectors. Suppose we have a
simple |Book| type in Haskell:
\begin{code}
data Book
= Book
{ author :: JSString
, title :: JSString
, pages :: Int
}
\end{code}
A concrete |Book| value would look as follows:
\begin{code}
myBook
= Book
{ author = toJS "me"
, title = toJS "story"
, pages = 123
}
\end{code}
The representation of |myBook| closely resembles an object with the same data
in JavaScript:
\begin{verbatim}
myBook
=
{ author : "me"
, title : "story"
, pages : 123
}
\end{verbatim}
In fact, a \js object very similar to the one shown above is already being
generated by the UHC. However, since it is generated as an application of
a constructor to some values, the generated datatype values are not directly
usable in other \js libraries. We require a mechanism to convert the Haskell
representation of the datatype into a \js representation. This idea is similar
to that of the FFI's wrapper import feature. Using a similar mechanism to the
wrapper, we can make Haskell datatypes available as \js objects. This mechanism
is exposed via de FEL, simply as |{}|:
\begin{code}
foreign import jscript "{}"
mkObj :: a -> IO (JSPtr b)
\end{code}
It takes a datatype |a| and converts it to a plain \js object, resulting in a
pointer to the new object. If the datatype contains record selectors, they will
be used as the object's indices. When no record selectors are available, an
integer is used instead.
Creating the object is achieved by recursively evaluating and cloning the data
inside the datatype to a new, empty object, disposing of RTS-specific
information in the process. Using the object wrapper, we can simplify our
example from before:
\begin{code}
main = do
let b' = myBook { pages = pages myBook + 1 }
b <- mkObj b'
p <- getAttr "pages" b
print p -- Prints 124
\end{code}
Note that even though this example is only one line shorter, we also have the
two strings available in our \js object, which would haven taken two more lines
in the original example. More importantly, Haskell's type system is in a much
better position to catch programmer mistakes, since record selectors are used
in the modification of the |pages| value instead of strings.
\subsection{Pure objects}
Objects in \js are mutable by nature. By modifying an object, you modify it for
everything that has a pointer to that particular object. This forces any update
operation to be defined in |IO|. In order to escape the |IO| monad, update
operations need to become non-destructive, which is achieved by creating a copy
of an object before modifying it. The RTS exports a primitive to do exactly
this:
\begin{code}
primClone :: JSPtr a -> JSPtr a
\end{code}
By cloning an object first, all pointers to the original object remain
untouched when modifying the clone. This enables pure variants of the
|primSetAttr| and |primModAttr| functions:
\begin{code}
primPureSetAttr :: JSString -> a -> JSPtr c -> JSPtr c
primPureModAttr :: JSString -> (a -> b) -> JSPtr c -> JSPtr c
\end{code}
Since a potentially large tree of objects will be cloned by these pure
functions, they should be used with care. The cloning method used is a
modification of the cloning method used by jQuery\cite{www11jquery}.
%%]
%%[JCUApp
\label{jcu}
% What does the app do? How is it implemented? Does it work? Does it work well?
% Is the UHC JS backend a full replacement for JS coding? Etc?
% \cite{swierstra2011,stutterheim2012} \cite{snap}
To explore the limitations, and to demonstrate the features of the UHC
JavaScript back-end in a real-life scenario, we ported the `JCU Prolog Proof
Assistant'\cite{swierstra2011}, a web application developed to aid in
teaching\cite{stutterheim2012} Prolog at the Junior College Utrecht. It is a
tool developed for students to learn about important concepts in computer
science, such as proofs, trees, unification, and backtracking, by means of
proving Prolog queries manually. Students enter a Prolog query, after which
they can build a proof by dragging and dropping Prolog rules and facts, and by
applying substitutions manually throughout the proof tree.
The application was originally programmed in \coffeescript\cite{coffeescript},
a layer of syntactic sugar for \js, and used the \brunch\cite{brunch}
framework. In the original implementation, all Prolog logic was implemented
server-side in Haskell, using the NanoProlog\cite{nanoprolog-package} library.
We rewrote the application in Haskell using the UHC and the UHC-JavaScript
library. We also use jQuery for interacting with the DOM and the jQuery
AjaxQueue\cite{ajaxq} plugin for sequential non-blocking communication with the
server. The resulting application has the same functionality as the
original implementation and appears to be at least as stable, although this has
only been manually tested. As is expected of applications that interact heavily
with a graphical user interface, a large part of the application's code lives
in the |IO| monad.
With the ability to compile Haskell to JavaScript comes the possibility of
running any Haskell library that compiles on the UHC in the browser, without
modification. We use this feature in the JCU web application to run the
NanoProlog library in the browser, allowing us to perform proof checking and
unification at the client-side, eliminating the need for many AJAX requests.
We can take the idea of running the application completely client-side even
further, by eliminating the need for a server altogether. We can do so by using
HTML 5 Local Storage, a client-side database offered by nearly all modern
browsers, and accessible via JavaScript, to store the set of rules and facts.
This has the advantage that you can run the assistant, even if you have no
Internet connection available.
\subsection{\label{issues}Implementation Issues}
% TODO describe: Which issues do you run into in general when you develop a JS
% app in Haskell?
% Ale: non-termination/threads, no native OO, interfacing with annoyingly
% multi-interpretable functions (functions that are dimensioned in both the number
% and type of their arguments)
% \TODO{Mismatch GHC/UHC with shared modules, some copying done, but should have separated out definitions, points to the issue where UHC is missing some features}
Most of the problems we encountered in porting the JCU application to Haskell
were due to the lack of advanced language features in UHC, such as functional
dependencies and type families. Practically, this implies that only part of the
libraries available on Hackage today can currently be compiled to JavaScript
using the UHC JavaScript back-end.
Another issue arises from JavaScript's scoping rules. In \js, the keyword
|this| is dynamically scoped while all other variables are lexically scoped.
Since we emulate lazy evaluation by native JavaScript functions encapsulated by
objects, the |this| keyword can in some cases point to the runtime system,
rather than the expected scope, exposing the runtime system to the programmer.
Simply importing |this| as a function using the FFI is not an option here.
A common use-case of when this might happen is when an imported JavaScript
library expects the programmer to make use of the |this| keyword in a callback
function. The \jq library, for example, expects event callbacks to get the
active DOM-node using the |this| keyword. One way to still get a reference to
the expected object when using |this| is to create a wrapper function that
captures the expected scope and passes it to the wrapped function as explicit
argument. We have implemented this solution in the |wrappedThis| function,
which is part of our RTS.
% One would be tempted to import the
% keyword as a function as follows:
%
% \begin{code}
% foreign import js "this"
% this :: IO JQuery
% \end{code}
%
% However, this would insert the \js |this| keyword inside the \js code that
% represents our Haskell function. This function is then wrapped by our dynamic
% wrapper. Since our function is called by our RTS, the |this| keyword referreds
% to the RTS objects' scope, essentially exposing the RTS to the Haskell world
% and hiding the scope that one would expect to get when we would be using plain
%\js. One way to deal with this problem is by creating a wrapper function that
Figure \ref{code:this} shows how the |wrappedThis| function can be used to
obtain the value of an HTML input field. The code above the definition of
|bindInput| is copied from the \js library; |valString| is a function that gets
the value of a \jq object as a |String|. We query the DOM using jQuery,
retrieving all \texttt{input} elements, such as text fields, in the DOM. We
define a function |alertHndlr| that takes the string value of a jQuery object
and then shows it in an alert box. Note the explicit |this| parameter. We then
wrap it so it becomes a \js function, after which we partially apply it to an
explicit |this| parameter using |wrappedThis|. Finally, we bind the event
handler to all input fields retrieved by our jQuery selector.
%TODO: Rewrite the bit above?
\begin{figure}
\begin{code}
data JQueryPtr
type JQuery = JSPtr JQueryPtr
foreign import js "%1.bind(%*)"
bind :: JQuery -> JSString -> JEventHandler -> IO ()
type ThisEventHandler = JQuery -> JQuery -> JEventResult
type JEventHandler = JSFunPtr (JQuery -> JEventResult)
type JThisEventHandler = JSFunPtr ThisEventHandler
valString :: JQuery -> IO String
mkJThisEventHandler :: ThisEventHandler -> IO JThisEventHandler
foreign import js "wrappedThis(%1)"
wrappedThis :: JThisEventHandler -> IO JEventHandler
bindInput = do
let alertHndlr :: ThisEventHandler
alertHndlr this _ = valString this >>= alert
inputField <- jQuery "input"
eh <- mkJThisEventHandler alertHndlr >>= wrappedThis
bind inputField (toJS "blur") eh
\end{code}
\caption{Code for adding an event handler to an input field}
\label{code:this}
\end{figure}
A last example of implementation difficulties is found in the lack of threading
support in our current implementation. In addition to the web-based proof
exerciser, we offer a web-based user interface to NanoProlog's interpreter. In
some cases, the interpreter can get stuck in an infinite recursion when trying
to unify a rule. For example, trying to proof the query |silly(X)|, where
|silly| is defined as |silly(X):-silly(X).| will never terminate. Originally,
we spawned a new thread on the server, which we would terminate after a given
amount of time. Our current approach, however, does not yet offer threading,
risking blocking the client-side process causing a tab or the whole browser to
hang. JavaScript's WebWorkers might provide a solution to this problem,
although we have yet to investigate this option. Another solution would be to
change the implementation to limit it's recursion depth.
\subsection{\label{performance}Performance}
% Performance is decent, except for the actual Prolog backtracking. Where is the bottleneck?
% Ale: Probably as I've stated before the problem is in the memory management as
% is supported by the benchmarks in Chrome and Fx.
In general, the performance of the web application is on par with the original
implementation in JavaScript, but only when using a state of the art JavaScript
engine, as is found in Google Chrome or Safari. The largest bottleneck seems to
be memory management. Building up lazy Haskell expressions leads to a large
number of JavaScript objects. The quick creation and then successive
destruction of these large expressions places a strain on the memory manager
and garbage collector. Other popular browsers, such as Firefox, Opera, and
Internet Explorer, perform significantly worse than the aforementioned
browsers, although this has only been tested informally.
%%]
%%[futureWork
\label{future}
While we have shown that it already is possible to implement an entire
JavaScript application in Haskell, there is still a lot of room for
improvement. As mentioned before, the UHC itself lacks support for the more
advanced Haskell features. Implementing these in the UHC would go a long way to
making the UHC JavaScript back-end a true alternative to JavaScript.
Our current UHC-JavaScript library relies on the programmer to use imported
functions correctly. The object-wrapper import, for example, will currently try
to wrap anything, possibly failing at runtime. Extra constraints could be
added, although the RTS cannot currently deal with them. Eventually, one could
image a higher-level library being built on top of the low-level imports to
provide improved type-safety. Such libraries may be based on generic
programming to eliminate repetition, functional reactive
programming\cite{Elliott:2009:PFR:1596638.1596643,Wan:2000:FRP:349299.349331,reactive-banana}
to interact with the DOM, or they may be an entire user-interface toolkit, such
as wxHaskell\cite{wxHaskell}.
Working with WebWorkers as an alternative to Haskell threads is currently not
investigated yet. Our JCU application would become significantly more usable
with a threading alternative.
Communication with the server is currently encoded manually, possibly using
existing libraries, like jQuery. One could imagine an approach inspired by
Cloud Haskell's\cite{epstein2011} typed channels, where communication proceeds
over type-safe communication channels, abstracting away from the actual AJAX
call.
Currently the only way of converting a datatype to a \js object is to do so at
runtime. This, however, is a process with time complexity linear in the number
of datatype records. Future work could focus on generating (parts of) \js
objects at compile-time, so that only dynamic values will need to be copied to
the object at runtime.
Targeting Haskell to a different platform means that some assumptions following
from using a single platform only are no longer valid. First, a different
platform means a different runtime environment. Almost all of the UNIX
functionality is available for the usual Haskell UNIX runtime, but is naturally
not available inside a web browser and, vice verse, specific JavaScript
libraries like jQuery are not available on a UNIX platform. Some library modules
of a package (partially) cannot be build on some platforms, while others
(partially) can. To cater for this, UHC rather ad-hoc marks modules to be
unavailable for a backend by a pragma \verb|{-# EXCLUDE_IF_TARGET js #-}|. Of
course |cpp| can still be used to select functionality inside a module. However,
in general, awareness of platform permeates all aspects of a language system,
from the compiler itself to the library build system like |Cabal|.
In particular, |Cabal| needs a specification mechanism for such variation in
target and platform to allow for selective compilation of a collection of
variants. Currently this means that UHC compilation for the JavaScript backend
cannot be done through |Cabal|.
A second aspect has more to do with the evolution of Haskell as an ecosystem.
Many libraries go far beyond the Haskell standard by making use of GHC
extensions. Currently, such libraries evolve to use type families, a feature not
yet available in UHC. For (non GHC) Haskell compiler writers to keep with this
pace of evolution poses a considerable challenge; yet in our opinion there is
value in the availability of compiler alternatives as well as variation in what
those compilers are good at.
Currently, we generate JavaScript from the compiler's core language. It might
be possible to generate faster code which uses native JavaScript language
features when generating JavaScript at a later stage in the compiler pipeline,
where the intermediate code is more imperative in nature.
% \subsection*{Generic |FromJS| and |ToJS| for converting objects/records}
% It should be fairly straightforward to implement a generic implementation for
% |FromJS| and |ToJS| using deriving Generic.
%
% \subsection*{Providing an api to build web applications}\label{ssec:providing-an-api-to-build-web-applications}
% One could think of providing a similar API such as WxHaskell\cite{wxHaskell}
% does for constructing native application, but for web applications. Also one
% could think of providing a Functional
% Reactive
% interface to building the web application.
%
% \subsection*{Type checking}
% Currently, foreign expressions are not type checked at all. In case of a
% programming error the compiler will currently panic in the best-case scenario
% and happily generate \js code that fails at runtime in the worst-case scenario.
% Constraints on the imports and exports need to be formalised and the foreign
% types should be type checked according to the foreign expressions. Some example
% constraints that could be type checked:
%
% \begin{itemize}
% \item Only datatype values may be exported as objects, not functions or
% primitive types.
% \item Only wrapped functions may be exported in objects.
% \end{itemize}
%
% The first item could be realised by supporting type constraints in the foreign
% import. This is already allowed by the type system, but the RTS does not
% currently support this.
% \subsection*{Testing}
% During development, the code has only received limited, informal testing. The
% UHC's test suite should be expanded to enable automated testing. Additionally,
% real-world \js front-end applications should be ported to Haskell and the
% \uhcjscript library to identify shortcomings of the existing ideas and
% implementations.
% \subsection*{Benchmarking an optimising}
% Some benchmarks have been performed that show that the generated \js is quite
% a bit slower than a hand-written version of the same code. Unfortunately, these
% numbers aren't publicly available, nor is is clear where the biggest
% bottlenecks are located. It would be very interesting to do another round of
% benchmarks, including the object code. Afterwards, it would be interesting to
% identify bottlenecks and find ways to speed up the generated code.
% \subsection*{Safe |this| scope (working title)}\label{future-work:this-scope}
% Is there a way to prevent the RTS from being exposed by using the keyword
% |this|?
% \subsubsection*{Numeric object indices}
% When a datatype without record selectors is converted into a \js object, the
% object's attribute name becomes an integer $\geq{1}$ with an underscore prefix.
% Ideally this would be a numeric index $\geq{0}$.
%\subsection*{Deployment}
%JavaScript code is usually downloaded, hence compact representation as well as
%avoiding or delaying loading of code is important. Although UHC allows pruning
%of unused code as to achieve a relative compact representation, it provides no
%mechanism for dynamic loading of modules. This is left for future
%implementation. In addition UHC is able to perform whole program analysis
%further compacting the JavaScript code and producing a single output JavaScript
%file.
%\TODO{Code compactification, module loading, whole prog, ...}
%\subsection*{Packages and The Haskell ecosystem}
%\TODO{Mismatch: Cabal does not deal with multiple backend of one compiler.}
%\TODO{Mismatch: Libraries take GHC as standard, not Haskell2010 (or H98).}
%\TODO{Unix based library is useless, basically other OS to run on, what about shared code, ...}
% \subsection*{Type system absence}
% JavaScript has no type system, the absence of which can be dealt with by using
% phantom types in wrapper functions around untyped FFI calls. More problematic
% are for example DOM functions returning objects with a different interface, like
% a DOM element or attribute. A sum type defining all possible result types could
% be used, but data types are not extensible, which might be too limiting in
% practice. Dynamics might be used as result values, but require assistance from
% the runtime system as well as knowledge about types (e.g. in the form of
% |TypeRep|). Existentially quantified data types and classes might be used
% (similar to extensible exceptions \cite{marlow06extensible-exception}), but then
% knowledge about the class system also seeps into the runtime system. Currently
% this has not yet been further addressed.
% %\TODO{Typing the untyped.}
% \subsection*{Compiling to native \js through Silly}
% The current setup translates Haskell to Core code and from there to our
% representation of Core code in \js. This Core code is then interpreted at
% runtime. To possibly gain performance it is interesting to see whether we can
% take the compilation a step further and compile down to
% Grin\cite{boquist96grin-optim} and possibly Silly\TODO{citation needed}. Then
% instead of translating to C we should compile the Grin/Silly code to \js. We
% expect some difficulties because of the lack of direct memory management in \js
% but it should otherwise be rather straightforward.
%
%%]
%%[relatedWork
\label{related}
% \paragraph{Other approaches.}
The idea of running Haskell in a browser is not new. To our knowledge the first
attempts to do so using JavaScript were made in the context of the York Haskell
Compiler (YHC) \cite{www07yhc-javascript}. The Document Object Model (DOM)
inside a browser was accessed via wrapper code generated from HTML standard
definitions \cite{www07haskell-in-browser}. However, YHC is no longer
maintained and direct interfacing to the DOM nowadays is replaced by libraries
built on top of the multiple DOM variations.
% TODO: Explicitly mention GHCJS here
GHCJS\cite{www11ghcjs-git,www12ghcjs-new} is an attempt to use the GHC API to
create a dedicated Haskell to JavaScript compiler. It uses the C calling
convention, rather than a dedicated |js| calling convention. A major advantage
of using the GHC API is that a mature, production-ready compiler, with support
for advanced type-system features is at the programmer's disposal, solving some
of the issues we are currently experiencing due to lack of these advanced
features in UHC. Currently, GHCJS does not support an import system like the
one described in this paper, so its ability to use external APIs is limited.
GHCJS' authors remarked that adding an FEL-like import mechanism to GHCJS
should be relatively straight-forward.
A very recent, and very promising looking attempt at cross-compiling Haskell to
JavaScript is the Fay language\cite{fay-lang} by Chris Doner, which aims to
support a subset of Haskell and compile to JavaScript. It, too, makes extensive
use of GHC, giving it a production-ready Haskell compiler and type-checker to
build on. In designing Fay's FFI, Doner drew some inspiration from the work we
present here, namely the FEL.
Another recent attempt is Haste\cite{ekblad2012} by Anton Ekblad, it compiles
Haskell to Javascript and supports the same code as GHC does, with some remarks
as how to perform large number arithmetics. It appears to be generating code
with a small footprint and which performs better than native \js. Haste also
comes with a small reactive library for interacting with the DOM.
Rather than focussing on cross-compiling, ``Functional JavaScript''
\cite{www07functional-javascript} offers a library for a more functional style
of programming in JavaScript. ``Haskell in JavaScript''
\cite{www10haskellinjavascript} offers an interpreter for Haskell, written in
JavaScript.
The workflow framework |iTasks|, built on top of the Clean system
\cite{www11clean-system}, uses a minimal platform independent functional
language, SAPL, which is interpreted in the browser by code written in Java.
The latest interpreter incarnations are written in JavaScript
\cite{jansen10phd-itasks-sapl-funcweb,bruel10sapl-javascript,plasmeijer08clientside-itasks-ajax}.
Although currently a Haskell front-end exists for Clean, the use of it in a
browser appears to be limited to the iTasks system. The intermediate language
SAPL also does not provide the facilities as provided by our Haskell FFI.
%\subsection{Object orientation.}
%
%Object Oriented behaviour itself can be realised inside Haskell by exploiting
%the class system \cite{shields01haskell-oo-overloading,kiselyov05haskell-oo}.
%However, we aim to access libraries written in JavaScript, not \emph{mimic}
%JavaScript or OO mechanism in general inside Haskell.
%
%% TODO: sounds shaky en niet meer helemaal waar voor ons ding?
%However, when functionality of the libraries would have to be (re)written in
%Haskell some form of OO mechanism should be available. This issue arises when
%one would code in Haskell a platform independent part of an otherwise OO GUI
%library, say |wxHaskell|. For now we limit ourselves to accessing JavaScript
%libraries via the FFI, hiding OO idiom inside FFI import entities.
%\TODO{Mapping to OO: FFI or other mechanism.}
%\TODO{Records in JS vs HS.}
% \paragraph{Side effects.}
%
% All access to JavaScript values is done in the IO monad, so side effect can be properly modelled.
% For now it is assumed that no threads exist.
% Since JavaScript's worker thread mechanism can be used safely we currently do not need
% semaphores, STM, or other shared data access machinery.
% Some values like the globally available @window@ in a browser could be accessed without the use of the IO monad because its value does not change.
% However, if and when this assumption would not hold in a near future it would break our wrapping code as well.
%\TODO{All in IO, what can be done without IO, threads? STM?.}
%\TODO{Onderstaande twee paragrafen zijn een beetje bloat maar er zit misschien nog wel wat in voor later.}
% \paragraph{Mapping to OO runtime environments.}
%
% In general, it is attractive to map onto available virtual machines for popular OO languages,
% in particular because of the availability of a wealth of libraries.
% Targeting the Java virtual machine (JVM) has been experimented with
% \cite{wakeling98haskell-to-java,func:lazy:java:parser-combinator,stewart02mthesis-multiparadigm-jit,tullsen96haskell-to-java},
% as well as with the @.NET@ platform \cite{monteiro05functional-to-dotnet,www04haskell-dotnet,meijer07mondrian,meijer01dotnet-scripting-mondrian};
% UHC also provides a backend to the JVM \cite{func:lazy:java:parser-combinator}
% using the same technique as described here.
% However, interfacing to libraries is still lacking in UHC, and in general library access and other
% runtime oriented features (threads, exceptions)
% is where the real work lies \cite{www12ghc-faq}.
% Wrapping and interfacing to libraries has to be done manually or by tools interpreting library code which requires substantial effort and is
% suffering from misinterpretation.
% In the case of JavaScript lack of typing annotations even precludes automatic FFI wrapper generation,
% unless type annotations in comments could be trustworthy and formal enough to be used instead.
% Efficient code generation is also an issue.
% Usually non standard OO language constructs are used to implement
% standard idiom of (lazy) functions.
% For now, with UHC we have taken the approach to first make it work and not bother about efficiency,
% generating code from an early stage in the compiler pipeline.
% We expect exploitation of the results of a strictness analyser to speed up the code considerably,
% especially because the existing JavaScript compilers to be better able to analyse the provided code.
%%]
\section{More information and download}
For the variant of the JCU application as implemented for this paper more info
(download, how to install, etc) is available, via the UHC www site
\cite{www09uhc} or directly \cite{www12uhc-js-backend}.
%%[conclusion
\label{conclusion}
We have shown that the UHC is capable of supporting the development of complete
client-side web applications. This opens the door to Haskell-only web
development. In the process we added the FEL to UHC and provided a library that
exposes the \js world to Haskell. Considering the increasing maturity of the
GHC-based solutions, we can conclude that our two major contributions to
solving the JavaScript problem are the FEL, and our experimental proof that
writing a complete, non-trivial web application, optionally using external
JavaScript libraries is now possible in Haskell. Since UHC does not support
advanced Haskell language features, and GHC's development is faster and more
consistent, it remains to be seen whether our implementation in UHC can grow to
a mature tool for developing JavaScript applications. While still keeping this
option open, we also call on authors of GHC-based solutions to consider using
the contributions of this paper in their work.
When it comes to libraries for writing JavaScript applications in Haskell,
better abstractions are still required to reduce the amount of code that lives
in the |IO| monad directly, and to give programming with the UHC JavaScript
backend a more functional feel. While in most cases performance is acceptable,
it needs to be improved if computationally heavy functions are to be run on the
client. In order for most of the frequently used Hackage libraries to be run on
the client, additional work on UHC and Cabal will have to be performed.
%%]
%%[XX
%%]
Jump to Line
Something went wrong with that request. Please try again.