2011 August 06
Niue is a programming language without syntax. It is easy to learn and makes an ideal extension language. Despite its simplicity, Niue can do anything a conventional programming language can do.
Programs written in Niue consist of literals and words. Anything that falls between brackets are treated as comments and are ignored by the interpreter. That is all there is. You have learned Niue. A little more explanation with examples might still be useful.
Following are examples of literals:
( numbers )
56
3.14159
( strings )
"hello, world"
'joe
A string without quotes will be treated as a word which maps to a value.
This value could be a literal, another word or even a block of code. Such a code block
can consume values that precede it and use them to compute new values. For example, we can
input two numbers followed by the + word to find their sum:
3 4 +
The dot (.) word is used to print the top value
on Niue's data stack:
.
=> 7
The following sections compare a few basic syntactic forms in the C language with their equivalent "syntax-less" expressions in Niue.
Following are some variable definitions in C:
char *name = "nemo";
int age = 2;
The same definitions in Niue will be:
'nemo 'name ;
2 'age ;
The define (;) word, creates a new word and binds it to a value.
Let us inspect the words that we just defined:
name .
=> nemo
age .
=> 2
A word can be re-bound to a new value. The following code increments
the value of age and assigns the new value back to age
itself:
age 1 + 'age ;
age .
=> 3
A C function to increment age:
int age;
void increment_age ()
{
age = age + 1;
}
age = 2;
increment_age ();
increment_age ();
printf ("%d\n", age); // => 4
The Niue equivalent of this function will be the following word definition:
[ age 1 + 'age ; ] 'increment-age ;
The opening square bracket ([) is a word that asks Niue not to execute the
instructions that follow. Instead, they should be compiled and saved. Niue stays
in this "compilation" mode until a corresponding ] word is encountered.
This sequence of compiled instructions makes up a code block. A code block
is also a literal like a number or a string. So it can be assigned to a word. When Niue
encounters a word that is bound to a code block, it is replaced by the value obtained
by executing that code block:
2 'age ;
increment-age
increment-age
age .
=> 4
Let us re-write the C function so that it do not modify a global variable. Instead, it increments its argument - in place:
void increment (int* p)
{
*p = *p + 1;
}
int age = 3;
increment (&age);
printf ("%d\n", age); // => 4
It is evident from the function signature that it will work properly for only one
numeric type - int. Even the range of integer values it can deal with
depends on the host platform:
double tax_rate = 5.7829;
increment (&tax_rate); // tax_rate => ?
int worlds_population = 6953738051;
increment (&worlds_population); // worlds_population => ?
The Niue version of increment might look a bit complicated.
But it is more versatile:
[ dup eval 1 + swap ; ] 'increment ;
3 'age ;
'age increment
age .
=> 4
Let us figure out what the increment word is doing.
Fist it duplicates the string literal:
'age 'age
eval interprets a string literal as Niue code. As age denotes
a word bound to an integer, it is replaced by its value:
'age 3
The next instruction increments this value by 1:
'age 4
swap transforms the above sequence to:
4 'age
As the last step, ; will rebind age to 4.
The increment word do not discriminate between various numeric types:
5.7829 'tax_rate ;
'tax_rate increment
tax_rate .
=> 6.7829
6953738051 'worlds-population ; ( http://www.census.gov/main/www/popclock.html,
10:31 UTC (EST+5) Aug 06, 2011 )
'worlds-population increment
worlds-population .
=> 6953738052
The increment function that we wrote in C is still not really a
"function" because it modifies its argument. Here is a more functional
increment :
int increment (int a)
{
return a + 1;
}
int x = 100;
printf ("%d\n", increment (x)); // => 101
printf ("%d\n", x); // => 100
The same functional increment in Niue is just too simple:
[ 1 + ] 'increment ;
100 'x ;
x increment .
=> 101
x .
=> 100
What if we need a function to increment and return two numbers instead of one? In C we have to define a data structure to hold those values:
struct point
{
int x;
int y;
};
struct point increment_point (struct point p)
{
struct point new_p = p;
++new_p.x;
++new_p.y;
return new_p;
}
struct point p;
p.x = 10;
p.y = 20;
p = increment_point (p);
printf ("%d:%d\n", p.x, p.y); // => 11:21
A Niue word that does the same job is just a one-liner:
[ 1 + swap 1 + ] 'increment-point ;
10 20 increment-point . .
=> 11 21
No special data structure needs to be defined as no restriction is imposed on the number of arguments and return values. This peculiar nature of Niue makes it easy to compose functions together. To understand what that means, let us contrast a few more C functions with their equivalent Niue words:
/* Doubles a value. */
int dbl (int x)
{
return 2 * x;
}
/* Uses `dbl` to double a point. */
struct point dbl_point (struct point p)
{
struct point new_p;
new_p.x = dbl (p.x);
new_p.y = dbl (p.y);
return new_p;
}
struct point p;
p.x = 10;
p.y = 20;
/* First increment and then double a point. */
p = dbl_point (increment_point (p));
printf ("%d:%d\n", p.x, p.y); // => 22:42
Here the result of a function call becomes the argument of another. In Niue, the above sequence of function calls could be written in a more natural order:
[ 2 * ] 'double ;
[ double swap double swap ] 'double-twice ;
10 20 increment-point double-twice
. .
=> 22 42
The above code demonstrates a common style of programming in Niue: We first input some data and compose together a sequence of words to transform that data into the desired result.
Note: It is possible to create complex data structures in Niue. In fact, a user-defined object in Niue is more powerful than an instance of a C structure because it can keep track of its own private state and respond to messages. It can even live in its own 'tiny' parallel process.
Sometimes we like to identify arguments by name. The following
word that calculates the factorial of a number demonstrates how to do this.
It also introduces the if and else words that are
used to conditionally execute code blocks.
[ 'n ;
n 1 = [ 1 ] if
[ n n 1 - factorial * ] else
] 'factorial ;
4 factorial .
=> 24
10 factorial .
=> 3628800
Next is an iterative version of factorial. It makes use
of the times word that executes a code block a specific number
of times. For each execution the code block is passed the current value of
the counter:
[ 'n ;
L1 'r ; ( The L before 1 marks it as a "Large Integer". )
[ 1 + r * 'r ; ] n times r ] 'factorial ;
10 factorial .
=> 3628800
100 factorial .
=> 93326215443944152681699238856266700490715968264381621468592963895217599993229915608
941463976156518286253697920827223758251185210916864000000000000000000000000
What if you want to pass values to a code block without caring about their order? You can define code blocks that emulate keyword arguments. Here is a word that finds the force of gravity between two objects, given their masses and the distance between the masses in meters:
( The Gravitational Constant. )
10 -11 ^ 6.673 * 'G ;
( Uses the formula: F = Gm1m2/d^2 )
[ 'm1 get-r 'm1 ;
'm2 get-r 'm2 ;
'd get-r 'd ;
m1 m2 * d 2 ^ / G * ] 'F ;
( Values to be used by the code block can be specified in any order,
just preceed each value by string to identify it. )
'm1 34.5 'm2 43.6 'd 100 F .
=> 1.0037526600000002E-11
'm2 43.6 'd 100 'm1 34.5 F .
=> 1.0037526600000002E-11