An expression oriented programming language / data notation, designed for elegant data processing and transference.
All terms in lala
will eventually converge to an expression, so it's fair to start from. Here is a list of all forms of expressions. The list will be revisited as new terms are introduced.
List of Expression
- Type System
- Literal
- Type Constructor
- Modular System
- Obstruction
- Projection
- Exposure
- Value Space
- Literal
- Application
- Binder Space
- Abstraction
- Pattern Language
- Control Flow
- Pattern Matching
- Pattern Enumeration
where
- Literals are just the application of the type
primitive
defined by a type constructor incore
. - An obstruction is a data block with certain computational order, a projection accesses the result of a previous block, and an exposure opens up a binded data block.
- Abstraction can be analogized by variables and functions, and works through bindings.
Let's bootstrap with some quick examples.
Literals contain int, float and string.
42; /* int */
-1; /* int */
0.0; /* float */
0.; /* float */
""; /* string */
"Ehaema"; /* string */
where /* ... */
represents comment.
A more sophisticated literal will come later.
Use binders to introduce new variables and functions.
ans := 42;
x := ans;
x' := ans - 1; /* only suffix `'`s are allowed */
add x y := x + y;
Operators are special binders defined in core
.
// Todo..
Use type by prefixing '
.
truth' := bool'true; /* type'data */
truth := 'true; /* shortened, equal effect */
// Todo..
Finally, perhaps key to maintainable data, let's introduce comment and document syntax.
/* Comment */
/* => tag
any markdown content
*/
within the markdown area, you may write anything that helps, from a speech to a note, which will be collected together with the binder directly below and renderred to a static doc page. The tags can be used to cluster the pages in a package; leave it blank if not useful.
If the doc is file-wise, or no binder is available, use !!
.
/* => tag !!
any markdown content
*/
Perhaps the most fundamental question in the short history of computer science is the question of how to collect, process and present data in a safe and flexible way. lala
is yet another attempt of an elegant solution for data abstraction.
// Note: !! Add Generalized Tuple ()
as true parallel; Tuple = Array made better?; {}
for dependent, simultaneous computation.
It all started with the sequence of evaluation. We want to collect the result of our computation in three ways: either sequential, simultaneous, or parallel. If the latter computation depends on the result of the previous one, it's sequential; if the order of computation satisfies a resolvable dependency graph, it's simultaneous; if we insist that a set of computations must all complete regardless of the order of completion within the set, it's parallel.
If you admit that all data processing is eventually a sequence of evaluation, it would natural to represent them with data blocks. To differentiate the three sequences of evaluation, []
, {}
and ()
are introduced as primitives.
Nullary.
[];
{};
();
// Todo..
Since each expression is a computation, it's not abrupt to propagate the use of ;
.
// Todo: List and Map
list := [0,1,2,3];
map := {
0: 0,
1: 1,
2: 2,
3: 3,
};
// Todo: Dynamically typed list and map
// Todo: List is more of a vector, for easier usage (?: doubly-linked list)
We use different separators for the two spaces. ;
acts upon the binder space, standing for a pulse of computation. an operation that don't show effect by returning value, and a reusable piece of code; while ,
acts upon the value space, standing for conjunction and product of values, and construction or deconstruction of things.
// Todo..
// Todo..
<sin; sin'> := trigonometric = [
math := [<lala>].math;
<math> := [<lala>];
<math> := lala;
sin := math.sin;
sin' := math.cos;
];
Note that three math
's are the same.
<sin; cos> := trigonometric = [
<lala.math>
];
// Note: see Exposure.
Principles:
- If a block can be seen, it can be projected.
- If a binder binds to a block, it is the block.
- If a binder is masked by
=
, it can't be seen outside the containing block. - If a binder is masked by
:=
, it can be seen outside the containing block.
Thus =
binders can't be projected to outside the containing block, while :=
binders can.
Note that binders appear in the form of patterns.
// Todo..
data := ![
alice := "kawaii";
ehaema := "elegant";
carol := [ alice; ehaema ];
];
is effectively
data := {
"alice" : "kawaii",
"ehaema" : "elegant",
"carol" : [
"kawaii",
"elegant"
],
};
<a; b; c> := data;
/* equals to */
_ := <a; b; c> = data;
// Todo..
A binder can be binded to any expression.
f := g := [];
add x y := x + y; /* function definition `add` */
res := add 1 2; /* 3 */
As for currying,
add_1 := add 1;
partially apply a function will produce any other function with fewer arguments to be fed. Under the hood, the binders (for functions) only takes one actual argument.
Suppose we want to pass a data block to a function and make use of the bindings immediately, considering it's similar to exposure, we could write
f <x;y;z> := x + y + z;
res = f [x := 3; y := 4; z := 5];
Note that it's unreasonable to write
/* error */
f <x;y;z> := <res> = [
res := x + y + z;
];
because res
would be ambiguous.
Abstraction is so common and signaficant in programming languages, but it's unbelievably hard to do it right.
// Todo: example of function argument evaluation order.
However, in lala you can explictly denote the evaluation order by passing arguments in different ways.
f x y z := () /* z -> y -> x -> (f x y z ) */
f [x,y,z] := () /* x -> y -> z -> (f [x,y,z]) */
f (x,y,z) := () /* (x,y,z), parallel -> (f (x,y,z)) */
f <x;y;z> := () /* <x;y;z>, preserved -> (f <x;y;z>) */
As we've in fact incountered many pattern usage, I think it's a good time now to formally introduce pattern language in lala:
/* deconstruct a list or array */
[a,b,c]
[a]+[b]+[c]
ab+[c] /* favored for performance (?: dl-list) */
_ +[c]
[a]+bc
[a,_,_]
[a, ..]
/* deconstruct a tuple */
a,b,c
(a,b,c)
(a,_,_)
(a, ..)
/* deconstruct a hashmap */
{0: a, 1: b, 2: c}
{"king": a, "queen": b, "eeloo": c}
{0: a, ..}
/* deconstruct a block */
<a;b;c>
<..>
Notice that there's little ;
in pattern language because ,
looks better (kidding). In fact, the design principle traces back to the difference between binder space and value space. Almost all patterns are dealing with values, so ,
appears everywhere, except <a; b; c>
as it deals with binder space elimination.
A few other comments:
ab+[c]
should be favored over[a]+bc
, unlike most fp language's behavior. This is because lala prefers vector impl over linked lists. e.g., in the use case ofjson
a vector is better most of the time. Say a history of operations are stored. Usually we append, not prepend the latest events.
bool := '{
true,
false,
};
truth := bool'true;
falsehood := 'false;
To bind data and use polymorphism,
tree 'a := '{
leaf '[
:data 'a,
],
node '[
:data 'a,
:lt tree 'a,
:rt tree 'a,
],
};
But you may choose not to force the type, leaving it ducked:
tree := '{
leaf '[
:data,
],
node '[
:data,
:lt,
:rt,
],
};
To recursively define types,
http_response_status 'data := '{
informational '{
continue,
switching,
/* ... */
},
successful '{
ok '[
:data, /* equal to `:data 'data` */
],
created,
accepted,
/* ... */
},
/* ... */
};
// Todo: All data structures can be typed.
// Todo: Binders defined with type (member function / variable).
// Todo...
// Todo..
It's called dada
.
Defines how "global" data blocks within lala's domain maps to reality, i.e. file system or repositories.
Though with a special purpose, dada
is just lala syntax, with no magic at all.