publish unlimited private modules for just $7/month

Kal is a highly readable, easy-to-use language that compiles to JavaScript. It's designed to be asynchronous and can run both on node.js and in the browser. Kal makes asynchronous programming easy and clean by allowing functions to pause and wait for I/O, replacing an awkward callback syntax with a clean, simple syntax.

This is the preferred method for installing Kal. Make sure you have installed node.js. Kal works with versions 0.6, 0.8, and 0.10. It might work with other versions as well. Install the latest "stable" release of Kal using npm:

If you need the latest and greatest (possibly unstable/broken) build, you can build Kal manually. Most users can skip this section and just use the latest npm version.

Kal is written in Kal, so you need a prebuilt version of the compiler available to do the initial build:

sudo npm install -g kal

Then you can clone the repo, install the developer dependencies, and build the compiler:

git clone https://github.com/rzimmerman/kal kal

cd kal

npm install

npm run-script make

Run the tests to make sure everything is going well:

npm test

If you're extra serious, you can use your new build to rebuild itself in case there were any notable changes to the compiler between the npm release and the latest commit. This will also run the tests.

npm run-script bootstrap

Now install your latest version using npm:

npm pack

Assuming the tests pass, this will make an archive file that you can install (the filename depends on the version):

sudo npm install -g kal-0.x.x.tgz

Alternatively you can just run the scripts/kal file if you don't want to install it globally.

If you installed Kal globally (using the -g option), you can run the interactive shell by running kal with no arguments.

$ kal

kal> 'hello' + ' ' + 'world'

'hello world'

You can use the kal utility to run or compile files. Run kal -h for the full option set. If you installed kal locally (didn't use the -g option), you will need to specify the path to the kal executable, usually located at node_modules/kal/scripts/kal.

Literate Kal offer an exciting way to write well-documented, readable code. The idea is based on Literate CoffeeScript. The compiler will treat any file with a .litkal or .md extension as a Markdown document. Code blocks, denoted by four spaces of indentation, are treated as Kal code while anything that is not indented is treated as a comment. See this example of a Literate Kal file. New in 0.5.2.

In Kal, spaces for indentation are significant and tabs are not valid. Indents are required for function definitions and blocks of code inside of if statements, try/catch blocks, and loops.

You should use two spaces to denote an indent. You can technically use any multiple of two spaces, but two is recommended as a style guideline. Any whitespace on blank lines is ignored. Semicolons at the end of statements are not required nor are they valid.

In general single statements cannot contain line breaks. Notable exceptions are list and object definitions. For example:

a = [1, 2,

3,

4]

b = {a:1

c:2}

Will work, however:

a = 1 +

1

Is invalid. Future versions may include better support for line breaks within statements.

Both define a variable my_function that takes two arguments and returns their sum. CoffeeScript syntax is also valid:

my_function = (arg1, arg2) ->

return arg1 + arg2

But is generally discouraged unless it significantly helps readability. It was originally included to ease porting of the Kal compiler from CoffeeScript to Kal. Coffee-style functions must contain a line break after the ->. => is not supported.

Functions can have default arguments. These will be used if the specified argument is null or undefined:

function default_args(x,y=2)

return x + y

print default_args(1) # prints 3

Functions are called using parentheses.

my_function(1, 2)

Will return 3. Parentheses are optional if the function has at least one argument:

my_function 1, 2

Is also valid. Function calls can be chained this way as well, so any of the following

print(my_function(1,2))

print my_function 1, 2

print my_function(1, 2)

will all print 3. When calling a function with no arguments, parentheses are required.

Tasks are similar to functions, except that they are intended to be called asynchronously (usually using a wait for statement).

task my_task(arg)

return arg * 2

or

my_task = task (arg)

return arg * 2

Tasks should not be called synchronously. If a task is called synchronously, it will return with no value. When called asynchronously.

print my_task 1

Is valid syntax, but will print undefined.

wait for x from my_task(1)

print x

Will print 2 as expected. See the wait for section for more details on asynchronous calls.

Objects and arrays are defined similarly to JavaScript. Newlines are valid inside of an array or object definition and indentation is ignored. Commas are optional when followed by a newline. CoffeeScript-style object definitions (no {}s) are only valid in assignments and must be preceded by a newline.

a = [1, 2, 3]

b = [1

2,

3

4]

c = {a:1,b:2,c:{d:3}}

d =

a:1, b:2

c:

d:3

Function definitions are only valid in CoffeeScript-style object definitions at this time.

d =

a:1, b:2

c:

d: function ()

return 2

e: ->

return 3

Objects work like JavaScript objects (because they are JavaScript objects), so you can access members either using array subscripts or . notation

will print 5 if the variable name is equal to 'Joe', otherwise it will print 6. Kind of like it says. Parentheses are required because tail conditionals associate right, meaning the following are equivalent:

Will print the numbers 1, 2, and 3. The value n the right of the for ... in expression is called the iterant. Currently it must be an array. Python-like iterable object support is coming soon.

for ... in loops can also have an index variable:

for x at index in [10,20,30]

print index, x

Will print the 0 10, 1 20, 2 30.

for loops can also be used on objects:

obj = {a:1,b:2}

for key of obj

print key, obj[key]

Will print a 1 and b 2.

When used on asynchronous code, the parallel and series specifiers are available:

for parallel x in y

wait for z from f(x)

for series x in y

wait for z from f(x)

series is the default if neither is specified. Parallel for loops are not guaranteed to execute in order! In fact, they often won't. Take special care when accessing variables separated by wait for asynchronous statements. Remember that a wait for releases control of execution, so other loop iterations running in parallel may alter local variables if you are not careful. See the wait for section for more details.

You can use a generator object as follows:

for val from generator_object

print val

A generator object is any object that has a next() method. The loop will keep calling the next() method until it does not return a value (returns undefined in JavaScript-speak).

You can also use a range of numbers as an implicit generator to save memory:

for val from 1 to 100000000 # will not instantiate a giant list

print val

while loops continuously run their code block until a condition is satisfied.

List comprehensions are a quick and useful way to create an array from another array:

y = [1,2,3]

x = [value * 2 for value in y]

will set x equal to [2,4,6]. Comprehensions also support an iterable object. Iterable objects support a next() method which returns the next value in the sequence each time it is called. When there are no more values in the sequence, it should return null.

Classes are defined with member method definitions. Methods are just functions that are added to the prototype of new instance objects (in other words, they are available to all instances of a class). The initialize method, if present, is used as the constructor when the new keyword is used. me (or its synonym this) is used in methods to access the current instance of the class. instanceof checks if an object is an instance of a class.

class Person

method initialize(name)

me.name = name

method printName()

print me.name

method nameLength()

return me.name.length

jen = new Person('Jen')

jen.printName() # prints 'Jen'

print(jen instanceof Person) # prints true

Classes can inherit from other classes and override or add to their method definitions. The super keyword can be used in a method to call the same function in the parent class.

class FrumpyPerson inherits from Person

method printName()

print 'Frumpy ' + me.name

method nameLength()

return 0

sue = new FrumpyPerson('Sue')

sue.printName() # prints 'Frumpy Sue'

print(sue instanceof Person) # prints true

print(sue instanceof FrumpyPerson) # prints true

print(jen instanceof FrumpyPerson) # prints false

You can add or alter a method or task to a class after it is defined (or from another file) using late binding using the of keyword.

try and catch blocks work similarly to JavaScript/CoffeeScript. finally blocks are not supported yet but are coming eventually. The throw statement (and its synonyms raise and fail with) work like JavaScript as well.

try

a = 'horse' / 2 # what are we doing? this will throw an error!

b = 1 # never runs

catch e

print 'caught it!', e

The e variable above stores the error object thrown with throw or by the system. You can give it any name you want and it is optional. The following is valid:

try

throw 'a string'

b = 1 # never runs

catch

print 'caught it!'

try/catch blocks can be nested. try blocks can contain asynchronous wait for calls, but catch blocks cannot at this time.

Double-quoted strings can contain interpolated values using #{...} blocks. These blocks can contain any valid Kal expression (including variables and function calls). This is the recommended way to do string concatenation as it is usually more readable.

The wait for statement executes a task and pauses execution (yielding to the runtime) until the task is complete. The following reads a file asynchronously and prints its contents (in node.js).

fs = require 'fs'

wait for data from fs.readFile '/home/user/file.txt'

print data.toString()

Note that:

For users familiar with node.js and JavaScript, fs.readFile is called with the file name argument and a callback. You don't need to supply a callback.

After the wait for line, execution is paused and other code can run. Keep this in mind if you have global variables that are modified asynchronously as they may change between the wait for line and the line after it.

Any errors reported by fs.readFile (returned via callback) will be thrown automatically. You should wrap the wait for in a try/catch if you want to catch these errors.

wait for can be used to call your own asynchronous tasks. It can also be used within for and while loops, try blocks, if statements, and any nesting combination you can think of. Really!

fs = require 'fs'

task readFileSafe(filename)

if 'secret' in filename

throw 'Illegal Access!'

else

wait for d from fs.readFile filename

return d

for parallel filename in ['secret/data.txt', 'test.txt', 'test2.txt']

try

wait for data from readFileSafe '/home/secret/file.txt'

print data.toString()

catch error

print "ERROR: #{error}"

print 'DONE!'

wait for can also be used without arguments by omitting the from keyword

wait for my_task()

Some node.js API functions (like http.get) don't follow the normal convention of calling back with an error argument. For these functions you must use the safe prefix, otherwise it will throw an error:

Code after the run in parallel block will not run until all tasks have completed. If any errors are thrown by one or more tasks, an array of errors will be thrown after all tasks in the block complete (or fail). Array elements are in the order that the tasks were specified. If no error was thrown by a task, its error element will be undefined (doesnt exist will be true). safe waits will not check for errors.