sunroof-compiler-0.2: Monadic Javascript Compiler

Safe HaskellNone

Language.Sunroof

Contents

Description

Sunroof provides a way to express Javascript computations in Haskell. The computations can be expressed using the JS monad.

There are ready to use API bindings for frequently used Javascript:

  • Browser - Bindings of the standard browser APIs.
  • Canvas - Bindings of the HTML5 canvas element API.
  • JQuery - Bindings of some JQuery methods.
  • Date - Bindings of the standard data API.

It also provides an abstraction over Javascripts (not existing) threading model. Cooperative multithreading can be emulated using the Sunroof abstractions (forkJS, yield, loop). Equivalents of well-known Haskell concurrency abstractions like MVar or Chan are also provided on Javascript level through JSMVar and JSChan.

Due to the threading abstraction there are two kinds of computations. They are indicated by the first type parameter of JS (a T value). Normal Javascript computations that can be assumed to terminate and that may deliver a result value are written in the JSA monad. While possibly blocking computations (those that involve threading operations) are written in the JSB monad.

As the computations are expressed in Haskell, they have a functional nature. It is possible to change the attribute values of objects using := and #:

 o # att := val

If a top-level mutable variable is needed, use the JSRef abstraction. It is comparable to IORef.

Synopsis

Notes

It is advised to use Sunroof with the following language extensions:

  • OverloadedStrings - Enables using literal strings for attribute names and Javascript strings.
  • DataKinds - Enables using JS A or JS B instead of JSA and JSB. This extension is not essential.

Sunroof Compiler

sunroofCompileJSA :: Sunroof a => CompilerOpts -> String -> JS A a -> IO StringSource

The sunroof compiler compiles an effect that returns a Sunroof/JavaScript value into a JavaScript program. An example invocation is

 GHCi> import Language.Sunroof
 GHCi> import Language.Sunroof.JS.Browser
 GHCi> import Data.Default
 GHCi> txt <- sunroofCompileJSA def "main" $ do alert(js "Hello");
 GHCi> putStrLn txt
 var main = (function() {
   alert("Hello");
 })();

(The extra function and application are intentional and are a common JavaScript trick to circumvent scoping issues.)

To generate a function, not just an effect, you can use the function combinator.

 GHCi> txt <- sunroofCompileJSA def "main" $ do
            function $ \ n -> do
                return (n * (n :: JSNumber))
 GHCi> putStrLn txt
 var main = (function() {
   var v1 = function(v0) {
     return v0*v0;
   };
   return v1;
 })();

Now main in JavaScript is bound to the square function.

sunroofCompileJSB :: CompilerOpts -> String -> JS B () -> IO StringSource

Compiles code using the blocking threading model. Usage is the same as for sunroofCompileJSA.

data CompilerOpts Source

Options to setup the compiler.

Constructors

CompilerOpts 

Fields

co_on :: Bool

Do we reify to capture Haskell-level lets / CSEs?

co_cse :: Bool

Do we also capture non-reified CSE, using Value Numbering?

co_const :: Bool

Do we constant fold?

co_verbose :: Int

How verbose is the compiler when running? standard 0 - 3 scale

co_compress :: Bool

Does the compiler output code without whitespace and layout? default == False

Instances

Show CompilerOpts 
Default CompilerOpts

Default compiler options.

Classes

class Sunroof a whereSource

Central type class of Sunroof. Every type that can be translated into Javascript with Sunroof has to implement this type class.

Methods

box :: Expr -> aSource

Create a Sunroof value from a plain Javascript expression.

unbox :: a -> ExprSource

Reveal the plain Javascript expression that represents this Sunroof value.

typeOf :: Proxy a -> TypeSource

Returns the type of Javascript expression this Sunroof value represents. The default implementation returns Base as type.

Instances

Sunroof ()

Unit is a Sunroof value. It can be viewed as a representation of null or void.

Sunroof JSBool 
Sunroof JSObject 
Sunroof JSNumber

First-class values in Javascript.

Sunroof JSString

First-class Javascript value.

Sunroof JSConsole

First-class values in Javascript.

Sunroof JSDate

First-class values in Javascript.

Sunroof JSCanvas

First-class values in Javascript.

SunroofArgument a => Sunroof (JSContinuation a)

Functions are first-class citizens of Javascript. Therefore they are Sunroof values.

Sunroof a => Sunroof (JSRef a) 
Sunroof a => Sunroof (JSArray a)

Arrays are first-class Javascript values.

SunroofArgument o0 => Sunroof (JSChan o0) 
SunroofArgument o0 => Sunroof (JSMVar o0) 
(SunroofArgument a, Sunroof r) => Sunroof (JSFunction a r)

Functions are first-class citizens of Javascript. Therefore they are Sunroof values.

(SunroofKey k, Sunroof a) => Sunroof (JSMap k a) 

class SunroofValue a whereSource

All Haskell values that have a Sunroof representation implement this class.

Associated Types

type ValueOf a :: *Source

The Sunroot type that is equivalent to the implementing Haskell type.

Methods

js :: a -> ValueOf aSource

Convert the Haskell value to its Sunroof equivalent.

Instances

SunroofValue Bool 
SunroofValue Char

Create a single character JSString from a Char.

SunroofValue Double 
SunroofValue Float 
SunroofValue Int 
SunroofValue Integer 
SunroofValue ()

Unit is unit.

SunroofValue [Char]

Create a JSString from a String.

Integral a => SunroofValue (Ratio a) 
SunroofArgument a => SunroofValue (a -> JS B ())

JSFunctions may be created from Haskell functions if they have the right form.

(SunroofArgument a, Sunroof b) => SunroofValue (a -> JS A b)

JSFunctions may be created from Haskell functions if they have the right form.

class SunroofArgument args whereSource

Everything that can be used as argument to a function is Javascript/Sunroof.

Methods

jsArgs :: args -> [Expr]Source

Turn the argument into a list of expressions.

jsValue :: UniqM m => m argsSource

Create a list of fresh variables for the arguments.

typesOf :: Proxy args -> [Type]Source

Get the type of the argument values.

Instances

SunroofArgument ()

Unit is the empty argument list.

Sunroof a => SunroofArgument a

Every Sunroof value can be an argument to a function.

(Sunroof a, Sunroof b) => SunroofArgument (a, b)

Two arguments.

(Sunroof a, Sunroof b, Sunroof c) => SunroofArgument (a, b, c)

Three arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d) => SunroofArgument (a, b, c, d)

Four arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d, Sunroof e) => SunroofArgument (a, b, c, d, e)

Five arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d, Sunroof e, Sunroof f) => SunroofArgument (a, b, c, d, e, f)

Six arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d, Sunroof e, Sunroof f, Sunroof g) => SunroofArgument (a, b, c, d, e, f, g)

Seven arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d, Sunroof e, Sunroof f, Sunroof g, Sunroof h) => SunroofArgument (a, b, c, d, e, f, g, h)

Eight arguments.

(Sunroof a, Sunroof b, Sunroof c, Sunroof d, Sunroof e, Sunroof f, Sunroof g, Sunroof h, Sunroof i) => SunroofArgument (a, b, c, d, e, f, g, h, i)

Nine arguments.

class Sunroof o => JSTuple o whereSource

If something is a JSTuple, it can easily be decomposed and recomposed from different components. This is meant as a convenient access to attributes of an object. TODO: revisit this

Associated Types

type Internals o Source

Methods

match :: Sunroof o => o -> Internals oSource

tuple :: Internals o -> JS t oSource

class Sunroof key => SunroofKey key whereSource

Everything that can be used as an key in a dictionary lookup.

Methods

jsKey :: key -> JSSelector aSource

Types

data Type Source

Abstract types for Javascript expressions in Sunroof.

Constructors

Base

Base type like object or other primtive types.

Unit

Unit or void type. There is a effect but no value.

Fun [Type] Type

Function type: (t_1,..,t_n) -> t

Instances

data T Source

The possible threading models for Javascript computations.

Constructors

A

Atomic - The computation will not be interrupted.

B

Blocking - The computation may block and wait to enable interleaving with other computations.

Instances

Eq T 
Ord T 
Show T 

data ThreadProxy t Source

A proxy to capture the type of threading model used. See SunroofThread.

Constructors

ThreadProxy 

class SunroofThread t whereSource

When implemented the type supports determining the threading model during runtime.

Methods

evalStyle :: ThreadProxy t -> TSource

Determine the used threading model captured the given ThreadProxy object.

blockableJS :: Sunroof a => JS t a -> JS B aSource

Create a possibly blocking computation from the given one.

data JS whereSource

The monadic type of Javascript computations.

JS t a is a computation using the thread model t (see T). It returns a result of type a.

Constructors

JS :: ((a -> Program (JSI t) ()) -> Program (JSI t) ()) -> JS t a 
:= :: (Sunroof a, Sunroof o) => JSSelector a -> a -> o -> JS t () 

Instances

Monad (JS t) 
Functor (JS t) 
(SunroofThread t, Sunroof a, SunroofArgument a) => IfB (JS t a) 
Monoid (JS t ()) 
Semigroup (JS t a)

We define the Semigroup instance for JS, where the first result (but not the first effect) is discarded. Thus, <> is the analog of the monadic >>.

SunroofArgument a => SunroofValue (a -> JS B ())

JSFunctions may be created from Haskell functions if they have the right form.

(SunroofArgument a, Sunroof b) => SunroofValue (a -> JS A b)

JSFunctions may be created from Haskell functions if they have the right form.

type JSA a = JS A aSource

Short-hand type for atmoic Javascript computations.

type JSB a = JS B aSource

Short-hand type for possibly blocking Javascript computations.

data JSFunction args ret Source

Type of Javascript functions. The first type argument is the type of function argument. This needs to be a instance of SunroofArgument. The second type argument of JSFunction is the function return type. It needs to be a instance of Sunroof.

Instances

Show (JSFunction a r) 
(SunroofArgument a, Sunroof r) => IfB (JSFunction a r)

Functions may be the result of a branch.

(SunroofArgument a, Sunroof r) => Sunroof (JSFunction a r)

Functions are first-class citizens of Javascript. Therefore they are Sunroof values.

data JSContinuation args Source

Type of Javascript functions. The first type argument is the type of function argument. This needs to be a instance of SunroofArgument. The second type argument of JSFunction is the function return type. It needs to be a instance of Sunroof.

Instances

Show (JSContinuation a) 
(SunroofArgument a, Sunroof r) => IfB (JSContinuation a)

Functions may be the result of a branch.

SunroofArgument a => Sunroof (JSContinuation a)

Functions are first-class citizens of Javascript. Therefore they are Sunroof values.

data JSSelector a Source

A JSSelector selects a field or attribute from a Javascript object. The phantom type is the type of the selected value. Note the selected field or attributes may also array entries (index).

Instances

Show (JSSelector a) 
IsString (JSSelector a)

Selectors can be created from the name of their attribute.

DSL Primitives and Utilties

done :: JS t aSource

Abort the current computation at this point.

liftJS :: Sunroof a => JS A a -> JS t aSource

Lift the atomic computation into another computation.

function :: (SunroofArgument a, Sunroof b) => (a -> JS A b) -> JS t (JSFunction a b)Source

Create an Atomic Javascript function from a Haskell function.

continuation :: SunroofArgument a => (a -> JS B ()) -> JS t (JSContinuation a)Source

We can compile Blockable functions that return (). Note that, with the B-style threads, we return from a call when we first block, not at completion of the call.

apply :: (SunroofArgument args, Sunroof ret) => JSFunction args ret -> args -> JS t retSource

apply f a applies the function f to the given arguments a. A typical use case looks like this:

 foo `apply` (x,y)

See $$ for a convenient infix operator to do this.

($$) :: (SunroofArgument args, Sunroof ret) => JSFunction args ret -> args -> JS t retSource

f $$ a applies the function f to the given arguments a. See apply.

goto :: forall args a t. SunroofArgument args => JSContinuation args -> args -> JS t aSource

goto calls the given continuation with the given argument, and never returns.

cast :: (Sunroof a, Sunroof b) => a -> bSource

Cast one Sunroof value into another.

This is sometimes needed due to Javascripts flexible type system.

(#) :: a -> (a -> JS t b) -> JS t bSource

The #-operator is the Haskell analog to the .-operator in Javascript. Example:

 document # getElementById "bla"

This can be seen as equivalent of document.getElementById("bla").

attr :: String -> JSSelector aSource

Creates a selector for attributes of Javascript objects. It is advised to use this together with an associated type signature to avoid ambiguity. Example:

 length :: JSSelector JSNumber
 length = attr "length"

Selectors can be used with !.

fun :: (SunroofArgument a, Sunroof r) => String -> JSFunction a rSource

Create a binding to a Javascript top-level function with the given name. It is advised to create these bindings with an associated type signature to ensure type safty while using this function. Example:

 alert :: JSFunction JSString ()
 alert = fun "alert"

invoke :: (SunroofArgument a, Sunroof r, Sunroof o) => String -> a -> o -> JS t rSource

invoke s a o calls the method with name s using the arguments a on the object o. A typical use would look like this:

 o # invoke "foo" (x, y)

Another use case is writing Javascript API bindings for common methods:

 getElementById :: JSString -> JSObject -> JS t JSObject
 getElementById s = invoke "getElementById" s

Like this the flexible type signature gets fixed. See # for how to use these bindings.

new :: SunroofArgument a => String -> a -> JS t JSObjectSource

new n a calls the new operator on the constructor n supplying the argument a. A typical use would look like this:

 new "Object" ()

evaluate :: Sunroof a => a -> JS t aSource

Evaluate a Sunroof value. This forces evaluation of the given expression to a value and enables binding it to a variable. Example:

 x <- evaluate $ "A" <> "B"
 alert x
 alert x

This would result in: var v0 = "A"+"B"; alert(v0); alert(v0);. But:

 x <- return $ "A" <> "B"
 alert x
 alert x

This will result in: alert("A"+"B"); alert("A"+"B");.

value :: Sunroof a => a -> JS t aSource

Synonym for evaluate.

switch :: (EqB a, BooleanOf a ~ JSBool, Sunroof a, Sunroof b, SunroofArgument b, SunroofThread t) => a -> [(a, JS t b)] -> JS t bSource

Combinator for switch-like statements in Javascript.

Note: This will not be translated into actual switch statment, because you are aloud arbitrary expressions in the cases.

nullJS :: JSObjectSource

The null reference in Javascript.

label :: JSString -> JSSelector aSource

Create a selector for a named field or attribute. For type safty it is adivsed to use this with an accompanying type signature. Example:

 array ! label "length"

See ! for further information on usage.

index :: JSNumber -> JSSelector aSource

Create a selector for an indexed value (e.g. array access). For type safty it is adivsed to use this with an accompanying type signature. Example:

 array ! index 4

See ! for further information on usage.

(!) :: forall o a. (Sunroof o, Sunroof a) => o -> JSSelector a -> aSource

Operator to use a selector on a Javascript object. Examples:

 array ! label "length"
 array ! index 4

callcc :: SunroofArgument a => (JSContinuation a -> JS B a) -> JS B aSource

Reify the current contination as a Javascript continuation

comment :: String -> JS t ()Source

Write a JavaScript comment into the generated source.

delete :: Sunroof a => JSSelector a -> JSObject -> JS t ()Source

o # delete lab removes the label lab from the object o.

Concurrency Primitives

forkJS :: SunroofThread t1 => JS t1 () -> JS t2 ()Source

Fork of the given computation in a different thread.

threadDelay :: JSNumber -> JSB ()Source

Delay the execution of all instructions after this one by the given amount of milliseconds.

yield :: JSB ()Source

Give another thread time to execute.

Basic JS types

JavaScript Object

data JSObject Source

Data type for all Javascript objects.

Instances

Show JSObject 
IfB JSObject 
EqB JSObject

Reference equality, not value equality.

Sunroof JSObject 
JSTuple JSObject 

this :: JSObjectSource

The this reference.

object :: String -> JSObjectSource

Create an arbitrary object from a literal in form of a string.

Boolean

data JSBool Source

Booleans in Javascript.

Numbers

int :: Sunroof a => a -> JSNumberSource

A explicit cast to int.

Strings

data JSString Source

Javascript string type.

Instances

Show JSString

Show the Javascript.

IsString JSString

Create them from Haskell Strings.

IfB JSString 
EqB JSString

Value equality.

Monoid JSString

Monoid under concatination and empty string.

Semigroup JSString

Semigroup under concatination.

Sunroof JSString

First-class Javascript value.

SunroofKey JSString 

string :: String -> JSStringSource

Create a Javascript string from a Haskell string.

Array

data JSArray a Source

Type if arrays in Javascript. The type parameter given the entry type.

Instances

Show (JSArray a)

Show the Javascript.

Sunroof a => IfB (JSArray a)

You can write branches that return arrays.

Sunroof a => Sunroof (JSArray a)

Arrays are first-class Javascript values.

array :: (SunroofValue a, Sunroof (ValueOf a)) => [a] -> JS t (JSArray (ValueOf a))Source

Create a literal array from a Haskell list.

newArray :: (SunroofArgument args, Sunroof a) => args -> JS t (JSArray a)Source

Create a new array object containing the given values.

length' :: JSSelector JSNumberSource

The length property of arrays.

lookup' :: Sunroof a => JSNumber -> JSArray a -> aSource

A type-safe version of array lookup.

insert' :: Sunroof a => JSNumber -> a -> JSArray a -> JS t ()Source

A type-safe version of array insert.

shift :: Sunroof a => JSArray a -> JS t aSource

Removes and return the first element of an array (dequeue). See http://www.w3schools.com/jsref/jsref_shift.asp.

unshift :: (SunroofArgument a, Sunroof a) => a -> JSArray a -> JS t ()Source

Adds a new element to the beginning of the array (queue). Returns nothing instead of the new length. See http://www.w3schools.com/jsref/jsref_unshift.asp.

pop :: Sunroof a => JSArray a -> JS t aSource

Pop a element from the array as if it was a stack. See http://www.w3schools.com/jsref/jsref_pop.asp.

push :: (SunroofArgument a, Sunroof a) => a -> JSArray a -> JS t ()Source

Push a element into the array as if it was a stack. Returns nothing instead of the new length. See http://www.w3schools.com/jsref/jsref_push.asp.

forEach :: (Sunroof a, SunroofArgument a) => (a -> JS A ()) -> JSArray a -> JS t ()Source

Foreach iteration method provided by most browsers. Execute the given action on each element of the array. See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach, http://msdn.microsoft.com/en-us/library/ie/ff679980.aspx.

empty :: Sunroof a => JS t (JSArray a)Source

The empty array.

References

data JSRef a Source

This is the IORef of Sunroof.

Instances

Sunroof a => Show (JSRef a) 
Sunroof a => IfB (JSRef a) 
Sunroof a => EqB (JSRef a)

Reference equality, not value equality.

Sunroof a => Sunroof (JSRef a) 

newJSRef :: Sunroof a => a -> JS t (JSRef a)Source

Create a new JSRef with the given intial value.

readJSRef :: Sunroof a => JSRef a -> JS t aSource

Non-blocking read of a JSRef.

writeJSRef :: Sunroof a => a -> JSRef a -> JS t ()Source

Non-blocking write of a JSRef.

modifyJSRef :: Sunroof a => (a -> JS A a) -> JSRef a -> JS t ()Source

Non-blocking modification of a JSRef.

Channnels

data JSChan a Source

JSChan abstraction. The type parameter gives the type of values held in the channel.

Instances

SunroofArgument o0 => Show (JSChan o0) 
SunroofArgument o0 => IfB (JSChan o0) 
SunroofArgument o => EqB (JSChan o)

Reference equality, not value equality.

SunroofArgument o0 => Sunroof (JSChan o0) 
SunroofArgument o0 => JSTuple (JSChan o0) 

newChan :: SunroofArgument a => JS t (JSChan a)Source

Create a new empty JSChan.

writeChan :: forall t a. (SunroofThread t, SunroofArgument a) => a -> JSChan a -> JS t ()Source

Put a value into the channel. This will never block.

readChan :: forall a. (Sunroof a, SunroofArgument a) => JSChan a -> JS B aSource

Take a value out of the channel. If there is no value inside, this will block until one is available.

Thread-safe Mutable Variables

data JSMVar a Source

JSMVar abstraction. The type parameter gives the type of values held in a JSMVar.

Instances

SunroofArgument o0 => Show (JSMVar o0) 
SunroofArgument o0 => IfB (JSMVar o0) 
SunroofArgument o => EqB (JSMVar o)

Reference equality, not value equality.

SunroofArgument o0 => Sunroof (JSMVar o0) 
SunroofArgument o0 => JSTuple (JSMVar o0) 

newMVar :: forall a t. SunroofArgument a => a -> JS t (JSMVar a)Source

Create a new JSMVar with the given value inside. See newEmptyMVar.

newEmptyMVar :: SunroofArgument a => JS t (JSMVar a)Source

Create a new empty JSMVar. See newMVar.

takeMVar :: forall a. (Sunroof a, SunroofArgument a) => JSMVar a -> JS B aSource

Take the value out of the JSMVar. If there is no value inside, this will block until one is available.

putMVar :: forall a. SunroofArgument a => a -> JSMVar a -> JS B ()Source

Put the value into the JSMVar. If there already is a value inside, this will block until it is taken out.

DSL Utilties

loop :: Sunroof a => a -> (a -> JSB a) -> JSB ()Source

loop x f executes the function f repeatedly. After each iteration the result value of the function is feed back as input of the next iteration. The initial value supplied for the first iteration is x. This loop will never terminate.

fixJS :: SunroofArgument a => (a -> JSA a) -> JS t aSource

jsfix is the mfix for the JS Monad.