The core is based on the difference between equality (=) and affectation (:=). Equality is used in the true sense, meaning that the variable will always be equal to the expression of the right hand side.

In the code:

area = length*width

The area will always be the length times the width, whatever the latter ones carry as value. It is declarative and immutable.

On the opposite, affectation means that you put the value of the right hand side expression at this current

moment of execution in the variable. It is what we are used to in most mainstream imperative languages like c++ and java.

So, let us start with a few examples:

—=== Equality vs Affectation ===—

x = 1

x = 2 // ERROR: x is already equal to 1

y := 1

y := 2 // OK, the value of y is now 2

Once equality has been set, it is immutable. We declare that x is equal to 1. It is a declarative statement and x cannot be equal to two different things. We also say x is bound to 1. On the opposite, y is just a “classic” variable that can hold any value assigned to it.

—=== Equality ===—

declare b

a = b

print a // prints undefined

b := 1

print a // prints 1

b := 2

print a // prints 2

Here, you can see that a is equal to the expression b. When you want to print a, it tries to evaluate the value of a. Since a is equal to b and the value of the latter is 2, it prints 2.

—=== Affectation ===—

b := 1

a := b

print a // prints 1

b := 2

print a // prints 1

When we write a := b, what we do is evaluating the value of b and put the result into a. Thus, at this point of execution, the value of a will be 1. When we change b afterwards, a is not affected since it is associated with the value 1 and not with b. Before going further, it is important to understand clearly the subtle but obvious difference between affectation and equality.

By equality, we bind the variable to the right hand side expression itself. On the opposite, by affectation, we evaluate the right hand side and put it into the variable.

—=== Having fun with both ===—

declare b,c

a = 100 + b // OK, b has been declared previously

b = 10 * c // OK, c has been declared previously

// now, we have a == 100 + 10*c

print a // prints undefined

c := 3

print a // prints 130

c := 7

print a // prints 170

c := a // c := 170

print a // prints 1800

This shows how we can have constructs which are both declarative and highly expressive. There is no need to propagate the value of affected variables nor to updates things upon a change. We simply “know” what a is equal to and that it depends on c.

—=== Unethical mathes ===—

declare b,c

a = b

b = 2*c

c = a // ERROR: cyclic dependency

Luckily, this can be checked at compile time.

———————————————————————————————————–

The other main part of the language are functions. As in functional programming, functions are treaded as first class citizens. Variables are linked (equal or affected) to either values, expressions or functions. Function can be passed as parameters to other functions, functions can be returned as result and so on.

There are three important fact about these functions:

-functions are deterministic/stateless

-functions cannot produce side effects

-functions can be nested

Declaring a function looks like this:

function <name>: (<input>) -> (<output>)

<body>

end function

The “end function” is optional, i will omit it when the body is just a single for readability.

So, for example, we could have:

function f : (x,y) -> (z)

z= x^2 + y^2

There is another way to declare exactly the same. By equaling f to the equivalent anonymous function. This looks like this:

f = function : (x,y) -> (z)

z = x^2 + y^2

Both are exactly the same. In both examples, the identifier f is bound to the function defined above. In fact, the first example can be seen as sintactic sugar for the second one. We will use the second notation mostly in the examples below.

—=== Functions are stateless ===—

a = 2

b := 3

c = b

f = function: (x) -> (y)

y = a*x // OK, a is stateless

g = function: (x) -> (y)

y = b*x // ERROR, b is stateful

h = function: (x) -> (y)

y = c*x // ERROR, c is (indirectly) stateful

—=== Functions are stateless, bis ===—

f = function: (x) -> (y)

//…

g := function: (x) -> (y)

//…

hf = function: (x,y) -> (z)

z = f(x) + f(y) // OK, the function is still stateless

hg = function: (x,y) -> (z)

z = g(x) + g(y) // ERROR, because g is stateful

hh = function: (x,y) -> (z)

if x == 1 or y == 1 then

z = 1

else

z = hh(x-1,y-1) + x*y // OK, stateless recursive calls

—=== Functions without side-effects ===—

declare a,b

f = function: (x,y) -> (z)

a = 1 // ERROR, functions cannot modify variables outside their scope

b := 2 // ERROR, functions cannot modify variables outside their scope

a = 1

b := 2

g = function: (x,y) -> (z)

declare a,b // redeclaring variables with local scope

a = 3 // OK, local variables

b := 4 // OK, local variables

print a // prints 1

print b // prints 2

—=== Function calls ===—

f = function: (x,y) -> (z)

z = x^2 + y^2

print f(1,2) // prints 5

declare a,b

c = f(a,b)

print c // prints undefined

a = 2

b := 3

print c // prints 13

f = function: (x,y) -> (z)

z = x^2 + y^2

g = function: (x,y) -> (z)

z = x^2 – y^2

declare h as function

ans = h(1,2)

print ans // prints undefined

h := function f

print ans // prints 5

h := function g

print ans // prints -3

h := function: (x,y) -> (z)

z = f(x,y) + g(x,y)

print ans // prints 2

print h(10,20) //prints 200

—=== Nested functions ===—

binomial = function: (i,n) -> (ans)

fact = function: (n) -> (ans)

if (n > 1)

ans = n * fact(n-1)

else

ans = 1

ans = fact(n)/(fact(i)*fact(n-i))

—=== Returning functions ===—

norm = function: (i) -> (function ans)

ans = function: (x,y) -> (dist)

dist = (x^i + y^i)^(1/i)

taxicabLength = function norm(1)

euclideanLength = function norm(2)

norm5 = function norm(5)

dist := taxicabLength(3,4)

print dist // prints 7

dist := euclideanLength(3,4)

print dist // prints 5

dist := norm5(3,4)

print dist // prints 4.174…

That was for the nice part. Now comes the trouble… đŸ˜‰

…are function always stateless if they satisfy the above examples?

consider the following case:

f = function: (c,d) -> (function g)

g = function: (x) -> (y)

y = c*x + d // Authorized or not? …can lead to state indirectly encapsuled in the function

declare a,b

h = function f(a,b) //What happens? …OK if a is stateless, ERROR if a is stateful?

// h is a function depending indirectly on a and b

a = 2 // still OK

b := 5 // ERROR, introduces state in h

…this last example is causing me trouble because I would like to

ensure that functions are always stateless, but the fact that

functions can be returned and that the input arguments are bound by

equality cause issues:

-either indirect state can ******* into functions

-either it becomes both hard to ensure that functions are always

stateles and can be unconvinient for the user since “forgotten”

variables cannot be assigned if part of a function

…i’m curious to hear any feedback.

cheers

June 20, 2008 at 6:19 pm |

I’m not sure if you’ve realized this, but you have reinvented dataflow programming.

Definitely a good thing to have in a language, but you can generalize your equations to provide omnidirectional dataflow. For example:

always (X = Y + Z);

to update Z do

(X := 3);

(Y := 4);

end

to update X do (Y := 5);

In this code, you specify which variable the data flows into, from other variable assignments.

TTFN