heap.coffee

heap.coffee is a dialect
of CoffeeScript that offers a
C-like type system with manual memory management. It compiles to
JavaScript and lets you write memory-efficient and GC pause-free code
less painfully. heap.coffee, like its
relative *JS, is early
research prototype work, so don't expect anything rock solid just yet.
The research goal here is to explore low-level statically typed features
in a high-level dynamically typed language.

This is an interactive tutorial, code is compiled as you type. To
execute a piece of code press Ctrl-R or Cmd-Enter.

Type Declarations and Synonyms

Type declarations are done using :: . To disambiguate the
prototype operator ::, the "is-type" operator must be spaced on
either side.

Local variables and destructuring left-hand sides may be typed. Though both
sides of a destructuring type declaration must match up. That is, you cannot
declare a plain variable to be of a destructuring type.

Data Types and Arithmetic

Like *JS, heap.coffee has 6 integral
types: i32, u32, i16, u16, i8,
and u8, which behave as they do in C. Additionally there
is double, the JavaScript number type, and any which are
used to interoperate with the JavaScript type system. Arithmetic operations
usually produce the double type even if their operands are integral
types due to possible overflow. heap.coffee emits the appropriate coercions
for assignments and cast operations.

heap.coffee allows you to define struct types, which is really the main
achievement of all these types.

type point = struct
x :: int
y :: int
z :: int
type line = struct
start :: point
end :: point
type box = struct
left :: line
top :: line
right :: line
bottom :: line
# Stack allocate a box and a take a pointer to its
# top line.
b :: box
p :: *line
p = &b.top
# Dot notation is overloaded to also work with struct
# pointers.
p.start.x = 42
print p.start.x

Functions

Since all functions in CoffeeScript are lambdas, the type of a function
value is the unified type of all its return expressions. However, the
parameter types must be explicitly declared. To do so there is both an
explicit notation and a convenience notation.

# Explicit notation for typing parameters.
#
# What happens here is that f is assigned
# the type (int) -> int, and that the right-
# hand side expression of the assignment
# computes the type (int) -> int, and then
# the assignment tries to reconcile the two.
# Since they are actually the same type,
# everything works out.
f :: (int) -> int
f = (x) :: (int) -> x
# The convenience notation only works when
# the right hand side of the variable
# being assigned to is a *syntactic* function.
# It will then automatically propagate the
# parameter type declarations to the right-
# hand side of the assignment.
g :: (int) -> int
g = (x) -> x
# Higher-order is okay.
add :: (int) -> (int) -> int
add = (x) -> (y) -> x + y

Objects and Memory

heap.coffee allows you to use manual memory management in addition to using
the managed runtime of JavaScript objects. The reason for this is simple:
performance. Even though JavaScript's managed runtime is getting faster
everyday, garbage collection still takes a toll on performance, especially
pauses.

Moreover, even something like a linked list cannot be declared in
JavaScript without the extra information of JavaScript objects. heap.coffee
allows you to metaphorically drop below that object model and back into
something C-like, but now instead of receiving SIGSEGV and crashing, at worst
the compiled JavaScript will just be buggy.