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);
}
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())
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);
}
Jena:
a = readInt() =>
b = readInt() =>
print(a * a + (b * b))
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 b
is 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,
}
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
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)
I did not get the syntax of vector.jena. could you elaborate that please.
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 tovec
name. How binding does work?name = a => b
Here
a
will be evaluated first. After that interpreter will evaluateb
inside name context, that hasa
value associated withname
.To complete 2D vector creation, we build second function with
y
parameter. Jena does function currying by default :x -> y -> ...
instead of(x, y) -> ...
.Here we build hashmap, where
.x
associated withx
,.y
withy
,+
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 :
This code will write on screen two lines :
Remember, that
map.symbol
is not a member access expression from OOP languages such as C# or Java. It is a function call, wheremap
is a function and.symbol
is an argument.I hope, this made picture clear for you :)
Do you have any other questions?
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 declarationwhy you bind self to vec, can you use self as is?
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, wherea
is a key andb
is a value.Why do I bind
self
tovec
? Because insidev -> ...
expressionself
will refer tov -> ...
function, but we need reference tox -> ...
function.To make things more clear :
.x
and.y
are independent full-complete expressions.v.x
would have same result assym = .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?
Looks great, thank you!
Its so cool!
Thank you :)
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
Fair point. But, I had some reasons to choose this way of handling operators :
a + b
would be a function call. It is better to have explicit execution order.a + b
may be expressed asop = + => a op b
or even(op -> a op b) +
, and it still must work.+ - * /
are just functions too.