📜 ⬆️ ⬇️

Not a regular programming language



Recently, a huge number of new programming languages ​​have appeared on the market: Go, Swift, Rust, Dart, Julia, Kotlin, Hack, Bosque - and this is only among those that are heard.
The value of what these languages ​​bring to the world of programming is hard to overestimate, but, as Y Combinator rightly pointed out last year, speaking of development tools:
The frameworks are getting better, the languages ​​are a little smarter, but basically we do the same.
This article will talk about a language built on an approach that is fundamentally different from the approaches used in all existing languages, including the ones listed above. By and large, this language can be considered a general-purpose language, although some of its capabilities and the current implementation of the platform built on it, nevertheless, probably limit its use to a slightly narrower area - the development of information systems.

Immediately, I’ll make a reservation, it’s not about the idea, prototype, or even MVP, but about a fully-fledged production-ready language with all the necessary infrastructure infrastructure - from the development environment (with debugger) to automatic support of several versions of the language (with automatic merge bugfixes between them , release-note, etc.). In addition, several dozens of ERP-level projects have already been implemented using this language, with hundreds of simultaneous users, terabyte databases, “need-yesterday” deadlines, limited budgets and developers with no IT experience. And all this at the same time. And, of course, it should be noted that now is not the year 2000, and all these projects were implemented on top of existing systems (which wasn’t there), which means that you first had to gradually, without stopping the business, do it “as it was”, and then also gradually, to do "as it should be." In general, this is how to sell the first electric cars not to rich hipsters in California, but to low-cost taxi services somewhere in Omsk.

A platform built in this language is released under the LGPL v3 license. Honestly, I didn’t want to write it directly in the introduction, as this is not the most important advantage of it, but after talking with people working on one of its main potential markets - ERP platforms, I noticed one feature: all these people, without exception, say that even if you do the same thing that is already on the market, but for free, it will be very cool. So leave it here.
')

A bit of theory


Let's start with the theory to identify the difference in the fundamental approaches used in this and other modern languages.

A small disclaimer, further reasoning to some extent is an attempt to pull an owl on the globe, but in principle we don’t really use the fundamental theory in programming, so we have to use what we have.

One of the first and most important problems solved by programming is the task of calculating the values ​​of functions. From the point of view of the theory of computation, there are two fundamentally different approaches to solving this problem.

The first such approach is the various machines (the most famous of which is the Turing machine) - a model that consists of the current state (memory) and the machine (processor), which at each step in one way or another changes this current state. This approach is also called the Von Neumann architecture, and it is the basis of all modern computers and 99 percent of existing languages.

The second approach is based on the use of operators, it is used by so-called partially recursive functions (hereinafter referred to as the RFF). In this case, the most important difference between this approach is not in the use of operators as such (operators, for example, exist in structured programming using the first approach), but in the possibility of iteration over all function values ​​(see the argument minimization operator) and in the absence of a state in calculation process.

Like the Turing machine, partially recursive functions are Turing-complete, that is, they can be used to specify any possible calculation. Here we will immediately clarify that both the Turing machine and the FFR are only minimal bases, and then we will speak about them as approaches, that is, a model with a memory-processor and a model with operators without using variables and the ability to iterate over all values functions accordingly.

The CRF as an approach has three main advantages:


Turing machines have at least two advantages:


Plus, in this comparison we are talking only about the tasks of calculating data, in the problems of changing data without Turing machines it is still necessary.

Having read to this point, any attentive reader will ask a reasonable question: “If the PRF approach is so good, why is it not used in any common modern language?”. So, in fact, it is not, it is used, and in the language that is used in the vast majority of existing information systems. As you can guess, this language is SQL. Here, of course, the same attentive reader will reasonably argue that SQL is the language of relational algebra (that is, working with tables, not functions), and it will be right. Formally. In fact, we can recall that the tables in the DBMS are usually in the third normal form, that is, they have key columns, which means that any remaining column of this table can be viewed as a function of its key columns. Not obvious, frankly. And why SQL never evolved from the language of relational algebra into a full-fledged programming language (that is, working with functions) is a big question. In my opinion, there are many reasons for this, the most important of which is “Russian (in fact, anyone) cannot work on an empty stomach, but does not want to be fed,” in the sense that, as practice shows, the work necessary for this truly titanic, and carries too great risks for small companies, and for large companies, firstly, everything is good, and secondly, this work cannot be forced with money — here it is quality that matters more than quantity. Actually, the most vivid illustration of what happens when a problem is tried to be solved by quantity, not quality , is Oracle, which even the most basic use of incrementality — updatable materialized views — managed to be implemented in such a way that this mechanism has a number of restrictions the size of several pages ( for the sake of Microsoft is still worse ). However, this is a separate story, perhaps, there will be a separate article about it.

At the same time, it’s not that SQL is bad. Not. At its level of abstraction, it performs its functions perfectly, and the current implementation of the platform uses it a little less than completely (in any case, much more than all other platforms). Another thing is that right after its birth, SQL actually stopped its development and did not become what it could be, namely, the language that will be discussed now.

But enough of the theory, it's time to go directly to the language.

So, we meet :


Specifically, this article will be the first part of the three (as there is still too much material even for two articles), and it will only talk about the logical model - that is, only what is directly related to the functionality of the system and is not related to the processes development and execution (performance optimization). Moreover, we will discuss only one of the two parts of the logical model - the domain logic. This logic determines what information the system stores and what can be done with this information (it is also often called business logic in developing business applications).

Graphically, all concepts of domain logic in lsFusion can be represented by the following image:


The arrows in this picture indicate the directions of using each other's concepts, thus, concepts form a kind of stack, and, accordingly, it is in the order of this stack that I will talk about them.

Properties


A property is an abstraction that takes as input one or more objects as parameters and returns some object as a result. The property has no aftereffect, and, in fact, is a pure function, however, unlike the latter, it can not only calculate values, but also store them. Actually, the name “property” itself is borrowed from other modern programming languages, where it is used for approximately the same purposes, but nails are encapsulated and, accordingly, are supported only for functions with one parameter. Well, in favor of the use of this particular term, the fact that this very word “property” is shorter than “pure function” has played, plus it has no unnecessary associations.

Properties are set recursively using a predefined set of operators. There are quite a few of these operators, so we will consider only the main ones (these operators cover 95% of any average project).

Primary Property (DATA)


The primary property is a property whose value is stored in the database and may change as a result of the execution of the corresponding action (about it a little later). By default, the value of each such property for any set of parameters is equal to the special value NULL.
quantity = DATA INTEGER (Item);
isDayOff = DATA BOOLEAN (Country, DATE );
When using the operator of the primary property, it is necessary to specify which classes the created property accepts as input (about the classes themselves also a little later), and which value class this property can return.

In fact, this operator summarizes the fields and collections in modern languages. So:

class X { 	
    Y y; 	
    Map<Y, Z> f; 	
    Map<Y, Map<M, Z>> m; 	
    List<Y> l;
    static Set<Y> s;
}

:
y = DATA Y (X);
f = DATA Z (X, Y);
m = DATA Z (X, Y, M);
l = DATA INTEGER (X,Y);
s = DATA BOOLEAN (Y);

(JOIN), , (+,-,/,*), (AND, OR), (+, CONCAT), (>,<,=), (CASE, IF), (IS)

f(a) = IF g(h(a)) > 5 AND a IS X THEN ‘AB’ + ‘CD’ ELSE x(5);
- , . , , , :


(GROUP)


– . (, ) .

:


, ( ), – ( ). , , , ( , , SQL – ). , ( BY), - :
// BY     ,   s
sum(DATE from, Stock s, DATE to) = GROUP sum(Invoice i) IF date(i) >= from AND date(i) <=to BY stock(i); 
, , , , , .

:


/ (PARTITION … ORDER)


( , ) , . , ( , ). / .
place(Team t) = PARTITION SUM 1 ORDER DESC points(t) BY conference(t);
, , , , , , .

SQL ( , ) (OVER PARTITION BY… ORDER BY).

(RECURSION)


– , . , , .

. , ( ):


, , , , :
//   integer  from  to (       System)
iterate(i, from, to) = RECURSION i=from STEP i=$i+1 AND i<=to CYCLES IMPOSSIBLE;
 
//      a  b   ( ,  ,  )
edge = DATA BOOLEAN (Node, Node);
pathes '- ' (a, b) = RECURSION 1 IF b=a STEP 1 IF edge(b, $b);
 
// ,     child  parent,  null,     (         child')
parent  = DATA Group (Group);
level '' (Group child, Group parent) = RECURSION 1 AND child IS Group AND parent = child STEP 1 IF parent = parent($parent);
 
//  ,        to, (   NULL)
fib(i, to) = RECURSION 1 IF (i=0 OR i=1STEP 1 IF (i=$i+1 OR i=$i+2AND i<to CYCLES IMPOSSIBLE;
, / , , , .

, , , , , lsFusion – .

SQL CTE, , , . , Postgres GROUP BY , , , , , , . , , WHILE’ .

. , , .


– , , , , ( , , ). , , “”, , -, , , -, , “”.

, – lsFusion. , – . , – ( , , , ). , – . .

, . / , , , , , . 3 97, lsFusion – 60 40.

, , . , -, ( , ), .

, :

(FOR), (WHILE)


, lsFusion , , NULL ( ).
FOR selected(Team team) DO
    MESSAGE 'Team ' + name(team) + ' was selected';
, :
showAllDetails(Invoice i) {
    FOR invoice(InvoiceDetail id) = i ORDER index(id) DO
        MESSAGE 'Sku : ' + nameSku(id) + ', ' + quantity(id);
}
, , (IF).

(WHILE) , :


(EXEC), ({…}), (CASE, IF), (BREAK), (RETURN)

f(a) {
    FOR t=x(b,a) DO {
        do(b);
        IF t>5 THEN
            BREAK;
    }
    MESSAGE 'Succeeded';
}
- . , , .

(CHANGE)


. , , , , NULL. :
//       
setDiscount(Customer c)  {
    discount(c, Item i) <- 15 WHERE selected(i);
}
, :
setDiscount(Customer c)  {
    FOR selected(Item i) DO
        discount(c, i) <- 15;
}
, , , ( , , , , selected discount – ), . , , .

(NEW)


( , , , , ). , , .

:
newSku ()  {
    LOCAL addedSkus = Sku (INTEGER);
    NEW Sku WHERE iterate(i, 13TO addedSkus(i);
    FOR Sku s = addedSkus(i) DO {
        id(s) <- 425;
        name(s) <- 'New Sku : ' + i;
    }
}
, , — NEW (FOR), ( ):
FOR iterate(i, 13NEW s=Sku DO  {
    id(s) <- i;
    name(s) <- 'New Sku : ' + i;
}
, FOR :
NEW s=Sku DO {
    id(s) <- 425;
    name(s) <- 'New Sku';
}
– , , , .

(DELETE)


– :
DELETE Sku s WHERE name(s) = 'MySku';
, «» .

, , .


, , . , , . .

, . , — / .

, , . , ( ), .

, , . , :
LOCAL f = INTEGER (INTEGERINTEGER);

f(1,3) <- 6;
f(2,2) <- 4;
f(f(1,3),4) <- 5;
f(a,a) <- NULL//      1-  2-  (  2,2)

MESSAGE GROUP CONCAT a + ',' + b + '->' + f(a, b),' ; '//  1,3->6 ; 6,4->5
: (APPLY) (CANCEL). , , , . , , , . – , , , , . — .

(NEWSESSION, NESTEDSESSION)


(, , http- ..). , , . , , , «», «» ( , ). NEWSESSION, ( ). :
run() {
    f(1) <- 2;
    APPLY;
    f(1) <- 1;
    NEWSESSION {
        MESSAGE f(1); //  2,       
        f(2) <- 5;
        APPLY;          
    }
    MESSAGE f(1); //  1,     1  ,   
}
, , , . , :
run(Store s) {
    NEWSESSION
        MESSAGE 'I see that param, its name is: ' + name(s);
}
, , ( ). NESTED, , , , . , ( , NESTED). :
g = DATA LOCAL NESTED INTEGER ();
run() {
    f(1) <- 1; g() <- 5;
    NEWSESSION NESTED (f) {
        MESSAGE f(1) + ' ' + g(); //  1 5
        f(1) <- 5; g() <- 7;
    }
    MESSAGE f(1) + ' ' + g(); //  5 7
}
. :


, (, ), , . , , - , , , , , :


(APPLY), (CANCEL)


– , . , . , :


// -------------------------- Object locks ---------------------------- //
 
locked = DATA User (Object);
lockResult = DATA LOCAL NESTED BOOLEAN ();
 
lock(Object object)  {
    NEWSESSION { 
        lockResult() < - NULL;
        APPLY SERIALIZABLE {
            IF locked(object) THEN {
                CANCEL;
            } ELSE {
                locked(object) <- currentUser();
                lockResult() <- TRUE;
            }
        }
    }
}
 
unlock(Object object)  {
    NEWSESSION
        APPLY locked(object) <- NULL;
}
PS: Authentication, , , ( ) ( ). , , lsFusion , (, ).

– , , , , ( ).

(PREV, CHANGED, SET, DROPPED)


: (PREV), (CHANGED), NULL NULL (SET) .. ( ), , , :
f = DATA INTEGER (INTEGER);
run() {
    f(1) <- 2;
    APPLY;
 
    f(1) <- 5;
    f(2) <- 3;
    MESSAGE GROUP SUM 1 IF CHANGED(f(a)); // ,    f     ,  2
    MESSAGE '. : ' + f(1) + ', . : ' + PREV(f(1)); //  . : 5, . : 2
}
. , , , , .


“ ?”, “ ?”. , , .

, , . , . .

:


, , :


, , , .

:


:


:


:


( , ), .
ON { //   ,       APPLY
    MESSAGE 'Something changed';
}
, , , , ‘Something changed’ (!) ( , ). , , - , (CHANGED, SET, DROPPED ..). , - , -, -. – :
//  email,          
WHEN SET(balance(Sku s, Stock st) < 0DO
      EMAIL SUBJECT '     ' + name(s) + '   ' + name(st);

WHEN LOCAL CHANGED(customer(Order o)) AND name(customer(o)) == 'Best customer' DO
    discount(OrderDetail d) <- 50 WHERE order(d) = o;

, – , . , :
ON {
    FOR SET(balance(Sku s, Stock st) < 0DO
        EMAIL SUBJECT '     ' + name(s) + '   ' + name(st);
}
, , / , , .

SQL ( ) . , , , ( ) , .

, :



, , . , , NULL:
//    0
CONSTRAINT balance(Sku s, Stock st) < 0 
    MESSAGE '    ';

// «»  
CONSTRAINT DROPCHANGED(barcode(Sku s)) AND name(currentUser()) != 'admin' 
    MESSAGE ' -       ';

//      ,   
CONSTRAINT sku(OrderDetail d) AND NOT in(sku(d), customer(order(d)))
    MESSAGE '        ';
, – , NULL (SET) , – NULL . – , , , / , .

, – ( ), , , ( ), , .


. , , , – . , :
f = DATA A (INTEGER);
, f NULL , A. :
f = Object (INTEGER);
CONSTRAINT f(i) AND NOT f(i) IS A MESSAGE ' '// f(i) => f(i) IS A
, , – . ( , , «»), , , , : - , .

, lsFusion . , lsFusion . , . lsFusion , , - :
CLASS A {
    f = DATA LONG (INTEGER); //  f = DATA LONG (A, INTEGER)
}
lsFusion , :
CLASS Animal;
CLASS Transport;
CLASS Car : Transport;
CLASS Horse : Transport, Animal;
, – .


lsFusion – . , , :
speed = ABSTRACT LONG (Transport);
/ , :
CLASS Breed;
speed = DATA LONG (Breed)
breed = DATA Breed (Animal);

speed(Horse h) += speed(breed(h)); //       
( ):
CLASS Thing;
CLASS Ship : Thing;
CLASS Asteroid : Thing;

collide ABSTRACT (Thing, Thing);
collide(Ship s1, Ship s2) +{
    MESSAGE 'Ship : ' + name(s1) + ', Ship : ' + name(s2);
}
collide(Ship s1, Asteroid a2) +{
    MESSAGE 'Ship : ' + name(s1) + ', Asteroid : ' + name(a2);
}
collide(Asteroid a1, Ship s2) +{
    MESSAGE 'Asteroid : ' + name(a1) + ', Ship : ' + name(s2);
}
collide(Asteroid a1, Asteroid a2) +{
    MESSAGE 'Asteroid : ' + name(a1) + ', Asteroid : ' + name(a2);
}
, , ( ), . ABSTRACT :
speed(Transport t) = CASE 
    WHEN t IS Horse THEN speed(breed(t))
    //  
END
, .

, , :
speed(Horse h) = speed(breed(h));
, , ( ). , , .


, , . () : , , . , , , , , . NULL , , :
f = DATA LONG (LONG);
g = DATA LONG (A);
h(a) = OVERRIDE f(a), g(a); //   


( ) – , . , , , :
CLASS Direction '' {
    left '',
    right '',
    forward ''
}

result(dir) = CASE
    WHEN dir = Direction.left THEN ' '
    WHEN dir = Direction.right THEN ' '
    WHEN dir = Direction.forward THEN ' '
END
, .

enum’, , .


( lsFusion, ) :


.

() , NULL . , , , , , .

:
//   A    B
b(A a) = AGGR B WHERE a IS A; 
//     a     B    A,   b(a(b)) = b

createC = DATA BOOLEAN (A, B)
//    A  B    createC    C
//     ,       ,      
c = AGGR C WHERE createC(A a, B b); 
//     a  b     C      B 
, , ( ):
CLASS Shipment '';
date = ABSTRACT DATE (Shipment);
CLASS Invoice '';
createShipment ' ' = DATA BOOLEAN (Invoice);
date ' ' = DATA DATE (Invoice);
CLASS ShipmentInvoice '  ' : Shipment;
//    ,       
shipment(Invoice invoice) = AGGR ShipmentInvoce WHERE createShipment(invoice);
date(ShipmentInvoice si) += sum(date(invoice(si)),1); //   =   + 1
, (, ), ( ) , . , ERP 1100 . , , , . , ( 50 ), , 1100 300 .

, , , , . , , – , – , , , « - », , , .


, lsFusion . , , , , , , , , , ( , , ). , lsFusion ( ACID), , SQL-, . , DSL lsFusion , , – – . , SQL , , lsFusion . , , – , github ( , ), (IDE, , VCS, ), slack telegram- , (linux maven, - ), , , , , lsFusion , SQL, ABAP 1 – .

, , lsFusion, , ( ), : ERP-, SQL- , ORM-, RAD-, . , , .

, :


, lsFusion ERP, RAD SQL , lsFusion ( ). , SQL, , , , , , Fortran C ( , ). ORM- , – . , , - .

SME ( , ), ( ). , 1 , .

, , , , , ( 12 ), .

Source: https://habr.com/ru/post/458376/


All Articles