/
TopicJavaScriptIFL.cltex
911 lines (767 loc) · 42.4 KB
/
TopicJavaScriptIFL.cltex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
%%[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; a compiler
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 on 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 hacking the browsers themselves. None of these options are ideal,
since they either require a lot of work, or force the use of strict, imperative
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 turning JavaScript into a high-level
byte-code of some sorts.
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. To overcome the impedance-mismatch between Haskell and JavaScript,
we have extended UHC's Foreign Function Interface (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) and FFI with our addition, after which
section~\ref{jcu} shows the implementation of a complete JavaScript application
in Haskell, after which sections~\ref{future} and~\ref{related} discuss future
and related work respectively. Finally, section~\ref{conclusion} concludes.
%%]
%%[HaskellToJavaScript
\label{hs2js}
\subsection{\label{rts}Runtime System}
There exists an obvious mismatch between Haskell and Object-Oriented (OO)
languages, 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 evaluating 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{code}
Fun.prototype = {
applyN : function ( args ) ...
needsNrArgs : function() ...
}
function Fun( fun ) { ... }
\end{code}
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, 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{code}
AppLT.prototype = {
applyN : function ( args ) ...
needsNrArgs : function() ...
}
function AppLT( fun, args ) { ... }
\end{code}
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{code}
App.prototype = {
applyN : function ( args ) ...
}
function App( fun, args ) { ... }
\end{code}
With this interface we now can embed Haskell functions;
for example the function |\x -> id ^ (id x)| is,
assuming an elementary JavaScript function |id| is available, by:
\begin{code}
new Fun( function(x) {
return new App(id, [new App(id,[x])]);
} )
\end{code}
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{code}
function eval( x ) {
while ( x && x.eOrV ) {
if ( typeof x.eOrV == 'function' ) {
x = x.eOrV() ;
} else {
x = x.eOrV ;
} }
return x ;
}
\end{code}
Even normal \js values can be thrown at |eval|, provided they do not
(accidentally) contain an |eOrV| field. The actual |eval| 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{code}
new Fun( function(a,b) {
return eval(a) * eval(b) ;
} )
\end{code}
Depending on the number of arguments provided, either an undersatured closure
is built, or the function is directly invoked using JavaScripts |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.
\section{\label{ffi}JavaScript Foreign Function Interface}
We have extended the FFI with the Foreign Expression Language (FEL), a small
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}. Common FFI features, such as the \emph{dynamic} and
\emph{wrapper}\cite{Haskell98} imports, work as expected, allowing one to deal
with higher-order JavaScript functions.
\begin{figure}[t]
\hrule
\begin{code}
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{code}
\hrule
\caption{Import entity notation for the JS calling convention}
\label{jsimpent}
\end{figure}
\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, like so: |function MyObj() {}|. Objects can then be
instantiated with the |new| keyword: |new MyObj()|. Each function also has a
prototype object. This prototype allows for defining values and functions
within the object scope. 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 now 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 -> 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 -> a
primSetProtoAttr :: JSString -> a -> JSString -> IO ()
primModProtoAttr :: JSString -> (a -> b) -> JSString -> IO ()
\end{code}
|JSString| is a type synonym for |PackedString|, which is used as the type for
JavaScript strings. The |primMkCtor| function creates a new constructor
function if it does not yet exist in the |window| scope. 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 new object. 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 in the shape of 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 can cause it to be of a different 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 in the form of 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 libraryl by a |JSPtr a| type,
which has no constructors, so it can't be instantiated directly. The only way
an object can be obtained is by getting it via the FFI. A |JSPtr a| requires
one type argument, 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 could 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. This is 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 just 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 are tasked with constructing 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 client-side, eliminating the need for many AJAX requests.
\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, amongst others. 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 then.
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. |valString| is a function that gets
the value of a \jq object as a |String|, while |alert| shows an alert box
containing the provided message. 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 biggest bottleneck seems to
be memory management. Building up lazy Haskell expressions leads to a large
amount 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, function 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}
% TODO: Include Fay
% \paragraph{Other approaches.}
The idea of running Haskell in a browser is not new. To our knowledge first
attempts to do so using JavaScript were done 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.
The idea of running functional programs in a browser even goes further back to
the availability of Java applets. 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 seems to be tied up to the iTasks system. The intermediate language
SAPL also does not provide the facilities as provided by our Haskell FFI.
%This limits used code to that which is generated by the iTasks system.
Of the GHC a version exists which generates JavaScript \cite{www11ghcjs-git},
based on the GHC API, supporting the use of primitives but not the FFI.
Further down we elaborate on some consequences of multiple platforms and
backends relevant for this GHC backend variant as well.
Both ``Functional javascript'' \cite{www07functional-javascript} and ``Haskell
in Javascript'' \cite{www10haskellinjavascript} do not use a separate Haskell
compiler. Instead, JavaScript is used directly in a functional style,
respectively a small compiler for a subset of Haskell has been written in
JavaScript.
A more recent attempt at cross-compiling Haskell to JavaScript it the Fay
language\cite{fay-lang}, which aims to support a subset of Haskell. It shows
promise, and even draws some inspiration from the work we present here, namely
the FEL.
%\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.
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, UHC and Cabal will need some more work as well.
%%]
%%[XX
%%]