Yhc/Javascript/Programmers guide
From HaskellWiki
Contents |
1 Up from the ground
This part of the Programmers Guide is a collection of notes taken during the development of the first Haskell-in-Browser demo, a program that accepts users' input using a HTML input element, and repeats whatever the user typed upon pressing Enter. Additionally, Roman numeral conversion will occur if user's input is a decimal or a Roman numeral that can be converted. A timer is provided to measure code performance. More...
2 DOM framework
In this section of the Yhc/Javascript Programmers Guide, the implementation of Document Object Model in Haskell is described. Continuation Passing Style usage is discussed. The section provides details on conversion of DOM specifications from Interface Definition Language to Haskell, and related issues and features. Finally, examples of Haskell programming with DOM are provided.
2.1 Continuation passing style
2.1.1 Rationale
Unlike the previous Echo example, the DOM framework uses CPS rather than monads to provide proper sequence of Haskell expressions evaluation. The choice of CPS is dictated by the internal structure of Fudget kernels which use CPS. An original Fudget (built on top of the X11 protocol and related I/O) sends a message to Fudlogue each time an input/output action is needed (even one not involving waiting for any asyncronous input, such as opening a window). With DOM interface implemented in CPS style, all synchronous operations (such as creating a DOM node, and basically all operations not involving event handling) can be performed without such message exchange, which significantly reduces execution overhead.
2.1.2 Wrapper functions
A function conforming the Continuation Passing Style always has as its last argument, continuation, which will take the result of this function's application to its other arguments, as an argument. Any non-CPS expression may be converted into a CPS one by applying a wrapper which transforms the expression into a function with one argument:
toCPS x = \k -> k x
A variant of this wrapper:
toCPE x = \k -> x `seq` (k x)
forces evaluation of the expression before passing it to the continuation.
Consider obtaining a current date and time from the browser. Browser provides a Javascript function new Date().getTime() for this purpose. So, at the first look the following might be enough:
getTimeStamp' a = unsafeJS "return new Date().getTime();"
getTimeStamp k = k `seq` (getTimeStamp' 0)
So, in a larger example:
main = getTimeStamp $ \t1 -> foo $ \_ -> bar $ \_ -> getTimeStamp $ \t2 -> putLine ("Time interval: " ++ show (t2 - t1) ++ " ms") $ id
putLine ("Time interval: " ++ show (t2 - t1) ++ " ms") $ \_ -> (t2 - t1)
In general, the example above gives some idea how Haskell programs using DOM in CPS style look like.
Thetype CPS c a = (a -> c) -> c
2.1.3 Unsafe interfaces with CPS
Usage ofTo access properties of Javascript objects, the following CPS-aware functions are provided:
unsafeGetProperty :: String -> b -> CPS d c unsafeSetProperty :: String -> b -> c -> CPS d c
The first function accepts Javascript property name as its first argument, and a reference to a Javascript object as the second. It passes the value of the property retrieved (in type-agnostic manner) to its continuation.
The second function accepts Javascript property name as its first argument, the value to set the property to as the second argument, and a reference to a Javascript object as the third. The continuation gets the reference to the Javascript object with updated property (that is, the update occurs in-place).
Both functions evaluate their arguments.
To unsafely convert Javascript values to HaskellunsafeToNum :: (Num b) => a -> CPS c b unsafeToString :: a -> CPS c String
The first function calls the Number Javascript constructor on the argument's value, the second calls the String Javascript constructor on its argument. Both functions evaluate their argument first.
To catch exceptions, the following function is provided:
catchJS :: a -> (b -> a) -> a
throw statement.
2.1.4 Programming examples
The EchoCPS Wiki page contains an example of a working Echo demo program written using the DOM interfaces.
2.2 DOM and the Web Consortium
The Document Object Model (DOM) is the base interface to access the content and structure of documents in a web browser. The Web Consortium has a page dedicated to DOM.
This Programmers Guide is based on the Document Object Model (DOM) Level 1 Specification (Second Edition) provided by the Web Consortium. This version of DOM, although not very new, can serve as the greatest common denominator for many types of web browsers available these days.
2.3 DOM and Interface Definition Language (IDL)
2.3.1 General information
The Web Consortium uses a subset of the Interface Definition Language proposed by the Object Management Group (OMG IDL) to describe the abstract interface to the Document Object Model, so it may be implemented in various programming languages. These definitions cover basic operations to create and delete document nodes, manipulate their attributes and contents, and insert/remove nodes within the document loaded into the browser.
2.3.2 Conversion to Haskell
In accordance with the Web Consortium Copyright Notice, IDL files provided by the Web Consortium may be freely redistributed by anybody. So, copy of these files is included with the Yhc Javascript Backend. A modified version of the HaskellDirect (trimmed down to only OMG IDL code symtax recognition, and with different model of Haskell code generation) is also included. This HaskellDirect-based utility runs automatically when the Javascript Backend is being installed, so the installation includes Haskell code autogenerated from the IDL files. Developers who define new interfaces on the browser side to be used with the Javascript Backend are encouraged to write their own IDL files, and use the same utility to produce Haskell interface code.
2.3.3 Technical details of IDL to Haskell conversion
This section gives general details of correspondence between IDL definitions and generated Haskell code. Deeper details related to programming will be discussed in next sections.
Consider this IDL definition (from the DOM section of the definitions):
interface Attr : Node {
readonly attribute DOMString name;
readonly attribute boolean specified;
// Modified in DOM Level 1:
attribute DOMString value;
// raises(DOMException) on setting
};
One interface definition in IDL results in creation of one Haskell module with the same name as the interface has. Module name will be prefixed with
#pragma prefix "w3c.org"
in the beginning of the file is ignored.
The Haskell translation is:
module DOM.Level1.Attr (get'name, get'specified, set'value, get'value) where import DOM.Level1.Dom import CPS import UnsafeJS import DOM.Level1.Document (createElement) get'name :: (CAttr this) => this -> CPS c String get'name = unsafeGetProperty "name" get'specified :: (CAttr this) => this -> CPS c Bool get'specified = unsafeGetProperty "specified" set'value :: (CAttr zz) => String -> zz -> CPS c zz set'value = unsafeSetProperty "value" get'value :: (CAttr this) => this -> CPS c String get'value = unsafeGetProperty "value"
data TAttr = TAttr -- phantom type for the interface class (CNode a) => CAttr a -- class reflecting inheritance from Node instance CAttr TAttr -- interfaces of Attr are implemented instance CNode TAttr -- interfaces of Node are implemented
readonly in their definitions only have getter methods (e. g. The Attr interface does not have methods, only attributes. The following interface illustrates how methods are represented:
interface NodeList {
Node item(in unsigned long index);
readonly attribute unsigned long length;
};
module DOM.Level1.NodeList (item, get'length) where import DOM.Level1.Dom import CPS import UnsafeJS import DOM.Level1.Document (createElement) item :: (CNodeList this, CNode zz) => this -> Int -> CPS c zz item a b = toCPE (item' a b) item' a b = unsafeJS "return((exprEval(a)).item(exprEval(b)));" get'length :: (CNodeList this) => this -> CPS c Int get'length = unsafeGetProperty "length"
NodeList) as the first argument, NodeList, corresponding to the in unsigned long index in the IDL definition. The last argument is the continuation. Type constraints Body of the method contains a type-aware wrapper over the unsafe code calling appropriate item method on the Javascript object implementing the NodeList interface.
2.3.4 Known omissions
- Exception information (
raises...) is completely ignored by the converter. If an exception in Javascript code occurs, it should be treated as described above, in the Unsafe interfaces with CPS section.
- The converter makes no distinction between
inandoutarguments.
- The converter does not tolerate multiple methods with the same name, but different number of arguments, within a single interface. It is however possible to have methods with the same name (regardless of number of arguments) in different interfaces. The andfocusmethods serve as a good example: they appear in at least two HTML elements: <input> and <textarea>. Developers are recommended to useblurstatement for importing modules with conflicting method names, and use qualified names to resolve ambiguities.import qualified
2.4 Haskell DOM vs. Javascript DOM
2.4.1 Haskell phantom types vs. Javascript object types
2.4.2 Haskell type classes reflect inheritance
2.5 The "defaulting" problem and DOM utility functions
2.6 Programming examples
3 Fudgets On web
(once we implement that)
