Danny Yoo <firstname.lastname@example.org>
The GitHub source repository to Whalesong can be found at https://github.com/dyoo/whalesong.
hot-cross-buns.html [src index.html] Demonstrates use of checkboxes. Uses view-has-attr? to see if a checkbox has been checked, and remove-view-attr to change the checked attribute when the user wants to reset the page.
Please go to the Racket submenu.
Select the Limit Memory item.
Change the setting to Unlimited.
Usage: whalesong <subcommand> [option ...] <arg ...>
where any unambiguous prefix can be used for a subcommand
For help on a particular subcommand, use 'whalesong <subcommand> --help'
whalesong get-runtime print the runtime library to standard output
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.
Let’s try making a simple, standalone executable. At the moment, the program must be written in the base language of (planet dyoo/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.
#lang planet dyoo/whalesong (display "hello world") (newline)
$ racket hello.rkt
$ whalesong build hello.rkt
Writing program #<path:/home/dyoo/work/whalesong/examples/hello.js>
Writing html #<path:/home/dyoo/work/whalesong/examples/hello.html>
$ ls -l hello.html
-rw-r--r-- 1 dyoo dyoo 3817 2011-09-10 15:02 hello.html
$ ls -l hello.js
-rw-r--r-- 1 dyoo dyoo 841948 2011-09-10 15:02 hello.js
Visit hello.html to execute this program.
Visit dom-play.html to execute this program.
$ ls -l fact.js
-rw-r--r-- 1 dyoo dyoo 27421 2011-07-11 22:02 fact.js
$ whalesong get-runtime > runtime.js
$ ls -l runtime.js
-rw-r--r-- 1 dyoo dyoo 544322 2011-07-11 22:12 runtime.js
// Each module compiled with 'whalesong get-runtime' is treated as a
// main module. invokeMains() will invoke them.
// Grab the definition of 'fact'...
var myFactClosure = plt.runtime.lookupInMains('fact');
// And call it!
The factorial of 10000 is <span id="answer">being computed</span>.
To create HTML + js documents
$ whalesong build [name-of-racket-file]
The whalesong commands support these command line options:
--verboseWrite verbose debugging information to standard error.
--dest-dirWrite files to a separate directory, rather than the current directory.
--split-modulesWrite 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.
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.
|(require (planet dyoo/whalesong:1:=14/resource))|
(define-resource id [path-string])
#lang planet dyoo/whalesong (require (planet dyoo/whalesong/resource)) (define-resource my-whale-image-resource "humpback.png")
If the resource has the extension ".html", then it will be run through an HTML purifying process to make sure the HTML is well-formed.
(resource? x) → boolean x : any
#lang planet dyoo/whalesong (require (planet dyoo/whalesong/resource) (planet dyoo/whalesong/image)) (define-resource my-whale-image-resource "humpback.png") (define WHALE-IMAGE (bitmap/url (resource->url my-whale-image-resource)))
|(require (planet dyoo/whalesong:1:=14/web-world))|
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.
Visit tick-tock.html to execute this program.
<head><title>My simple program</title></head>
<p>The current counter is: <span id="counter">fill-me-in</span></p>
#lang planet dyoo/whalesong (require (planet dyoo/whalesong/web-world) (planet dyoo/whalesong/resource)) (define-resource index.html) ;; draw: world view -> view (define (draw world dom) (update-view-text (view-focus dom "counter") world)) ;; tick: world view -> world (define (tick world dom) (add1 world)) ;; stop?: world view -> boolean (define (stop? world dom) (> world 10)) (big-bang 0 (initial-view index.html) (to-draw draw) (on-tick tick 1) (stop-when stop?))
We require a few libraries to get us some additional behavior; in particular, (planet dyoo/whalesong:1:=14/web-world) to let us write event-driven web-based programs, and (planet dyoo/whalesong:1:=14/resource) to give us access to external resources.
We use define-resource to refer to external files, like "index.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).
5.1 big-bang and its options
(big-bang w h ...) → world w : world h : big-bang-handler
(initial-view x) → big-bang-handler x : any
(stop-when stop?) → big-bang-handler stop? : ([w world] [dom view] -> boolean)
... (define-struct world (given expected)) ... ;; stop?: world view -> boolean (define (stop? world dom) (string=? (world-given world) (world-expected world))) (big-bang ... (stop-when stop?))
(on-mock-location-change location-f) → big-bang-handler location-f : ([w world] [v view] [e event]? -> world)
During the extent of a big-bang, a form widget will appear in the document.body to allow us to manually send location-changing events.
(on-location-change location-f) → big-bang-handler location-f : ([w world] [v view] [e event]? -> world)
(to-draw draw-f) → big-bang-handler draw-f : ([w world] [v view] -> view)
... (define-struct world (name age)) ;; draw: world view -> view (define (draw world dom) (update-view-text (view-focus dom "name-span") (world-name world))) ... (big-bang ... (to-draw draw))
(->view x) → view x : any
(view-focus? v id) → boolean v : view id : String
(view-focus v id) → view v : view id : String
(view-left? v) → boolean v : view
(view-left v) → view v : view
(view-right? v) → boolean v : view
(view-right v) → view v : view
(view-up? v) → boolean v : view
(view-up v) → view v : view
(view-down? v) → boolean v : view
(view-down v) → view v : view
(view-forward? v) → boolean v : view
(view-forward v) → view v : view
(view-backward? v) → boolean v : view
(view-backward v) → view v : view
(view-text v) → string v : view
(update-view-text v s) → view v : view s : string
Attach a world-updating event to the focus. When the world-updater is called, the view will be focused on the element that triggered the event.
Common event types include "click", "mouseenter", "change".
(view-bind-many a-view [id type world-updater] ...)
(define (click-handler w v) ...) (define (change-handler w v) ...) (define-resource index.html) (define my-static-view (->view index.html)) (define connected-view (view-bind-many my-static-view ["id1" "click" click-handler] ["id2" "click" click-handler] ["id3" "change" change-handler])) ...
(view-bind-many* v id+type+updater-list) → view v : view id+type+updater-list : (listof (list string string world-updater))
(define (click-handler w v) ...) (define (change-handler w v) ...) (define-resource index.html) (define my-static-view (->view index.html)) (define connected-view (view-bind-many* my-static-view `(["id1" "click" ,click-handler] ["id2" "click" ,click-handler] ["id3" "change" ,change-handler]))) ...
(view-show v) → view v : view
(view-hide v) → view v : view
(view-attr v name) → view v : view name : String
(view-has-attr? v name) → boolean v : view name : String
(update-view-attr v name value) → view v : view name : String value : String
(remove-view-attr v name) → view v : view name : String
(view-css v name) → view v : view name : String
(update-view-css v name value) → view v : view name : String value : String
(view-id v) → world v : view
(view-form-value v) → view v : view
(update-view-form-value v value) → view v : view value : String
(view-append-child v d) → view v : view d : dom
(view-insert-left v d) → view v : view d : dom
(view-insert-right v d) → view v : view d : dom
(view-remove v) → view v : view
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.
(struct event (kvpairs) #:extra-constructor-name make-event) kvpairs : (listof (list symbol (or/c string number)))
#lang planet dyoo/whalesong (require (planet dyoo/whalesong/web-world)) ;; tick: world view -> world (define (tick world view) (add1 world)) ;; draw: world view -> view (define (draw world view) (view-append-child view (xexp->dom `(p "hello, can you see this? " ,(number->string world))))) (big-bang 0 (initial-view (xexp->dom '(html (head) (body)))) (on-tick tick 1) (to-draw draw))
Normally, we’ll want to do as much of the statics as possible with ".html" resources, but when nothing else will do, we can generate DOM nodes programmatically.
We can create new DOMs from an xexp, which is a s-expression representation for a DOM node. Here are examples of expressions that evaluate to xexps:
'(p "hello, this" "is an item")
'(div (@ (id "my-div-0")) (span "This is a span in a div"))
`(div (@ ,(fresh-id)) (span "This is another span in a div whose id is dynamically generated"))
( ‹id› ‹xexp›* )
( ‹id› ( @ ‹key-value›* ) ‹xexp›* )
( ‹symbol› ‹string› )
(xexp? x) → boolean x : any
(xexp->dom an-xexp) → dom an-xexp : xexp
(fresh-id) → string
(view->xexp a-view) → xexp a-view : view
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.
... ;; Redirect standard output to a div called "stdout-div". (current-output-port (open-output-element "stdout-div")) ... (big-bang ... (on-tick (lambda (world dom) (printf "Tick!\n") (add1 world))) ...)
All subsequent I/O side effects after the call to current-output-port will be written out to the stdout-div, which can be easily styled with display: none to hide it from normal browser display.
(open-output-element id) → output-port id : string
(alert msg) → void msg : string?
body : any/c
(call-method object method-name arg ...) → any/c object : any/c method-name : string? arg : any/c
($ locator) → any/c locator : any/c
(viewport-width) → number?
(viewport-height) → number?
Whalesong provides a library to support writing functional I/O programs (A Functional I/O System). Here’s an example of such a world program:
[FIXME: embed a world program here.]
Google Closure Compiler (http://code.google.com/p/closure-compiler/)
Google Closure Library (http://code.google.com/closure/library/) (avltree.js)
The following folks have helped tremendously in the implementation of Whalesong by implementing libraries, giving guidence, reporting bugs, and suggesting improvements.
Jens Axel Søgaard