Difference between revisions of "Yhc/Javascript"
(Oversaturation 2) |
m (Typo corrected) |
||
Line 221: | Line 221: | ||
To preserve laziness, the thunk of <code>g</code> should not be evaluated earlier than it is actually needed, i. e. when the value of <code>x</code> is needed. Calling the _ap method of <code>g</code> would have resulted in oversaturation and inevitable error in computations. |
To preserve laziness, the thunk of <code>g</code> should not be evaluated earlier than it is actually needed, i. e. when the value of <code>x</code> is needed. Calling the _ap method of <code>g</code> would have resulted in oversaturation and inevitable error in computations. |
||
− | To work this around, logics of HSFun._ap method was changed, and special object type HSDly was introduced. HSFun._ap may accept any number of arguments in an array. Length of the array of arguments is compared with number of arguments needed to saturate the call. If the call becomes under- or completely saturated after the arguments have been applied to, no special action is taken: undersaturated call remains the same, saturated is wrapped in HSDly. If however oversaturation is about to happen, portion of arguments necessary to saturate the call is absorbed into the accumulated |
+ | To work this around, logics of HSFun._ap method was changed, and special object type HSDly was introduced. HSFun._ap may accept any number of arguments in an array. Length of the array of arguments is compared with number of arguments needed to saturate the call. If the call becomes under- or completely saturated after the arguments have been applied to, no special action is taken: undersaturated call remains the same, saturated is wrapped in HSDly. If however oversaturation is about to happen, portion of arguments necessary to saturate the call is absorbed into the accumulated arguments array, and the rest of arguments are carried over to the HSDly._ap method. |
Behavior of HSDly objects is as follows: the _ap method accepts as many arguments as provided, and accumulates them inside. The _c method evaluates the delayed thunk first, and then calls its _ap method with all the arguments accumulated up to the moment. In this case it is expected that the delayed thunk will evaluate into another function call (as can be seen in the example above). These actions may lead to either a value computed as result of application, or another function call, under-or completely or over-saturated. In the two latter cases, the result will be wrapped into another HSDly object with arguments remaining carried over. |
Behavior of HSDly objects is as follows: the _ap method accepts as many arguments as provided, and accumulates them inside. The _c method evaluates the delayed thunk first, and then calls its _ap method with all the arguments accumulated up to the moment. In this case it is expected that the delayed thunk will evaluate into another function call (as can be seen in the example above). These actions may lead to either a value computed as result of application, or another function call, under-or completely or over-saturated. In the two latter cases, the result will be wrapped into another HSDly object with arguments remaining carried over. |
Revision as of 13:52, 8 November 2006
Brief Overview
An experimental sub-project, Yhc Core to Javascript Converter (ycr2js), is aimed to create a tool that generates Javascript out of a binary Yhc core file.
The project was started as an experimental patch to nhc98 in attempt to convert its internal PosLambda constructs into Javascript expressions. After some initial success, the project was switched to use the Yhc Core as the source for transformation. Recently, with a great amount of help from the Yhc Team, the project has been integrated into the main Yhc source tree and is moving towards closer integration with the compiler.
Ability to convert an arbitrary Haskell source into Javascript makes it possible to execute Haskell programs in a Web browser. This, in turn, allows for development of both client and server sides of an Internet application entirely in Haskell.
Server side solutions in Haskell have been around for a while, such as HAppS -- Haskell Application Server, Haskell Server Pages, and others. For the client side, HSPClientSide has been recently introduced, which is a close analog to ycr2js. HSPClientSide provides a domain-specific language to define the client side Web page structure (static HTML and Javascript). On the contrary, ycr2js helps convert any compilable Haskell source into Javascript.
Principles of Operation
The Yhc compiler generally produces a binary bytecode file (usually named with .hbc extension) for each Haskell module compiled. These bytecode files are to be interpreted by yhi, a command-line bytecode interpreter.
The compiler is also capable of producing a binary core file (usually named with .ycr extension), and also its human-readable representation for each Haskell module compiled. The internal structure of core is based on significantly simplified nhc98's PosLambda constructs (Yhc is derived from nhc98 code). Core consists of definitions for compiled Haskell functions and data objects.
The feature of core linking was added recently to Yhc. This allows for merging core files from several modules together, removing functions that are not used (similar to static linking performed by a traditional Unix or Windows executable linker). The resulting file (usually named with .yca extension) has the same format as per-module core files.
Binary core files may be read back into computer memory using the Yhc Core API functions.
The ycr2js program reads the binary core file specified (.yca or .ycr), and performs conversion of Haskell functions compiled into Core to their Javascript representation storing the generated Javascript code in a file. Resulting Javascript may be embedded on a (X)HTML page to be loaded into a Web browser.
Users Guide
Note: ycr2js is currently a standalone program distributed within the Yhc source tree. The usage guidelines below are relevant only for the standalone version. If ycr2js gets integrated in Yhc tighter than now, some or all of the following statements may be no longer applicable ansd will be updated accordingly.
Downloading
The ycr2js program along with additional tools is distributed within the Yhc source tree. Download and install Yhc as recommended here and here. Include the core=1
options on the scons
command line when building Yhc to generate Core for all Haskell library modules. The yhc
and ghc
executables should be on the PATH after the installation is coplete.
Building and Installation on Unix
Change from the toplevel directory of the Yhc source tree to the src/translator/js
directory. Execute commands:
make all
make install
make test
All three commands should finish without error.
Building and Installation on Windows
This currently does not work, but will do once the Scons build system that Yhc uses has been integrated.
What Is Installed
The Makefile
in the ycr2js directory instructs make
to place all necessary files relatively to the yhc
executable location. After the installation of ycr2js, the directory structure will be as follows (assuming that prefix is the base path of the Yhc installation):
prefix/bin | |||
ycr2js | Core to Javascript Converter | ||
pgbuild | XHTML Page Building Utility | ||
prefix/lib | |||
haskell | |||
StdOverlay.hs | Standard Javascript Core Overlay | ||
UnsafeJS.hs | Contains unsafeJS , a pseudo-function to inline Javascript code in Haskell functions
| ||
javascript | |||
Runtime.js | Runtime Support Javascript Library, needs to be included with every XHTML page generated | ||
xhtml | |||
emptyx.html | An Empty XHTML Page template |
The structure shown above represents a "bare" installation essential for ycr2js to function properly. Actual installation may include more files than these.
The base location of Yhc installation will be further on referred as prefix unless otherwise stated.
Compiling Haskell into Core
In order to obtain a linked Core file for a Haskell source(s), the Yhc compiler should be run like this:
yhc -includes prefix/lib/haskell \
[-includes other include directories] \
-linkcore Main.hs [other Haskell source files]
Other yhc options may be used as needed, but specifying the prefix/lib/haskell
directory with the -include
option is important. The -linkcore
option instructs Yhc to link all core files generated together, along with library modules used. It is assumed that the user program's main module is named Main
, and its entry point is main
. Type signature of Main.main
does not matter.
Converting Core into Javascript
Building a XHTML Page
Tools Summary
Sample Makefile
Inner Workings
In this section, internal structure of Javascript objects and runtime support algorithms is reviewed.
Javascript Objects
The table below summarizes types of Javascript objects used in the ycr2jsgenerated Javascript code.
Member/ Constructor |
Prop Meth Constr |
H S C o n s |
H S E O L |
H S F u n |
H S D l y |
H S D a t a |
Description/Arguments | |||
---|---|---|---|---|---|---|---|---|---|---|
HSCons | C | * | Builds a list CONS cell | head: head element |
tail: remainder of the list | |||||
HSEOL | C | * | Final element of a list or an empty list | |||||||
HSFun | C | * | Creates a function thunk with no arguments applied to | name: function name to be used for debugging/exception tracing |
arity: arity of the function known by the compiler |
body: expression to apply to function's arguments and evaluate | ||||
HSDly | C | * | A special object to wrap around a saturated function call | thunk: saturated function call that is a HSFun object with number of arguments applied to (_a) equal to the function arity (_x); evaluation of this thunk will be delayed until it is applied to an argument which would have oversaturated the call in the absence of HSDly | ||||||
HSData | C | * | Builds a data object other than a CONS cell or an Empty List | con: constructor name (with non-alphanumeric characters replaced with underscored character codes) |
arrs: a Javascript Array containing contructor arguments | |||||
_r | P | * | * | * | * | * | Boolean: true when a thunk may be evaluated.
| |||
_c | M | * | * | * | * | * | Evaluate a thunk. If this method is said as "has no action", this means that it just returns this and does nothing else.
| |||
_a | P | * | * | Array:
| ||||||
_ap | M | * | * | Apply function call/delayed saturated call to argument(s)
|
targs: Array containing the arguments to be applied to | |||||
_b | P | * | Holds the expression to apply to function's arguments and evaluate: the third argument of the HSFun constructor is copied here | |||||||
_x | P | * | Holds the function arity: the second argument of the HSFun constructor is copied here | |||||||
_n | P | * | Holds the function name: the first argument of the HSFun constructor is copied here | |||||||
_d | P | * | Holds the saturated function call (HSFun object with _a.length == _x : the first argument of the HSDly constructor is copied here
| |||||||
_t | P | * | * | * | Constructor name for a Data or a CONS/Empty list cell to be used for pattern matching
| |||||
_f | P | * | * | * | Constructor arguments (may be empty)
| |||||
toString | M | * | Method of Object overridden by HSCons. Used for unmarshalling of Haskell lists (including Strings) into Javascript as Strings. The method evaluates all elements of the list (therefore it should be finite) and if the list contains characters, they are concatenated into a Javascript String, otherwise the _toArray method is called, and the toString method is called upon _toArray's result. | |||||||
_toArray | M | * | Method used for unmarshalling of Haskell lists (including Strings) into Javascript as Arrays. The method evaluates all elements of the list (therefore it should be finite) and concatenates them into a Javascript Array. Internal representation of Haskell type Char is its numeric value, so a Haskell String will be converted into a Javascript Array of Number 's.
|
Evaluation of Expressions
The Javascript runtime support library provides a function exprEval
which is used to evaluate all expressions starting with the toplevel expression (starting point).
In essence, this function looks like this:
function exprEval (e) {
for (var ex = e; ex != undefined && ex._r ; ex = ex._c())
;
return ex;
};
This is a loop that checks whether a given expression exists (not undefined
) and can be evaluated (_r == true)
. In this case, it calls the expression's _c method and analyzes its return. If the returned expression also may be evaluated, the function loops and evaluates it. This is repeated until an expression that no longer can be evaluated is returned (normal situation, e. g. a primitive value or a Data object), or an undefined
value is returned (this is abnormal situation).
While evaluating an expression, exprEval
may be recursively called to evaluate nested expressions.
Special Notes
Oversaturation
Oversaturation happens when a function thunk (HSFun object) is applied to more arguments than function arity (_x).
For example, this piece of Haskell code:
let g = fst tup x = g (5::Int) (6::Int)
converts into the following Javascript (indentation manual):
var Bug_46Bug_46Prelude_46217_46g=
new HSFun("Bug.Bug.Prelude.217.g", 0, function(){
var v285=new HSFun("v285", 0, function(){
return Prelude_46Prelude_46Num_46Prelude_46Integer;}
);
return (Prelude_46fst)._ap([(Bug_46tup)._ap([v285])]);
});
var Bug_46Bug_46Prelude_46218_46x=
new HSFun("Bug.Bug.Prelude.218.x", 0, function(){
return (Bug_46Bug_46Prelude_46217_46g)._ap([5, 6]);
});
As it can be clearly seen, g
is defined with arity = 0 which is reasonable: its definition does not have any formal arguments. But when computing x
, g
is called with two arguments: 5 and 6.
To preserve laziness, the thunk of g
should not be evaluated earlier than it is actually needed, i. e. when the value of x
is needed. Calling the _ap method of g
would have resulted in oversaturation and inevitable error in computations.
To work this around, logics of HSFun._ap method was changed, and special object type HSDly was introduced. HSFun._ap may accept any number of arguments in an array. Length of the array of arguments is compared with number of arguments needed to saturate the call. If the call becomes under- or completely saturated after the arguments have been applied to, no special action is taken: undersaturated call remains the same, saturated is wrapped in HSDly. If however oversaturation is about to happen, portion of arguments necessary to saturate the call is absorbed into the accumulated arguments array, and the rest of arguments are carried over to the HSDly._ap method.
Behavior of HSDly objects is as follows: the _ap method accepts as many arguments as provided, and accumulates them inside. The _c method evaluates the delayed thunk first, and then calls its _ap method with all the arguments accumulated up to the moment. In this case it is expected that the delayed thunk will evaluate into another function call (as can be seen in the example above). These actions may lead to either a value computed as result of application, or another function call, under-or completely or over-saturated. In the two latter cases, the result will be wrapped into another HSDly object with arguments remaining carried over.
Examples and Demos
- Echo
Description: Type any text in the input field provided, and see it echoed right above.
Tested with: Netscape 7/Linux
Demo URL: http://darcs.haskell.org/yhc/web/jsdemos/Echo.html
Haskell Source: http://darcs.haskell.org/yhc/web/jsdemos/Echo.hs
--DimitryGolubovsky 19:02, 6 November 2006 (UTC)