DEV Community

Taqmuraz
Taqmuraz

Posted on

Jena - pure functional Lisp descendant

Yes, Jena -- one more programming language. I am the author.

Why do I develop a new programming language?

For my personal use, at least. I have a lot of ideas that most of modern programming languages don't meet. I am tired of compromises.

What is a purpose of this language?

I would like to write short and declarative code. With no syntax garbage such as classes or interfaces.
Next, I need as independent language as possible. But, it must communicate with at least one existing language, to ensure it will not die right after birth. I chose Java. Simple, portable, fast enough -- all I need.

Common idea

Lisp influenced me a lot. Maybe, it is my most loved language. After Jena, of course.
Lisp has a simple syntax, based on lists. Commonly list is interpreted as a function call.
(a b c)in Lisp is the same, as a(b, c) in C.
In Jena it would be like as a b c. Yes, that simple. No annoying parentheses everywhere, same flexibility.

Everything is a function

In Jena everything is a function. Any function always accepts only one parameter and always returns another function. Jena has no types (or classes), objects or methods. You may think that such OOP things are necessary, but soon you will see -- they are not.

Everything is an expression

Another Jena primary concept -- everything is an expression. Jena does not require you to declare something before you use it. File with Jena program contains only one expression. It may be even string or number literal.

Literals

Every Jena expression is a literal or a list of literals.
For example, 1 is just a number literal, when print 1 is a list of literals : name literal and number literal.
All existing literals with examples :

  • number : 1, 2, 150, 1.5, 0.25
  • string : "hello, world", "Hello\nI am Peter"
  • operator : + - * / % == != > < & | !
  • name : peter, a, b, myValue, his_value
  • symbol : .name, .people, .count
  • parentheses : (1), (a + b), (a + (b.c))
  • array : [], [1, 5.2, "text"], [[1 + b], value]
  • entry : a:b, "code":23, (1 + 9):"ten"
  • map : {}, { 1:"first", 2:"second" }, { .age:25, .name:"Peter" }
  • none : ()
  • binding : name = "John" => println name, a = 10 => print(a == 10)
  • function : arg -> arg * 2, a -> b -> a + b, () -> 10.times

Code examples

I know, you can't wait to see code examples.
Let us look at the next simple C program :

#include "stdio.h"
int main()
{
    int a, b;
    scanf("%d", &a);
    scanf("%d", &b);
    printf("a + b = %d", a + b);
}
Enter fullscreen mode Exit fullscreen mode

It reads from the input stream two lines as decimal integer numbers and writes their sum to the output stream.
How will look Jena program that does the same?

print(readInt() + readInt())
Enter fullscreen mode Exit fullscreen mode

Well, too simple. Maybe, we want to calculate sum of squares?
C:

#include "stdio.h"
int main()
{
    int a, b;
    scanf("%d", &a);
    scanf("%d", &b);
    printf("a + b = %d", a * a + b * b);
}
Enter fullscreen mode Exit fullscreen mode

Jena:

a = readInt() =>
b = readInt() =>
print(a * a + (b * b))
Enter fullscreen mode Exit fullscreen mode

In Jena operators are just functions, and they don't define dedicated execution order. It means, that expression 1 + 2 * 3 will have 9 as a result, but never 7, because interpreter will see ((((1 +) 2) *) 3).
What is 1 + then?

True functions

Everything is a function. Numbers as well.
Expression a b is a function call expression, where a is a function and bis an argument. So, in expression 1 + function is 1 and argument is +. What the result of this expression? Another function, right. To complete addition, we have to call that another function, giving second argument.
Let us write a function, that will behave as a 2D vector.
vector.jena

x -> vec = self => y ->
{
    .x:x,
    .y:y,
    +:v -> vec (x + (v.x)) (y + (v.y)),
    .printTo:p -> ["x = ", x, " y = ", y].each p,
}
Enter fullscreen mode Exit fullscreen mode

And now we are going to use that function :
main.jena

vector = source "vector.jena" =>
a = vector 1 5 =>
b = vector (-10) 11 =>
(a + b).printTo print
Enter fullscreen mode Exit fullscreen mode

As you see, self is a special name. It is a reference to the function, expressed by the outer function literal.

That's all for today.
Thanks if you read that to the end.

Please, ask questions or share critique in comments.

Top comments (9)

Collapse
 
evgenijshelukhin profile image
evgenijShelukhin

I did not get the syntax of vector.jena. could you elaborate that please.

Collapse
 
taqmuraz profile image
Taqmuraz

Yes, of course :)
Let us begin from the top :
x -> vec = self => ...
Here we are building function, that has one parameter x. Inside this function we store reference to itself, binding it to vec name. How binding does work?
name = a => b
Here a will be evaluated first. After that interpreter will evaluate b inside name context, that has a value associated with name.

y -> ...
Enter fullscreen mode Exit fullscreen mode

To complete 2D vector creation, we build second function with y parameter. Jena does function currying by default : x -> y -> ... instead of (x, y) -> ....

{
    .x:x,
    .y:y,
    +:v -> vec (x + (v.x)) (y + (v.y)),
    .printTo:p -> ["x = ", x, " y = ", y].each p,
}
Enter fullscreen mode Exit fullscreen mode

Here we build hashmap, where .x associated with x, .y with y, + with addition function and .printTo with display function. In Jena map is a function, that takes key as a parameter and returns associated value.
Example :

map = {
    .symbol:"value, associated with .symbol",
    1:"value, associated with 1",
} =>
[
    println(map .symbol),
    println(map 1),
]
Enter fullscreen mode Exit fullscreen mode

This code will write on screen two lines :

value, associated with .symbol
value, associated with 1
Enter fullscreen mode Exit fullscreen mode

Remember, that map.symbol is not a member access expression from OOP languages such as C# or Java. It is a function call, where map is a function and .symbol is an argument.
I hope, this made picture clear for you :)
Do you have any other questions?

Collapse
 
evgenijshelukhin profile image
evgenijShelukhin

cool, now it almost clear.

so in terms of c#
.x:x is something like a field declaration

+:v -> vec (x + (v.x)) (y + (v.y)) is operator declaration
.printTo:p -> ["x = ", x, " y = ", y].each p is method declaration

why you bind self to vec, can you use self as is?

Thread Thread
 
taqmuraz profile image
Taqmuraz • Edited

Yes, you may use OOP approaches and consider hashmap as an object, where symbols(member names) are keys and members are values. But Jena itself does not operate with such concepts as classes, objects, methods or fields. Expression a:b creates an entry function, where a is a key and b is a value.

Why do I bind self to vec? Because inside v -> ... expression self will refer to v -> ... function, but we need reference to x -> ... function.

To make things more clear :
.x and .y are independent full-complete expressions.
v.x would have same result as sym = .x => v sym, or [v.x, v.y] would have same result as [.x, .y] .map v.

Thanks for question :)
Do you have other questions?

Thread Thread
 
evgenijshelukhin profile image
evgenijShelukhin

Looks great, thank you!

Collapse
 
fst2000 profile image
Sergey

Its so cool!

Collapse
 
taqmuraz profile image
Taqmuraz

Thank you :)

Collapse
 
rkgekk profile image
RKGekk

the fact that there is no mathematical priority for the execution of operators, it doesn’t look nice. code is cluttered with a huge number of parentheses

Collapse
 
taqmuraz profile image
Taqmuraz

Fair point. But, I had some reasons to choose this way of handling operators :

  • homogeneity - everything is a function, including operators, and rules must be same.
  • explicitness - in Jena such expression as a + b would be a function call. It is better to have explicit execution order.
  • simplicity and speed - it would take some time to analyze and sort every function call list that contain operators, and, sometimes, it would be impossible. Why? Because, a + b may be expressed as op = + => a op b or even (op -> a op b) +, and it still must work.
  • Lisp influence - as a Lisp descendant, Jena also prefers flexibility and simplicity over anything else. In Lisp such operators as + - * / are just functions too.