Whalesong: a Racket to JavaScript compiler

1Introduction

Whalesong is a compiler from Racket to JavaScript; it takes Racket
programs and translates them so that they can run stand-alone on a
user’s web browser. It should allow Racket programs to run with
(hopefully!) little modification, and provide access through the
foreign-function interface to native JavaScript APIs. The included
runtime library supports the numeric tower, an image library, and a
framework to program the web in functional event-driven style.

This may take a few minutes, as Racket is compiling Whalesong, its
dependencies, and its documentation. When it finally finishes,
you should see a "whalesong" launcher in the current
directory.

You should also see a "whalesong-gui" launcher that includes
a minimal graphical user interface.

At this point, you should be able to run the "whalesong" executable from the command line.

$ ./whalesong

Usage: whalesong <subcommand> [option ...] <arg ...>

where any unambiguous prefix can be used for a subcommand

The Whalesong command-line tool for compiling Racket to JavaScript

For help on a particular subcommand, use 'whalesong <subcommand> --help'

whalesong buildbuild a standalone html and javascript package

whalesong get-runtimeprint the runtime library to standard output

whalesong get-javascriptGets just the JavaScript code and prints it to standard output

and if this does appear, then Whalesong should be installed successfully.

To repeat: whenever Whalesong’s source code is updated from Github,
please re-run the raco setup step. Otherwise, Racket will try to
recompile Whalesong on every single use, which can be very expensive.

2.2Making .html files with Whalesong

Let’s try making a simple, standalone executable. At the moment, the
program must be written in the base language of (planetdyoo/whalesong). This restriction unfortunately prevents arbitrary
racket/base programs from compiling at the moment;
the developers (namely, dyoo) will be working to remove this
restriction as quickly as possible.

This program uses the JQuery API provided by (planetdyoo/whalesong/js),
as well as the native JavaScript FFI to produce output on the browser.
If we run Whalesong on this program, and view the resulting "dom-play.html" in our
web browser, we should see a pale, green page with some output.

2.3Using Whalesong functions from JavaScript

Whalesong also allows functions defined from Racket to be used from
JavaScript. As an example, we can take the boring factorial
function and define it in a module called "fact.rkt":

Replacing the 10000 with "one-billion-dollars" should
reliably produce a proper error message.

3Using whalesong

Whalesong provides a command-line utility called whalesong for
translating Racket to JavaScript. It can be run in several modes:

To create HTML + js documents

To output the compiled JavaScript as a single ".js" file

Using whalesong to generate HTML+js documents is
relatively straightforward with the build command. To use it,
pass the name of the file to it:

$ whalesong build [name-of-racket-file]

A ".html" and ".js" will be written to the current directory, as will any external resources that the program uses.

The whalesong commands support these command line options:

--compress-javascript

Use Google Closure’s JavaScript
compiler to significantly compress the JavaScript. Using this
currently requires a Java 1.6 JDK.

--verbose

Write verbose debugging information to standard error.

--dest-dir

Write files to a separate directory, rather than the current directory.

--split-modules

Write each dependent module as a
separate file, rather than in one large ".js". This may be
necessary if your browser environment prohibits large ".js"
files. The files will be numbered starting from 1.

For more advanced users, whalesong can be used to generate
JavaScript in non-standalone mode. This gives the web developer more
fine-grained control over how to control and deploy the outputted
program.

3.1build

Given the name of a program, this builds
".html" and ".js" files into the current working directory.

The ".html" and ".js" should be self-contained, with an exception: if
the file uses any external resources by using
define-resource, those resources are written into the current
working directory, if they do not already exist there.

3.2get-javascript

Given the name of a program, writes the JavaScript to standard output,
as well as its dependent modules. The outputted file is meant to be
used as a SCRIPT source.

By default, the given program will be treated as a main module.
All main modules will be executed when the JavaScript function
plt.runtime.invokeMains() is called.

3.3get-runtime

Prints out the core runtime library that the files generated by
get-javascript depend on.

4Including external resources

Programs may need to use external file resources that aren’t
themselves Racket programs, but instead some other kind of data.
Graphical programs will often use ".png"s, and web-related
programs ".html"s, for example. Whalesong provides the
(planetdyoo/whalesong:1:=18/resource) library to refer and use these
external resources. When Whalesong compiles a program into a package,
these resources will be bundled alongside the JavaScript-compiled
output.

5The web-world API

The web-world library allows you to write functional event-driven
World programs for the web; the
user defines functional callbacks to handle events, and receive and
consume a world argument.

One difference introduced by the web is the web page itself: because
the page itself is a source of state, it too will be passed to
callbacks. This library presents a functional version of the DOM in
the form of a view.

We require a few libraries to get us some additional
behavior; in particular, (planetdyoo/whalesong:1:=18/web-world) to let
us write event-driven web-based programs, and (planetdyoo/whalesong:1:=18/resource)
to give us access to external resources.

We use define-resource to refer to external files, like "view.html" that
we’d like to include in our program.

We use big-bang to start up a computation that
responses to events. In this example, that’s clock ticks introduced
by on-tick, though because we’re on the web, we can
bind to many other kinds of web events (by using view-bind).

If the collection of ids, types, and handlers can’t be represented as a static list, then
view-bind-many* is an alternate helper function that may be helpful to bind
a bulk number of handlers to a view.

Remove the dom node at the focus from the view v. Focus tries to move
to the right, if there’s a next sibling. If that fails, focus then
moves to the left, if there’s a previous sibling. If that fails too,
then focus moves to the parent.

5.3Events

An event is a structure that holds name-value pairs.
Whenever an event occurs in web-world, it may include some auxiliary
information about the event. As a concrete example, location events
from on-location-change and on-mock-location-change
can send latitude and longitude values, as long as the world callback
can accept the event as an argument.

5.5Tips and tricks: Hiding standard output or directing it to an element

For a web-world program, output is normally done by using
to-draw. However, side effecting functions, such as
printf or display, are still available, and will
append to document.body.

We may want to disable such printing or redirect it to a particular
element on the page. For such purposes, use a combination of
current-output-port and open-output-element to
redirect the output of these side effect functions to somewhere else.

Given either a string representation of a JavaScript
function, or a javascript function object, returns a procedure that
can be called. The first two arguments to the function are special,
representing the success and fail continuations that continue the rest
of the computation.

Creates a new handler type that can be used with big-bang.
big-bang calls the first argument at the beginning of the
event-loop, and calls the second right before the event loop
terminates.

The setup and shutdown functions are usually
constructed with js-function->procedure in order to bind to
native JavaScript APIs.

The setup function is called with an JavaScript function
value that, when called, emits a new event into the world’s event
loop. The return value of the setup function will be saved,
and when the shutdown procedure calls, that value is passed
to it, with the intent that shutting down a service will likely
require information that’s produced at setup-time.

For example, we can reimplement some of the behavior of
on-location-change with the following: