Не очередной язык программирования. Часть 1: Логика предметной области

  • Tutorial


В последнее время на рынке появилось огромное количество новых языков программирования: Go, Swift, Rust, Dart, Julia, Kotlin, Hack, Bosque – и это только из числа тех, которые на слуху.
Ценность того, что эти языки привносят в мир программирования, тяжело переоценить, но, как правильно в прошлом году отмечал Y Combinator, говоря про инструменты разработки:
Фреймворки становятся лучше, языки немного умнее, но в основном мы делаем то же самое.
В данной статье будет рассказано о языке, построенном на подходе, принципиально отличающемся от подходов, используемых во всех существующих языках, в том числе вышеперечисленных. По большому счету, этот язык можно считать языком общего назначения, хотя некоторые его возможности и текущая реализация платформы, построенной на нем, все же, наверное, ограничивают его применение немного более узкой областью – разработкой информационных систем.

Сразу оговорюсь, речь пойдет не об идее, прототипе, и даже не о MVP, а о полноценном production-ready языке со всей необходимой языку инфраструктурой – от среды разработки (с отладчиком) до автоматической поддержки нескольких версий языка (с автоматическими merge багфиксов между ними, release-note и т.п.). Кроме того, с использованием этого языка уже реализовано несколько десятков проектов сложности уровня ERP, с сотнями одновременных пользователей, терабайтными базами, сроками «нужно вчера», ограниченными бюджетами и разработчиками без опыта в IT. Причем все это одновременно. Ну и, конечно, следует учесть, что сейчас не 2000 год, и все эти проекты реализовывались поверх существующих систем (чего там только не было), а значит, сначала нужно было постепенно, без остановки бизнеса сделать «как было», а потом, также постепенно, сделать «как должно быть». В общем, это как продавать первые электромобили не богатым хипстерам в Калифорнии, а лоукост-службам такси где-нибудь в Омске.

Платформа, построенная на этом языке, выпускается под лицензией LGPL v3. Честно, не хотел это писать прямо во вступлении, так как это далеко не самое главное ее преимущество, но, пообщавшись с людьми, работающими на одном из ее основных потенциальных рынков – ERP платформ, заметил одну особенность: все эти люди без исключения говорят, что даже если вы сделаете то же самое, что уже есть на рынке, но бесплатно, то это уже будет очень круто. Так что оставлю это тут.

Немного теории


Начнем с теории, чтобы обозначить разницу в фундаментальных подходах, используемых в этом и других современных языках.

Небольшой дисклеймер, дальнейшие рассуждения в какой-то степени являются попыткой натянуть сову на глобус, но с фундаментальной теорией в программировании в принципе, скажем прямо, не очень, поэтому приходится использовать то, что есть.

Одной из самых первых и главных задач, решаемых программированием, является задача вычисления значений функций. С точки зрения теории вычислений, в решении этой задачи существует два принципиально разных подхода.

Первым таким подходом являются различные машины (самой известной из которых является машина Тьюринга) – модель, которая состоит из текущего состояния (памяти) и машины (процессора), которая на каждом шаге тем или иным образом изменяет это текущее состояние. Этот подход также принято называть архитектурой Фон Неймана, и именно он лежит в основе всех современных компьютеров и 99 процентов существующих языков.

Второй подход основан на использовании операторов, его используют так называемые частично-рекурсивные функции (далее ЧРФ). При этом самое главное отличие этого подхода не в использовании операторов как таковых (операторы, к примеру, есть и в структурном программировании, использующим первый подход), а в возможности итерирования по всем значениям функции (см. оператор минимизации аргумента) и в отсутствии состояния в процессе вычисления.

Как и машина Тьюринга, частично-рекурсивные функции полны по Тьюрингу, то есть с их помощью можно задать любое возможное вычисление. Здесь сразу уточним, что и машина Тьюринга, и ЧРФ – это только минимальные базисы, а дальше речь пойдет о них именно как о подходах, то есть о модели с памятью-процессором и о модели с операторами без использования переменных и возможностью итерирования по всем значениям функций соответственно.

У ЧРФ как подхода есть три основных преимущества:

  • Он гораздо лучше оптимизируется. Это касается как непосредственно оптимизации самого процесса вычисления значения, так и возможности параллелизма такого вычисления. В первом же подходе эффект последействия, наоборот, вносит очень большую сложность в эти процессы.
  • Он гораздо лучше инкрементируется, то есть для построенной функции можно гораздо эффективнее определить, как будут изменяться ее значения при изменении значений функций, которые эта построенная функция использует. Строго говоря, это преимущество является частным случаем первого, но именно оно дает огромное количество возможностей, которых принципиально не может быть в первом подходе, поэтому выделено отдельным пунктом.
  • Он значительно проще для понимания. То есть, грубо говоря, описание функции подсчета суммы одного показателя в разрезе двух других показателей гораздо проще для понимания, чем если то же самое описать в терминах первого подхода. Впрочем, в алгоритмически сложных задачах ситуация диаметрально противоположная, но тут стоит отметить, что алгоритмически сложных задач в абсолютном большинстве областей хорошо если 5%. Вообще, если немного обобщить, то ЧРФ – это математика, а машины Тьюринга – это информатика. Соответственно, математику изучают чуть ли не в детском саду, а информатику факультативно и со старших классов. Так себе сравнение, конечно, но все же какую-то метрику в данном вопросе дает.

У машин Тьюринга есть как минимум два преимущества:

  • Уже упомянутая лучшая применимость в алгоритмически сложных задачах
  • Все современные компьютеры построены на этом подходе.

Плюс, в этом сравнении речь идет только о задачах вычисления данных, в задачах изменения данных без машин Тьюринга все равно не обойтись.

Дочитав до этого места, любой внимательный читатель задаст резонный вопрос: “Если ЧРФ подход так хорош, почему он не используется ни в одном распространенном современном языке?”. Так вот, на самом деле, это не так, он используется, причем в языке, который применяется в подавляющем большинстве существующих информационных систем. Как легко догадаться, этим языком является SQL. Тут, конечно, тот же внимательный читатель резонно возразит, что SQL – это язык реляционной алгебры (то есть работы с таблицами, а не функциями), и будет прав. Формально. Фактически же можно вспомнить, что таблицы в СУБД обычно находятся в третьей нормальной форме, то есть имеют колонки-ключи, а значит, любую оставшуюся колонку этой таблицы можно рассматривать как функцию от ее колонок-ключей. Не очевидно, прямо скажем. И то, почему SQL так и не перерос из языка реляционной алгебры в полноценный язык программирования (то есть работы с функциями) – большой вопрос. На мой взгляд, причин тому много, самая главная из которых – «русский (на самом деле любой) человек на голодный желудок работать не может, а на сытый не хочет», в том смысле, что, как показывает практика, необходимая для этого работа поистине титаническая и несет слишком большие риски для небольших компаний, а у крупных компаний – во-первых, и так все хорошо, а во-вторых, эту работу невозможно форсировать деньгами – здесь важнее качество, а не количество. Собственно, самой наглядной иллюстрацией того, что бывает, когда проблему пытаются решать количеством, а не качеством, является Oracle, который даже самое базовое применение инкрементальности – обновляемые материализованные представления – ухитрился реализовать так, что у этого механизма количество ограничений размером с несколько страниц (справедливости ради, у Microsoft все еще хуже). Впрочем, это уже отдельная история, возможно, про нее будет отдельная статья.

В то же время речь не идет о том, что SQL плох. Нет. На своем уровне абстрагирования он отлично выполняет свои функции, и текущая реализация платформы использует его чуть менее, чем полностью (во всяком случае, значительно больше, чем все остальные платформы). Другое дело, что сразу после своего рождения SQL фактически остановился в развитии и так и не стал тем, кем мог стать, а именно языком, о котором сейчас пойдет речь.

Но хватит теории, пора переходить непосредственно к языку.

Итак, встречаем:


Конкретно эта статья будет первой частью из трех (так как материала все же слишком много даже для двух статей), и в ней будет рассказано только про логическую модель – то есть только про то, что связано непосредственно с функционалом системы и никак не связано с процессами разработки и выполнения (оптимизации производительности). Более того, речь пойдет только об одной из двух частей логической модели – логике предметной области. Эта логика определяет, какую информацию хранит система, и что с этой информацией можно делать (при разработке бизнес-приложений ее также часто называют бизнес-логикой).

Графически все понятия логики предметной области в lsFusion можно представить следующей картинкой:


Стрелки на этой картинке обозначают направления использования понятиями друг друга, таким образом, понятия образуют своего рода стек, и, соответственно, именно в порядке этого стека я и буду про них рассказывать.


Свойства


Свойство – это абстракция, которая принимает на вход один или несколько объектов в качестве параметров и возвращает некоторый объект в качестве результата. Свойство не имеет последействия, и, по сути, является чистой функцией, однако, в отличие от последней, может не только вычислять значения, но и хранить их. Собственно, само название “свойство” позаимствовано из других современных языков программирования, где оно используется приблизительно для тех же целей, но при этом гвоздями прибито к инкапсуляции и, соответственно, поддерживается только для функций с одним параметром. Ну и в пользу использования именно этого термина сыграло то, что это само слово “свойство” короче, чем «чистая функция», плюс не имеет ненужных ассоциаций.

Свойства задаются рекурсивно при помощи предопределенного набора операторов. Этих операторов достаточно много, поэтому рассмотрим только основные из них (эти операторы покрывают 95% любого среднестатического проекта).

Первичное свойство (DATA)


Первичное свойство – это свойство, значение которого хранится в базе данных и может изменяться в результате выполнения соответствующего действия (о нем чуть позже). По умолчанию значение каждого такого свойства для любого набора параметров равно специальному значению NULL.
quantity = DATA INTEGER (Item);
isDayOff = DATA BOOLEAN (Country, DATE);
При использовании оператора первичного свойства необходимо задать, какие классы создаваемое свойство принимает на вход (о самих классах также чуть позже), и какой класс значения это свойство может возвращать.

Фактически этот оператор обобщает поля и коллекции в современных языках. Так:

class X { 	
    Y y; 	
    Map<Y, Z> f; 	
    Map<Y, Map<M, Z>> m; 	
    List<Y> n;
    LinkedHashSet<Y> l; // упорядоченное множество 
    static Set<Y> s;
}

Эквивалентно:
y = DATA Y (X);
f = DATA Z (X, Y);
m = DATA Z (X, Y, M);
n = DATA Y (X,INTEGER);
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);
Тут все более-менее стандартно, поэтому останавливаться на этих операторах подробно особенного смысла нет. Единственное, что, наверное, все же стоит отметить:

  • В логических операторах и операторах выбора в качестве условий можно использовать не только свойства со значениями логических типов, а вообще любые свойства. Соответственно, условием в этом случае будет определенность значения свойства (то есть отличие от NULL). Собственно, сам логический тип в lsFusion – это, по сути, константа, то есть его множество значений состоит из ровно одного элемента – значения TRUE (роль FALSE выполняет значение NULL), никаких крышесносящих 3-state’ов.
  • Для арифметических и строковых операторов есть специальные формы работы с NULL: (+), (-), CONCAT с сепаратором. При использовании этих форм:
    • в арифметических операторах: NULL на входе интерпретируется как 0, а на выходе – наоборот, 0 заменяется на NULL (то есть 5 (+) NULL = 5, 5 (-) 5 = NULL, но 5 + NULL = NULL и 5 — 5 = 0).
    • в строковых операторах: NULL на входе игнорируется и соответственно сепаратор не добавляется (то есть CONCAT ‘ ‘, ‘John’,’Smith’ = ‘John Smith’, а CONCAT ‘ ‘, ‘John’, NULL = ‘John’, но ‘John’ + ‘ ‘ + NULL = NULL).
  • Для оператора простого выбора (IF) существует (и очень часто используется) постфиксная форма: f(a) IF g(a), которая возвращает f(a) если g(a) не NULL, и NULL – в обратном случае.

Группировка (GROUP)


Группировка – самый часто используемый оператор работы со множествами. Этот оператор берет свойство и для всех его значений вычисляет некоторую агрегирующую функцию (например, сумму) в разрезе значений других свойств.

С точки зрения синтаксиса есть две формы этого оператора:

  • Функциональный:
    sum(Invoice i) = GROUP SUM sum(InvoiceDetail id) IF invoice(id) = i;
    currentBalance(Sku sk) = GROUP SUM currentBalance(sk, Stock st);
    Эта форма допускает замыкания на лексический контекст, то есть внутри оператора можно использовать параметры внешнего контекста (в примерах выше параметры i и sk). Особенность функциональной формы в том, что ее можно использовать в выражениях, то есть писать что-то вроде:
    x() = (GROUP SUM f(a)) + 5;
  • SQL-стиль:
    sum = GROUP SUM sum(InvoiceDetail id) BY invoice(id);
    currentBalance = GROUP SUM currentBalance(Sku sk, Stock st) BY sk;
    В отличие от функциональной эту форму оператора можно использовать только при объявлении свойств (как и, скажем, оператор создания первичного свойства)

С точки зрения лаконичности кода первую форму имеет смысл использовать, когда группировка идет по параметрам (пример с остатком), вторую – по свойствам (пример с инвойсом). Хотя, по большому счету, это все же дело вкуса, кому как привычнее (для людей больше работавших с функциональным программированием, скорее будет привычна первая форма, для работавших с 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)


Рекурсия – наверное, самый сложный для понимания оператор работы с множествами. Он нужен для реализации вычислений с неизвестным заранее количеством итераций, в частности, для работы с графами.

Для оператора рекурсии необходимо задать начальное свойство и свойство шага. Соответственно, алгоритм вычисления этого оператора состоит в следующем (далее почти дословная цитата из документации):

  • Сначала рекурсивно строится промежуточное свойство (result) с дополнительным первым параметром (номером операции) следующим образом:
    • result(0, o1, o2, ..., oN) = initial(o1, ..., oN), где initial – начальное свойство
    • result(i+1, o1, o2, ..., oN) = step(o1, ..., oN, $o1, $o2, ..., $oN) IF result(i, $o1, $o2, ..., $oN), где step – свойство шага.
  • Затем для всех значений полученного свойства вычисляется сумма в разрезе всех его параметров, за исключением номера операции (то есть o1, o2, …, oN). Теоретически вместо суммы может быть любая агрегирующая функция, но в текущей реализации поддерживается только сумма.

Не самое очевидное определение, скажем прямо, поэтому суть этого оператора, наверное, все же проще понять по примерам:
// итерация по 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) отличается от обычного цикла только тем, что:

  • продолжает выполнения до тех пор, пока для условия цикла есть хоть одно не NULL значение (в этом смысле он очень похож на оператор рекурсии в свойствах)
  • не обязан вводить новый параметр

Вызов (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)


Применение и отмена изменений – операции, для которых сессии собственно и создавались. В описании сессий они уже были упомянуты, и их семантика следует из их названия. Единственное, что стоит отметить:

  • При применении и отмене изменений все изменения локальных первичных свойств удаляются. Иногда такое поведение нежелательно, поэтому, как и для создания сессий, для этих операторов поддерживается опция NESTED (с аналогичным поведением).
  • При применении изменений есть возможность указать дополнительное действие, которое будет выполнено сразу после начала транзакции. Главное отличие выполнения этого дополнительного действия внутри транзакции от его выполнения сразу перед применением изменения заключается в том, что если применение по какой-либо причине будет отменено, то и изменения, сделанные в этом дополнительном действии, также будут отменены. Более того, если причиной отмены применения был конфликт записи (update conflict), а значит, применение будет автоматически выполнено еще раз, то в этом случае указанное дополнительное действие также будет выполнено еще раз. К примеру, такое поведение можно использовать для реализации долгосрочной пессимистичной блокировки:

// -------------------------- 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
}
На этом с операторами создания действий закончим. Не потому что они закончились, просто, как и со свойствами, остальные либо очень редко используются, либо тесно связаны с понятиями других уровней абстракции языка и будут рассмотрены там.

События


Действия отвечают на вопрос “Что делать?”, но не отвечают на вопрос “Когда это делать?”. Для определения моментов, когда нужно выполнять те или иные действия, в платформе существуют события.

Сразу оговорюсь, дальше речь пойдет о событиях предметной области, помимо них в логике представления также существуют события формы. Это два совершенно не связанных механизма, и на событиях формы мы подробнее остановимся в статье про логику представления. Но в дальнейшем события без уточнения их вида будем считать событиями предметной области.

События предметной области бывают двух типов:

  • Синхронные – происходят непосредственно после изменения данных.
  • Асинхронные – происходят в произвольные моменты времени по мере того, как сервер успевает выполнить все заданные обработки и / или по истечению некоторого периода времени.

В свою очередь, с точки зрения области видимости изменений, события можно разделить на:

  • Локальные – происходят локально для каждой сессии изменений.
  • Глобальные – происходят глобально для всей базы данных.

Таким образом, события могут быть синхронными локальными, синхронными глобальными, асинхронными локальными и асинхронными глобальными.

Преимущества синхронных событий:

  • При необходимости в обработках можно выполнять отмену изменений, если, например, эти изменения не удовлетворяют необходимым условиям.
  • Они гарантируют большую целостность, так как после окончания записи изменений пользователь гарантированно будет работать уже с обновленными данными.

Преимущества асинхронных событий:

  • Можно сразу отпустить пользователя, а обработки выполнять «на фоне». Это улучшает эргономику системы, правда, возможно, только когда обновление данных не критично для дальнейшей работы пользователя (для глобальных событий, например, в течение ближайших 5-10 минут, пока сервер не успеет выполнить очередной цикл обработок).
  • Обработки группируются для большого количества изменений, в том числе сделанных различными пользователями (в случае глобальных событий), и, соответственно, выполняются меньшее число раз, тем самым улучшая общую производительность системы.

Преимущества локальных событий:

  • Пользователь видит результаты обработок событий сразу, а не только после того, как он сохранил их в общую базу.

Преимущества глобальных событий:

  • Обеспечивают лучшую производительность и целостность, как за счет того, что обработки выполняются только после сохранения изменений в общую базу (то есть существенно реже), так и за счет использования многочисленных возможностей СУБД, связанных с работой с транзакциями.

Пока в платформе поддерживаются только синхронные глобальные и асинхронные локальные (как самые часто используемые, поддержка остальных видов событий также планируется в будущем), поэтому дальше будем говорить просто о глобальных и локальных событиях.
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-фреймворки, и просто электронные таблицы. Первый, четвертый и пятый типы платформ имеют пользовательский интерфейс в комплекте, во втором и третьем для этого используются сторонние технологии.

У каждого из этих пяти типов платформ есть своя ниша, где они в основном обитают:

  • SQL-сервера с процедурными расширениями – бизнес-приложения с относительно сложной логикой и большими объемами данных – это, как правило, ритейл и банки.
  • ERP-платформы – остальные бизнес-приложения со сложной логикой – оптовая торговля, производство, финансы и т.п.
  • ORM-фреймворки – веб-приложения (сервисы, порталы), ну и очень высоконагруженные приложения с относительно несложной логикой.
  • RAD – узкоспециализированные низконагруженные бизнес-приложения с простой логикой, там где, как правило, сильно ограничен IT-бюджет.
  • Электронные таблицы – используются там же, где и RAD, правда, из-за низкого порога вхождения их можно встретить везде, где только можно, начиная от крупных корпораций и заканчивая полной автоматизацией малого бизнеса чисто на Excel (да, такое тоже встречается, и даже не знаю, какие ощущения это больше вызывает – восторг или страх).

На мой сугубо субъективный взгляд, в глобальной перспективе lsFusion под силу полностью заменить ERP, RAD и SQL платформы, которые lsFusion превосходит по всем нефункциональным требованиям (а по многим из них превосходит на порядок). Правда, что касается SQL, тут речь скорее идет не о замене, а о надстройке, то есть так же, как, скажем, Fortran и C пришли на смену ассемблеру (на ассемблере по прежнему можно писать, но непонятно зачем). С ORM-фреймворками очевидно будет тяжело конкурировать в предельных гибкости и масштабируемости, а с электронными таблицами – с порогом вхождения в очень простых задачах и в работе с неструктурированными данными. Хотя все же, возможно, какую-то часть рынка удастся отвоевать и у них.

Ну и в среднесрочной перспективе фокус в основном будет сделан на SME (у которых ограничены человеческие ресурсы и бюджеты на ИТ, но при этом большие потребности в гибкости и эргономике используемых решений), а также на нестандартные задачи (где мало готовых решений и их кастомизация по объему превосходит сами эти решения). То есть занять ту нишу, которую сейчас занимает 1С в России, но только сделать это в мировом масштабе.

Это все, конечно, звучит чересчур амбициозно, но после того пути, который уже удалось пройти, чтобы просто заставить всю эту технологию работать (а на это ушло без малого 12 лет), такая задача уже не кажется настолько невыполнимой.

UPD: Вторую часть статьи можно найти тут.
lsFusion
45,16
Не очередной язык программирования
Поделиться публикацией

Комментарии 424

    +6
    Фреймворки становятся лучше, языки немного умнее, но в основном мы делаем то же самое.

    Не из того же самого уже достаточно давно есть Haskell
      –4
      Если цель просто не делать тоже самое, то есть brainfuck. Чего уж там :)
        +4
        В brainfuck-е ничего оригинального нет, он просто предельно примитивен. О нём бы давно забыли, если бы не матерное название. Настоящий «brain fuck» это именно Haskell.
          +2
          вобщета brainfuck — это машина тьюринга(с парой лишних операций)
            0
            Настоящие brainfuck — это APL/K/J и FP.
            Haskell вполне понятныя язык.
              0
              Сам то он понятный, мозг выносит от того что на нём пишут гранды. С продолжениями, расширениями языка и многоэтажными преобразователями типов. Например dsl-hdl-и на нём, ClaSH в частности. (Что характерно, Chisel почему-то пользуется большей популярностью).
                0
                Clash и Lava ориентированы на схемы с одним тактовым сигналом. Это делает их достаточно простыми, но не всегда применимыми. Модель Chisel ближе к обычным HDL, с процессами реагирующими на изменения сигналов.
                Мне кажется, человеку со стороны, не знакомому с HDL, проще будет освоить Clash, но для иинженеров ил микроэлектронной индустрии Chisel понятнее и мощнее.
                  0
                  Да бог с ними, с клоками. Я вот это хотел на Clash переложить, и сразу упёрся в запрет рекурсивных описаний, а это для моей древовидной структуры очень нужно. Даже на SystemVerilog-е как-то получилось, а тут нет!
          +3
          Функциональный стиль существует где-то с 1960-го года. Всё то же самое…
          +1

          Как тут с кейс-сенситивностью (она же чуыствительность к регистру)? А то в примерах ключевые слова полностью заглавными, остальное — не очень заглавными, как-то неоднородно выглядит

            0
            Язык полностью CASE-sensitive, в том числе ключевые слова. И они именно большими буквами, так как много ключевых слов, и если бы они были маленькими, было бы невозможно использовать в именах кучу распространенных слов (например, sum, group, in и т.п.) Ну и в блокноте проще читать такой текст.
              +4
              Ну то есть в результате половина кода будет набрана КАПСОМ. Это было понятно, когда сиквел только зарождался и не было подсветки кода, но зачем это делать сейчас?
                –1
                Ключевые слова в языке пишутся большими буквами, потому что их много, и потому что часть из этих ключевых слов — это вполне обычные английские слова. Если сделать их маленькими буквами, то мы не сможем именовать элементы (классы, свойства, формы, и т.д.) в системе этими словами.

                Другой вопрос, почему их так много. Начнем с инструкций языка, которые в основном являются объявлениями некоторых элементов системы, их относительно много, и эти объявления необходимо как-то синтаксически разделять. Разделены они по факту в основном ключевыми словами (CLASS, FORM, CONSTRAINT, etc), идущими в начале объявления. Кроме этого (и что более важно) свойства и действия у нас бывают разных типов и создаются с помощью операторов. Типов много, операторов соответственно тоже, их тоже нужно как-то синтаксически разделять. Можно разделять их, например, некими конструкциями из спецсимволов, как, например, разделяются между собой (и у нас естественно тоже) арифметические, логические, битовые операторы, а можно ключевыми словами. Мы выбрали второй вариант, который, как нам казалось, был более читаемым и понятным потенциальным разработчикам с не очень большим техническим бэкграундом (ну и проводил какие-то параллели с sql). Да, выглядит несколько олдово, но, как мне кажется, это дело привычки. Тем более с поддержкой IDE набирать этот код не так неудобно, как кажется на первый взгляд.
                  0
                  на всякий случай — я не минусую Ваши ответы. И в целом, как full time SQL разработчик, считаю инициативу очень полезной. К капсу я в основном «привязался» потому, что считаю, что SQL на самом деле лучший язык для работы с данными, но как все SQL-related языки программирования не развиваются с такой скоростью, как остальные ЯП. И неиспользование капса делает язык более читаемым -> более подходящим для, собственно, программирования.

                  В целом SQL как язык запросов довольно полный, но как язык программирования его можно было развить добавив возможностей, например, возможность работать с query как с first-class-citizen, то есть реально сделать тип данных query (что то наподобие C# iqueryable).
                  0
                  Но тогда и автодополнения не было. А сейчас можно писать код не нажимая ни shift ни caps lock, и при этом называть свойства sum. Меня если честно к примеру раздражает в java называть переменные aclass, благо в java ключевых слов — кот наплакал.

                  Но как я уже писал, видимо придется добавить режим с low-case ключевыми словами, раз это некоторых так раздражает.
                    0
                    Меня если честно к примеру раздражает в java называть переменные aclass

                    clazz же, ну!

                      +1
                      Точно :)

                      А interface как правильно — interfaze?
                      0
                      Меня если честно к примеру раздражает в java называть переменные aclass, благо в java ключевых слов — кот наплакал

                      Я, честно, не понимаю, зачем называть переменную class.

                      А сейчас можно писать код не нажимая ни shift ни caps lock, и при этом называть свойства sum

                      Так проблемы с написанием кода нет, проблема у меня лично возникает с чтением кода. Код пишется 1 раз а читается миллион раз. И лично мне понимать код, наполовину написанный капсом, сложнее, чем код, написанный лоу кейсом.
                      0
                      Дело вкуса, мне и сейчас в последней SQL Server Management Studio, несмотря на подсветку и автоподстановку, удобно в запросах ключевые слова писать капсом, перечитывать и разбирать проще. (но писать полноценные приложения в таком стиле не стал бы)
                    +1
                    Тут ключевые слова большими как SQL. А классы и свойства — как в Java классы и методы.
                      +1
                      в sql ключевые слова можно любым регистром писать.
                      заглавными — не более, чем рекомендация.
                        0
                        Да, но тут так пришлось сделать, чтобы упрости грамматику. Иначе синтаксис в других местах был бы более сложный. К тому же, часто бывает лучше рекомендацию сделать обязательной, чтобы код был однотипным.
                    0
                    Какие проекты сейчас пишут с использованием данного языка и кто?
                      +1
                      Тут есть часть решений, которые сейчас в продакшне.

                      Ключевой продукт на языке сейчас — это ERP (на нем сейчас работает около половины из ТОП-10 розничных сетей Беларуси, несколько заводов и оптовиков). Все компании из разных отраслей и размеров, благо модульность позволяет как из кубиков собирать решение и для маленького магазина секонд-хенд и для FMCG сети (в тексте статьи про это было)

                      Также есть ряд заказных/полу-заказных проектов, например, из области близкой к BPM — так, половина процессов одного очень известного фонда автоматизировано на этом языке, есть кейс с автоматизацией крупной юридической компании и бюджетированием нескольких мелких.

                      Плюс всякие внутренние проекты вроде CRM и т.п.

                      Для всех этих проектов в основном используем людей indoor, причем практически все без минимального / с минимальным IT бэкграундом (это дает реально большое количество плюсов). Но есть ряд проектов, где доработки идут сторонними разработчиками (как заказчика так и третьих лиц)

                      Правда тут надо понимать, что мы специально тормозили процесс использования языка, чтобы сохранить максимальный контроль за кодовой базой. Это позволяло изменять язык не оглядываясь на обратную совместимость, даже несмотря что первые проекты начали внедряться 8 лет назад. Сейчас конечно, так уже не получится, но мы довели его как раз до того состояния, где функционал в основном будет добавляться, а не изменяться, поэтому и сделали первый long-term backward-compatibility релиз.
                      +6
                      в глобальной перспективе lsFusion под силу полностью заменить ERP, RAD и SQL платформы, которые lsFusion превосходит по всем нефункциональным требованиям

                      Гм. Чтобы потеснить платформу, нужно предоставлять то, что делает платформа. ERP-платформа — это, в том числе, пользовательский интерфейс, механизмы безопасности и интеграции, и все это с учетом расширяемости и настройки, в идеале — конечным пользователем. А вы говорите именно о языке.

                        0
                        В этом языке есть в том числе и функции платформы (все что вы говорите). Это будет в следующих статьях. Хотя уже сейчас можете зайти на сайт и посмотреть / попробовать онлайн как это выглядит. Вся документация с примерами, how-to и т.п. есть.

                        Но платформа вся полностью language-based. Поэтому речь идет о ней как о языке. То есть как с Java говорим партия, подразумеваем ленин, говорим язык, подразумеваем платформа и наоборот.
                          +2
                          В этом языке есть в том числе и функции платформы (все что вы говорите).

                          Хм. Я даже перечитал два раза, чтобы быть уверенным, что там это написано: "в языке есть [...] все, что вы говорите". То есть в языке есть механизмы интеграции.


                          Можно пример того, как в вашем языке (нет, не в библиотеке, нет, не в платформе) сделан REST API?


                          Хотя уже сейчас можете зайти на сайт и посмотреть / попробовать онлайн как это выглядит.

                          Я вот сходил по вашей ссылке на ERP, и первый попавшийся мне файл — он на Java. И, кстати, никакой документации, никаких примеров, никакого how to, вообще ничего.

                            –2
                            del
                              +1

                              Вот я про это и говорю: это не в языке. Это вы на языке написали какой-то код, который что-то делает. Я такое могу написать, скажем, на C#, и это все еще не будет иметь отношения к платформе.


                              А платформа — это когда у вас есть в системе сущность Country, и она доступна по униформному протоколу по адресу https://вашаерп/api/Countries, а если пользователь накатил на систему расширение, которое в это Country добавляет новое свойство ISOCode, то оно автоматически отобразилось в API (и да, у вас к этому всему сразу доступны машинно-читаемые описания, по которым можно сгенерить клиента на любом языке).


                              В вашем случае функция платформы ограничивается тем, что вы автоматически выставляете свойство из модуля по фиксированному HTTP-адресу. Вы не делаете автоматического преобразования тела (ни на вход, ни на выход), там нет возможности управлять заголовками и кодами, и так далее, далее, далее. Для высокоуровневого — слишком много кода, для низкоуровневого — слишком мало контроля.

                                0
                                В вашем случае функция платформы ограничивается тем, что вы автоматически выставляете свойство из модуля по фиксированному HTTP-адресу. Вы не делаете автоматического преобразования тела (ни на вход, ни на выход), там нет возможности управлять заголовками и кодами, и так далее, далее, далее. Для высокоуровневого — слишком много кода, для низкоуровневого — слишком мало контроля.


                                Там есть все функции по управлению заголовками и кодами причем в обе стороны. Например свойство headers (ЕМНИП) при обращении из внешней системы, и HEADERS clause в операторе EXTERNAL (в примерах добавим в ближайшее время), в обратную сторону все тоже самое с именем headersTo. И этого хватало, к примеру, для интеграции с ЭСЧФ (без единой строки Java кода), а поверьте там очень жесткая интеграция (один только правильный порядок тегов в XML чего стоит).

                                На остальное ответил ниже.
                                  0
                                  Там есть все функции по управлению заголовками и кодами причем в обе стороны.

                                  В примере нет. К сожалению, угадывать, что в системе есть, слишком сложно.


                                  поверьте там очень жесткая интеграция (один только правильный порядок тегов в XML чего стоит).

                                  Гм, это то, что любой уважающий себя кодогенератор делает прямо по XSD? Или вам XSD не дали?


                                  На остальное ответил ниже.

                                  Про машинно-читаемое описание? Про no-code операции для объявленных в системе бизнес-сущностей?


                                  Как бы окей, да, вы дали низкоуровневый механизм, вы говорите, что в нем есть все средства контроля над протоколом, я вам поверю. Этим механизмом вы соревнуетесь с кучей существующих сервисных фреймворков, и я не думаю, что вы победите. А чтобы играть на поле платформ, вам нужны решения, которые предоставляют API внешним потребителям без единой дополнительной строчки кода.

                                    0
                                    В примере нет. К сожалению, угадывать, что в системе есть, слишком сложно.

                                    Забавно, но про пример с HEADERS мне писали 3 дня назад, но как-то не успели его добавить. Извиняюсь, лишим человека который писал How-to премии, договорились? :)
                                    Гм, это то, что любой уважающий себя кодогенератор делает прямо по XSD? Или вам XSD не дали?

                                    Я просто в качестве примера привел, что разработчики этого сервиса не сильно заморачивались простотой этого интерфейса, но например генерация headers для аутентификации делается средствами языка.
                                    Про машинно-читаемое описание? Про no-code операции для объявленных в системе бизнес-сущностей?
                                    Как бы окей, да, вы дали низкоуровневый механизм, вы говорите, что в нем есть все средства контроля над протоколом, я вам поверю. Этим механизмом вы соревнуетесь с кучей существующих сервисных фреймворков, и я не думаю, что вы победите. А чтобы играть на поле платформ, вам нужны решения, которые предоставляют API внешним потребителям без единой дополнительной строчки кода.

                                    Про машинно-читаемое описание, честно говоря совсем не понимаю, что вы имеете ввиду.
                                    No-code операции для объявленных в системе бизнес-сущностей, есть много в том числе такие, которых у других принципиально быть не может, например вы можете для ЛЮБОГО показателя на форме, в том числе вычисляемого (!) включить логирование. Да это наверное часть платформы, а не языка (тут да в коментарии ниже забыл добавить, что искать не language-based возможности кроме раздела управление)
                                      0
                                      Про машинно-читаемое описание, честно говоря совсем не понимаю, что вы имеете ввиду.

                                      Я имею в виду приблизительно вот так: https://вашаерп/api/Countries?wsdl — получаем готовый WSDL, который можно скормить любому совместимому инструменту и получить готовый SOAP-клиент, аналогично для OpenAPI и REST-клиентов.


                                      генерация headers для аутентификации делается средствами языка.

                                      Если мы говорим про "написать на языке программирования формирование заголовков запроса", то удивительно, что вы вообще считаете нужным об этом упоминать. Вот если бы у вас средствами платформы было автоматическое обновление OAuth-токенов для систем, к которым вы обращаетесь (и автоматическая подстановка этих токенов в запросы) — это да, повод для гордости.


                                      No-code операции для объявленных в системе бизнес-сущностей, есть много

                                      Я пока про API говорил. Банальный автоматический REST (а лучше — OData или GraphQL) или SOAP API для всех бизнес-сущностей в системе.


                                      в том числе такие, которых у других принципиально быть не может

                                      … у других кого? Платформ? Ну, я бы не был на вашем месте так уверен, платформы разные бывают.

                                        0
                                        Я имею в виду приблизительно вот так: вашаерп/api/Countries?wsdl — получаем готовый WSL, который можно скормить любому совместимому инструменту и получить готовый SOAP-клиент, аналогично для OpenAPI и REST-клиентов.

                                        Ну если честно, пока именно генерацию WSDL мы пока не делали. Тут важно что: интеграция в lsFusion разделена на две части, первое непосредственно обращение с передачей данных (как примитивов, так и XML и JSON) по сетевому протоколу, например, HTTP, а второе разбор переданных данных (чтение из XML, JSON в поля, свойства и т.п.). Соответственно за вторую часть (разбор данных) отвечают формы (это универсальный механизм и для отчетов и для экспортов/импортов и для интерактивных форм) и по сути они являются спецификацией для обмена. Да можно сделать конвертер Формы<->WSDL (а точнее генерацию формы по WSDL и наоборот), но в последнее время куда чаще сталкиваемся JSON Api, так что генерация формы по JSON пока актуальнее :) Но согласен задача важная хоть и не критичная, думаю сделаем в ближайшей версии (вот прямо сейчас начали :) )
                                        Вот если бы у вас средствами платформы было автоматическое обновление OAuth-токенов для систем, к которым вы обращаетесь (и автоматическая подстановка этих токенов в запросы) — это да, повод для гордости.

                                        Компилятор с универсальным predicate-pushdown (а не с частными случаями как в современных СУБД), пессимистичный cost-based планировщик (который не получает в середине плана 1 и сваливается в nested loop двух 10 тысячных таблиц), архитектура, в которой можно в любой момент откатить запрос \ транзакцию и начать ее заново, когда компилятор ошибся или произошел update conflict, и абсолютная реактивность везде (например материализации любых показателей в одно слово без ограничений) — это повод для гордости. А обновление OAuth-токен — это просто фича, которая в задачах ERP-платформ, с которых вы начали спор, не очень то нужна.
                                        Я пока про API говорил. Банальный автоматический REST (а лучше — OData или GraphQL) или SOAP API для всех бизнес-сущностей в системе.

                                        Я же вроде сверху писал про REST Api. Но в любом случае предлагаю дождаться третьей статьи там все будет. А то я ее тут уже по сути начал писать.
                                        … у других кого? Платформ? Ну, я бы не был на вашем месте так уверен, платформы разные бывают.

                                        Для того чтобы это сделать нужно поддержать полную инкрементальность (реактивность) по данным, что является очень сложной архитектурной задачей (я в тексте статьи кидал ссылки, что у Oracle и Microsoft получилось в одном частном случае — обновляемых материализованных представлениях). Так что, если вы не уверены, приведите пример.
                                          0
                                          Соответственно за вторую часть (разбор данных) отвечают формы

                                          И почему-то мне кажется, что все приведенные ради примеры — это не так, а совсем иначе.


                                          А обновление OAuth-токен — это просто фича, которая в задачах ERP-платформ, с которых вы начали спор, не очень то нужна.

                                          Эм. Она как раз очень нужна, потому что современные платформы постоянно интегрируются с кем-то. Я, собственно, несколько лет только интеграционными задачами и занимался.


                                          Для того чтобы это сделать нужно поддержать полную инкрементальность (реактивность) по данным

                                          Я боюсь, что у вас может быть свое представление того, что считать "логгированием".


                                          что является очень сложной архитектурной задачей

                                          Паттерн Event Sourcing.

                                            –1
                                            И почему-то мне кажется, что все приведенные ради примеры — это не так, а совсем иначе.

                                            В языке есть shortcut для импортов из плоских форматов, когда форму не надо создавать (она создается неявно). Собственно они в примере и используются. Вы лучше пример в попробовать онлайн или обращении из внешней системы посмотрите.
                                            Эм. Она как раз очень нужна, потому что современные платформы постоянно интегрируются с кем-то. Я, собственно, несколько лет только интеграционными задачами и занимался.

                                            В этом смысле современный мир достаточно забавен. Платформы постоянно интегрируются с фейсбуками, а когда человек стоит в банке и выполняет какую-то простейшую операцию, при этом операторша такое ощущение набирает войну и мир, никого не волнует. Точнее не так, они считают что это криво, но что поделаешь. Так вот поделать можно много чего, просто в этом винигрете из ORM, SQL, Reporting systems, и т.п. тяжело что-то сделать. Даже с нуля.
                                            Собственно вы несколько лет только интеграционными задачами занимались, а мы последние n-лет разгребали конюшни из Дельфи/Ораклов, Аксапт, 1Ск, Аксессов и кучу чего еще. И поверьте там все далеко не так хорошо, раз их владельцы решили их поменять.
                                            Я боюсь, что у вас может быть свое представление того, что считать «логгированием».

                                            Смотрите вы видите, на форме какой нибудь показатель, не знаю, например в скольки ассортиментных матрицах учавствует товар. Этот показатель считается каким-то сложным способом (как хватило фантазии разработчика, заказчика). На нижнем уровне это естественно должно выполняться в SQL потому как иначе в FMCG сети с их объемами все естественно ляжет (но это детали разработчик и пользователь этого не видят / не знают). И дальше пользователь решил, а давайте я его буду логировать. Он жмет правую кнопку и выбирает логировать. После этого он может нажать правую кнопку и просмотреть когда и кем это свойство изменялось (еще раз оно не хранится, а вычисляется, в том числе с импользованием десятка таблиц). Причем оно может изменяться в куче сценариев, товар перенесли между матрицами, матрицы перепривязали к другим матрицам и т.п. Все эти сценарии выполняют разные люди / написаны разными разработчиками, которые вообще не знают ни про какое логирование, ни про существование формы с этим показателем, ни даже о существовании самого показателя.

                                            На самом деле, чтобы реализовать это эффективно, нужно сделать механизмы инкрементального обновления каждого типа оператора (скажем GROUP SUM, (+) и RECURSION инкрементируются очень хорошо, композиции так себе, PARTITION ORDER терпимо), затем нужен компилятор запросов с эффективным predicate push down (не колонка-колонка как в Oracle и MSSQL а в общем случае с UNION'ами, с группировкой внешнего контекста и т.п.), с материализацией подзапросов (все СУБД офигенные оптимисты и очень любят косячить со статистикой и estimate'ть 1 row, после чего nested loop может угрохать сервер), правильное разбиение логического условия на условие запроса и тип join'а (зачем SQL разделил логику отборов в типы JOIN'ов и WHERE, а просто не сделал скажем чтобы для FULL JOIN можно было писать WHERE IN JOIN a OR IN JOIN b — загадка), а это нужно делать с учетом статистики и там еще с десяток таких оптимизаций. И без каждой из этих оптимизаций никуда, проверено практикой и разборками с заказчиками. И вы не скажете разработчику сказать, ты не правильно сделал запрос, он вообще не в курсе, что такое SQL. И сделать так чтобы все это работало из коробки на больших объемах поверьте очень сложная задача. И именно на нее ушло целых 12 лет.
                                              0
                                              Платформы постоянно интегрируются с фейсбуками

                                              Не с фейсбуками, а с е-коммерсом, складскими платформами, соседним CRM и еще миллионом вещей, которые бизнес использует для своей работы.


                                              Собственно вы несколько лет только интеграционными задачами занимались, а мы последние n-лет разгребали конюшни из Дельфи/Ораклов, Аксапт, 1Ск, Аксессов и кучу чего еще.

                                              Вы не одиноки в этом разгребании.


                                              Смотрите вы видите, на форме какой нибудь показатель, не знаю, например в скольки ассортиментных матрицах учавствует товар. Этот показатель считается каким-то сложным способом (как хватило фантазии разработчика, заказчика). На нижнем уровне это естественно должно выполняться в SQL

                                              Гм. Вы только что ввели невозможный набор ограничений, потому что есть расчеты, которые невозможно (или избыточно сложно) сделать в SQL.


                                              И сделать так чтобы все это работало из коробки на больших объемах поверьте очень сложная задача

                                              Поверю. Невозможная. Поэтому я не верю, что вы ее решили в полном объеме — скорее что вы выбрали набор (вполне разумных) ограничений, в рамках которых вы ее решили, а для всех остальных случаев добавили какие-то (вполне разумные) заглушки.

                                                –1
                                                Гм. Вы только что ввели невозможный набор ограничений, потому что есть расчеты, которые невозможно (или избыточно сложно) сделать в SQL.

                                                В SQL да, а в более высокоуровневых операторах (группировка, разбиение и рекурсия) без проблем.
                                                Поверю. Невозможная. Поэтому я не верю, что вы ее решили в полном объеме — скорее что вы выбрали набор (вполне разумных) ограничений, в рамках которых вы ее решили, а для всех остальных случаев добавили какие-то (вполне разумные) заглушки.

                                                Вот уже близко к сути подошли. Я понимаю, что не верите, я честно говоря сам по началу заходил к клиентам, и бродил по формам, просто наблюдая как все работает, понимая какая махина крутится внутри. Ощущение как на самолете летишь, как такая бандура из железа вообще может быть в воздухе :).
                                                Явных ограничений нет, в некоторых случаях может потребоваться оптимизация производительности, но там как в самолете (не тот который индусами по 9 долларов в час, а в идеальном), каждый верхний оптимизатор подстраховывает нижний, поэтому такие случаи если и возникают то при стечении большого количества разных факторов.
                                                То есть, к примеру, те же материализации разработчики расставляют по джедайски, IDE подсказывает приблизительную сложность (то есть как далеко до следующих материализованных показателей), а материализуем-ка это свойство. В том числе можно материализовать PARTITION и RECURSION (современные субд даже если в запросе подзапросы есть отказываются это делать, не говоря уже про такие сложные операторы, кстати забавно что RECURSION при этом на самом деле хорошо «инкрементится»).
                                                  +1
                                                  В SQL да, а в более высокоуровневых операторах (группировка, разбиение и рекурсия) без проблем.

                                                  Вроде бы только что было ограничение "должно выполняться в SQL". Уже нет?


                                                  Явных ограничений нет

                                                  Ну то есть, какую бы операцию я не решил вызвать, будет работать, будет работать за предсказуемое время, и будут сохраняться все ранее сделанные гарантии — например, будут видны все изменения данного значения, безотносительно того, заходили ли люди на форму, и время/причина изменения?

                                                    0
                                                    Вроде бы только что было ограничение «должно выполняться в SQL». Уже нет?

                                                    Тут видимо мы не допоняли друг друга. SQL вообще полон по тьюрингу, на нем можно любое вычисление реализовать. Я думал что речь шла о том, что есть вычисления, которые на SQL просто сложно задавать (настолько что это «невозможно» сделать). Соответственно написал, что на более высокоуровневых операторах (которые во всяком случае функциями а не таблицами оперируют) это куда проще.

                                                    Но выполняться естественно должно на SQL, иначе не вытянет по производительности.
                                                    Ну то есть, какую бы операцию я не решил вызвать, будет работать, будет работать за предсказуемое время, и будут сохраняться все ранее сделанные гарантии — например, будут видны все изменения данного значения, безотносительно того, заходили ли люди на форму, и время/причина изменения?

                                                    Да все чисто, нажал включить логирование, нажал правую кнопку увидел когда изменялся показатель и чье изменение к этому привело.

                                                    На самом деле там просто неявно создается свойство, событие и форма:
                                                    a(X x,Y y) = ...
                                                    logA = DATA (Session, X, Y) 
                                                    WHEN CHANGED(a(x,y)) DO
                                                          logA(currentSession(), x, y) <- a(x,y);
                                                    FORM LogA
                                                          OBJECTS x=X, y=Y PANEL
                                                          OBJECTS s=Session
                                                          PROPERTIES dateTime(s), user(s), logA(s,x,y)
                                                    ;
                                                    

                                                    Ну и на саму форму в которой включалось логирование неявно добавляется событие:
                                                    ...
                                                    PROPERTIES a(x,y) ON SHORTCUT 'показать лог' {
                                                         SHOW LogA OBJECTS x=x, y=y;
                                                    }
                                                    ...
                                                    

                                                    Но это я уже сильно забежал вперед.
                                                      +1
                                                      SQL вообще полон по тьюрингу, на нем можно любое вычисление реализовать.

                                                      Но будет ли это эффективно?


                                                      Я думал что речь шла о том, что есть вычисления, которые на SQL просто сложно задавать

                                                      Нет, речь шла о том, что есть вычисления, которые на SQL делать неэффективно, а есть операции, которые вообще не вычисления.


                                                      Но выполняться естественно должно на SQL, иначе не вытянет по производительности.

                                                      Вы правда никогда не видели операций, которые быстрее делать не на SQL?


                                                      Да все чисто, нажал включить логирование, нажал правую кнопку увидел когда изменялся показатель и чье изменение к этому привело.

                                                      Эмм. Вот представьте себе, что у вас показатель рассчитывается внешней системой; вызов веб-сервиса, который отдает вам некое значение в зависимости от других показателей на форме, и этот веб-сервис — не чистая функция. Как вы будете показывать, от чего зависит текущее значение?

                                                        0
                                                        Нет, речь шла о том, что есть вычисления, которые на SQL делать неэффективно, а есть операции, которые вообще не вычисления.

                                                        Ну я вел речь именно про вычисления. И практически все алгоритмически простые задачи решаются эффективно на SQL. Под алгоритмически простыми я имею ввиду, суммы, фильтры, разбиения и т.п., но их может быть просто много например штук 10 при расчете.
                                                        Вы правда никогда не видели операций, которые быстрее делать не на SQL?

                                                        Видел. Например задача коммивояжера. Но речь то не про такие операции, а про вычисления различных показателей в большинстве ИС / бизнес-приложений.
                                                        Эмм. Вот представьте себе, что у вас показатель рассчитывается внешней системой; вызов веб-сервиса, который отдает вам некое значение в зависимости от других показателей на форме, и этот веб-сервис — не чистая функция. Как вы будете показывать, от чего зависит текущее значение?

                                                        А я где-то писал про веб-сервис? Речь в примере шла только про данные внутри системы.
                                                          0
                                                          И практически все алгоритмически простые задачи решаются эффективно на SQL.

                                                          Вот, скажем, нахождение k ближних в n-мерном пространстве, да?


                                                          Под алгоритмически простыми я имею ввиду, суммы, фильтры, разбиения
                                                          [...]
                                                          Но речь то не про такие операции, а про вычисления различных показателей в большинстве ИС

                                                          А, вот и пример того, о чем я говорил. Вы приняли некий набор ограничений, и он вам кажется совершенно естественным. Но у других бизнесов, внезапно, другие задачи. Например простая задача sentiment analysis для входящих, или простая задача выявления аномалий, или простая задача рекомендаций...


                                                          А я где-то писал про веб-сервис?

                                                          Я специально спрашивал: "какую бы операцию я не решил вызвать". Вызов веб-сервиса, вроде как, полновправная операция в вашем языке, мне это совсем недавно рассказывали.


                                                          Речь в примере шла только про данные внутри системы.

                                                          Вот и еще один пример "разумных ограничений". Впрочем, даже для них выполняться будет плохо, я подозреваю, но тут вопрос того, насколько вы хитры и изворотливы в простановке зависимостей.

                                                            0
                                                            А, вот и пример того, о чем я говорил. Вы приняли некий набор ограничений, и он вам кажется совершенно естественным. Но у других бизнесов, внезапно, другие задачи. Например простая задача sentiment analysis для входящих, или простая задача выявления аномалий, или простая задача рекомендаций...

                                                            Ну на балалайке платформа тоже играть не умеет. Давайте так, например, в ERP-системах описанных задач штук 5. При этом «обычных» задач / показателей без вывертов тысяч 40. Тут не то что правило Парето нарушается, тут оно даже близко не стояло.
                                                            Я специально спрашивал: «какую бы операцию я не решил вызвать». Вызов веб-сервиса, вроде как, полновправная операция в вашем языке, мне это совсем недавно рассказывали.

                                                            Это логика действий, а речь опять таки шла о вычислениях. Но любых вычислениях, вообще любых. Понятно, что погоду она предсказывать не умеет, если вы это имеете ввиду под «разумными ограничениями», ok, пусть будет так. Хотя тут становится интересно, а ограничения в Oracle и MSSQL на материализованные обновляемые представления в статье — разумные?
                                                              0
                                                              Давайте так, например, в ERP-системах описанных задач штук 5.

                                                              … и эти 5 в какой-то момент могут стать market differentiator.


                                                              Это логика действий, а речь опять таки шла о вычислениях.

                                                              У вас было написано:


                                                              "Этот показатель считается каким-то сложным способом (как хватило фантазии разработчика, заказчика)."


                                                              И я явно переспросил: какую бы операцию. Вызов веб-сервиса для расчета чего-то в нынешнем мире — вполне себе сложный (даже не такой уж и сложный) способ расчета.


                                                              Но любых вычислениях, вообще любых.

                                                              Выше уже выяснили, что не любых. Forward propagation по нейронной сети для получения классификатора по тексту? Мне почему-то кажется, что нет.


                                                              Я не спорю, что вы решили хорошую, красивую, сложную задачу. Я бы с ней не справился.

                                                                –2
                                                                … и эти 5 в какой-то момент могут стать market differentiator.

                                                                В этом и ирония, операторша пишущая трехтомник при выполнении простейшей операции и очереди в кассу банка людей мало волнуют. А вот интеграция с фейсбук — это market differentiator. Но с другой стороны — селяви.
                                                                И я явно переспросил: какую бы операцию. Вызов веб-сервиса для расчета чего-то в нынешнем мире — вполне себе сложный (даже не такой уж и сложный) способ расчета.

                                                                Помните в КВН очень старый номер детей лейтенанта Шмидта с «падлавил». Ок, вы опять выиграли :)
                                                                  0
                                                                  В этом и ирония, операторша пишущая трехтомник при выполнении простейшей операции и очереди в кассу банка людей мало волнуют. А вот интеграция с фейсбук — это market differentiator.

                                                                  Ну, если не считать того, что я писал не про вещи типа интеграции с фейсбук, а про вещи типа "эскалируй на менеджера кейс, в котором клиент очевидно раздражен"...


                                                                  Я не знаю, про каких людей, которых мало волнует, думаете вы. Я думаю про людей, которые пытаются сделать рутинные ежедневные операции (типа ввода расходов за поездки) проще и быстрее.

                                                                    0
                                                                    Не принимайте на свой счет. Я просто общаюсь в том числе с людьми из банковской сферы, и они так бодро расказывают как у них все классно, а когда им задаешь предметные вопросы про вполне бытовые вещи, они говорит да, криво, неудобно, но что поделаешь. И вместо того чтобы лечить болезнь, они начинают лечить симптомы, типа а давайте к 30 плохо работающим системам, прикрутим еще 31-ю, которая будет интегрировать эти 30 систем. В итоге получаем 31 плохо работающую систему. Они реально считают, что технический долг можно решить, добавляя сверху новые и новые костыли.
                              0
                              и первый попавшийся мне файл

                              Это потому, что буква «j» идет в английском раньше буквы «l». Вообще, там сейчас 323 файла Java (в основном legacy код, который появился до многих возможностей в самой платформе) и 2274 файла на lsFusion.
                              И, кстати, никакой документации, никаких примеров, никакого how to, вообще ничего

                              Просто ERP на ее базе — пока что коммерческий продукт. И это единственная в нем защита от несанкционированного распространения.
                          0
                          Не в ту ветку.
                          Можно пример того, как в вашем языке (нет, не в библиотеке, нет, не в платформе) сделан REST API?

                          Вот тут. Но это обращение к внешней системе. Что касается из внешней системы — любое действие можно вызвать из внешней системы при помощи http запроса. То есть фактически создание действия это и есть создание REST Api. Собственно у нас очень часто так и идет интеграция — создаем действие doSomething() {}, а тем кто интегрируется кидаем ссылку хттп://mysite.com/exec?action=doSomething. Хотя конечно если захотеть можно и тут придраться.
                          Я вот сходил по вашей ссылке на ERP, и первый попавшийся мне файл — он на Java

                          Ну как я уже писал первые проекты начали внедряться 8 лет назад, а механизмы интеграции появились года 3 назад. Честно говоря давно собирались их переделать, но тут решили не трогать то, что работает. А так, за последние 3 года в ERP хорошо если 500 строк кода на Java сделано, и то это работа с каким-то специфичным оборудованием, где уже есть готовые библиотеки.
                          И, кстати, никакой документации, никаких примеров, никакого how to, вообще ничего.

                          Документация, Примеры (в том числе в попробовать онлайн), How-to
                            0
                            любое действие можно вызвать из внешней системы при помощи http запроса

                            И это функция языка или платформы?


                            Хотя конечно если захотеть можно и тут придраться.

                            Да зачем придираться, достаточно одного простого пункта: как получить машинно-читаемое описание параметров и ответов вашего действия?

                              0
                              И это функция языка или платформы?

                              Давайте так, во-первых, прямо в языке есть функция интеграции (копия из документации по ссылке):

                              externalHTTP()  { 
                                  EXTERNAL HTTP GET 'https://www.cs.cmu.edu/~chuck/lennapg/len_std.jpg' TO exportFile; 
                                  open(exportFile()); 

                                  EXTERNAL HTTP 'http://tryonline.lsfusion.org/exec?action=getExamples' PARAMS JSONFILE('\{«mode»=1\}'TO exportFile; // фигурные скобки escape'ся так как используются в интернационализации
                                  IMPORT FROM exportFile() FIELDS () TEXT caption, TEXT code DO
                                      MESSAGE 'Example : ' + caption + ', code : ' + code;
                                      
                                  EXTERNAL HTTP 'http://tryonline.lsfusion.org/exec?action=doSomething&someprm=$1' BODYURL 'otherprm=$2&andonemore=$3' PARAMS 1,2,'3'// передает в BODY url-encoded второй и третий параметры
                              }
                              externalSQL ()  { 
                                  EXPORT TABLE FROM bc=barcode(Article a) WHERE name(a) LIKE '%Мясо%'// получаем все штрих-коды товаров с именем мясо
                                  EXTERNAL SQL 'jdbc:mysql://$1/test?user=root&password=' EXEC 'select price AS pc, articles.barcode AS brc from $2 x JOIN articles ON x.bc=articles.barcode' PARAMS 'localhost',exportFile() TO exportFile; // читаем цены для считанных штрих-кодов
                                  
                                  // для всех товаров с полученными штрих-кодами записываем цены
                                  LOCAL price = INTEGER (INTEGER);
                                  LOCAL barcode = STRING[30] (INTEGER);
                                  IMPORT FROM exportFile() TO price=pc,barcode=brc;
                                  FOR barcode(Article a) = barcode(INTEGER i) DO 
                                      price(a) <- price(i);
                              }

                              Во-вторых, еще раз это lsFusion это и язык и платформа. Точно так же как Java это и язык и платформа. Точнее даже не так, есть Java язык и Java платформа, точно так же есть и lsFusion язык и lsFusion платформа.
                                0
                                прямо в языке есть функция интеграции

                                … которая обеспечивает только вызовы наружу, но не входящие. Кстати, вы вообще разделяете язык и его стандартную библиотеку?


                                точно так же есть и lsFusion язык и lsFusion платформа.

                                Именно об этом я и говорю: чтобы заявлять "в глобальной перспективе lsFusion под силу полностью заменить ERP, RAD и SQL платформы", нужно говорить о платформе, а не о языке. А статья, насколько я ее смог прочитать, именно о языке.

                                  0
                                  … которая обеспечивает только вызовы наружу, но не входящие.

                                  Да, но и средства интеграции прямо в языке тоже есть. Хотя ладно, немного глупый спор, будем считать, что вы выиграли :).
                                  Кстати, вы вообще разделяете язык и его стандартную библиотеку?

                                  Честно говоря, разделяли, первые лет 10. Но потом когда встал вопрос автоматизации всех процессов (а у нас почти все процессы автоматизированы до одной кнопки) возник вопрос целесообразности поддержки этой библиотеки отдельно. То есть для нее нужны отдельные версии, репозитарии, нужно постоянно переключаться между несколькими ветками и т.п… При этом выигрыш был непонятен, модульность обеспечивается другими средствами (REQUIRE модуль в заголовке), а экономить сто килобайт на сто мегабайтном файле смысла очень мало. Поэтому где-то 2 года назад решили долить большинство стандартных библиотек внутрь платформы, тем более что в платформе и так количество системных библиотек было сопоставимо с количеством этих стандартных библиотек.
                                  Именно об этом я и говорю: чтобы заявлять «в глобальной перспективе lsFusion под силу полностью заменить ERP, RAD и SQL платформы», нужно говорить о платформе, а не о языке. А статья, насколько я ее смог прочитать, именно о языке.

                                  Еще раз платформа практически полностью language-based. То что вы нашли одну фичу (обращение из внешней системы) не language-based, поздравляю, но вам очень повезет если найдете вторую (поверьте я участвовал в написании документации знаю о чем говорю). Плюс теоретически кто-то другой может запилить другую реализацию платформы построенной на этом языке, и что тогда? Вы же не придираетесь что Java как язык продвигают, а не как платформу.
                                    0
                                    Поэтому где-то 2 года назад решили долить большинство стандартных библиотек внутрь платформы, тем более что в платформе и так количество системных библиотек было сопоставимо с количеством этих стандартных библиотек.

                                    Ну то есть единственный способ этим воспользоваться — это писать для одной платформы на одном языке. Хм.


                                    Еще раз платформа практически полностью language-based.

                                    Что вы понимаете под этим утверждением?


                                    Вы же не придираетесь что Java как язык продвигают, а не как платформу.

                                    А мне, собственно, все равно, потому что я знаю про существование Kotlin и Scala. А учитывая, что сам я пишу в основном для .net, где это разделение еще сильнее, мне все равно дважды.

                                      0
                                      Ну то есть единственный способ этим воспользоваться — это писать для одной платформы на одном языке. Хм.

                                      Не совсем понял, о чем речь. Нет, можно писать и на Java и на Kotlin, если сильно понадобится, что мы кстати и делаем, собственно ссылку вы сами приводили выше. Подключается проще некуда f = INTERNAL 'my.class.action' и используйте как обычное действие. Наоборот все чуть сложнее, но поверьте это все равно куда проще чем в условном ORM. Ну и если надо DI, то сам сервер можно подключать через Spring Beans, и наоборот если в той же виртуальной машине нужен мини Java сервер (вроде оборудования как вы видели), его можно через Spring Beans подключить к серверу приложений.
                                      Что вы понимаете под этим утверждением?

                                      То есть любая абстракция системы задается кодом на этом языке (кроме дизайна отчетов там — xml, ну в будущем кастомизацию дизайна форм на react'е скорее всего подключим там будет javascript/react.
                                      А учитывая, что сам я пишу в основном для .net, где это разделение еще сильнее, мне все равно дважды.

                                      Я если честно так и подумал, когда спор возник про разделение языка и платформы (у джавистов таких вопросов обычно не возникает). Ну ок, вы выиграли поздравляю. Но это не отрицает факта существования языка lsFusion и того, что платформы на нем, могут конкурировать в том числе с ERP-платформами.
                                        0
                                        Но это не отрицает факта существования языка lsFusion и того, что платформы на нем, могут конкурировать в том числе с ERP-платформами.

                                        … а вот это утверждение намного скучнее оригинального, потому что понятно, что платформы, написанные на (каком-то) языке, могут конкурировать с другими платформами. Вопрос в том, как писать будут.

                                          0
                                          Вот честно, хотел написать оригинальное утверждение, но тогда этот спор будет бесконечным. Просто сам язык (как и платформа) ну очень сильно отличается от всего, что есть сейчас на рынке (сейчас оставим вопрос в какую сторону, просто отличается). А мы спорим о терминологии. :(
                                            0
                                            Просто сам язык (как и платформа) ну очень сильно отличается от всего

                                            Ну отличается, да. Но это никак не говорит ни за успешность, ни за неуспешность конкуренции — она все равно будет зависеть в первую очередь от способностей тех, кто на языке пишет.

                            –1
                            del
                              +4
                              >> Он гораздо лучше оптимизируется

                              Когда сочиняли Хаскель, именно так и думали. Но потом «что-то пошло не так (с)».

                              >> само слово “свойство” короче, чем «чистая функция», плюс не имеет ненужных ассоциаций.

                              Зря вы заменили привычные названия на вам приятный новояз. Это мешает тем, кто знает о чём речь. Хотя да, обычно продвижение языков начинают с полностью некомпетентной части разработчиков, что даёт большое количество участников, но если при этом заранее исключать грамотных участников — вы быстро выйдете из моды, ибо некомпетентная часть очень скоро перескочит на очередной «тренд».

                              >> SQL большинство из них недолюбливают, слишком уж там много магии под капотом

                              Здесь опять уход в сторону примитива. Не «недолюбливают», а просто не понимают. Хотя опять же — повысить ЧСВ недостаточно грамотных разработчиков в рекламных целях — это работающий рекламный ход.

                              >> в глобальной перспективе lsFusion под силу полностью заменить ERP, RAD и SQL платформы

                              В целом наблюдаю картину стандартной эйфории из серии «мы строили, строили, и вот — построили!». Понятно, что в такие моменты возникает желание срочно слетать на Марс и именно на только что построенном Запорожце. И не беда, что к старому запору придётся прикрутить космическую ракету, ведь главное — как нам нравится наш великий замах!

                              Конструктивно же — выход «на рынок» с новым инструментом есть дело затратное. Затраты, обычно, мало кто считает. Поэтому мало кто реально выходит на рынок.

                              Большие конторы, у которых денег немеряно, обычно продумывают стратегию использования новых инструментов. Сначала они спрашивают себя — а что мы можем поиметь с вывода на рынок вот такой вот тулзы? И просто некие абстрактные толпы поклонников в данном случае им мало интересны. Потому что им нужно окупить затраты на вывод системы в массы. А теперь сравним с вами — что вы намереваетесь получить? Если ответ привычный «много денег», то возникает вопрос — как? Вы думали? Сильно подозреваю, что нет. Точнее «в общих чертах мне всё понятно» как раз на самом деле является ответом типа «не думал». Бывает ещё ответ «хочу увидеть рост популярности моего любимого зайчика (инструмента)». Но здесь возникает второй вопрос — а вы знаете, сколько денег стоит вывести инструмент в разряд «популярных»? Подозреваю, что ответ опять — нет.

                              Ладно, это всё лирика, так, на будущее, может вас заинтересует.

                              По языку же скажу одно — заимствование давно известных подходов в новом инструменте никогда не являлось созданием нового подхода. Хотя да, для целей маркетинга и не такое подходит.

                              Новый язык должен дать нечто ранее не использованное в других языках. Ваша задача — указать это нечто и тем заманить массы в ваш лагерь. Из выше приведённого текста суть преимущества вашего языка не очевидна. В основе — функциональный подход, скрещенный с процедурным с надеждой на декларативность получившихся SQL-like конструкций. При этом основной нишей изначально была явно работа с БД, хотя в дальнейшем «Остапа понесло» и набор ниш быстро расширился. В целом получаем просто новый язык, который просто нравится автору, который считает, что создал нечто небывалое, а потому всем это будет интересно и все быстро станут это всё учить, использовать, ну и далее близится happy end.

                              Не хочу сильно бить по самолюбию, тем более, что вижу интересный подход, но тем не менее хочу повторить — сделать чудо в современных реалия сложно. И боюсь, у вас не получилось. Хотя если вы настаиваете — было бы неплохо понять преимущества, которые позволят, например, тем же разработчикам ERP, делать продукт быстрее, качественнее, дешевле и т.д.
                                0
                                было бы неплохо понять преимущества, которые позволят, например, тем же разработчикам ERP, делать продукт быстрее, качественнее, дешевле

                                А можно уточнить, при помощи каких платформ/технологий сейчас по-вашему можно быстро, качественно и дешево делать ERP продукты? Ну, чтобы было с чем сравнивать…
                                  +1
                                  Для «по быстрому» — на тех, которые знает разработчик.

                                  Для «качественно» — при помощи средств, позволяющих гибко оперировать абстракциями. Например — Java. Хотя и Java ещё есть куда расти.

                                  Для автора же важно понять хотя бы одну область, где его детище окажется явно лучше других. Ну и показать эту область остальным.
                                    0
                                    Для «по быстрому» — на тех, которые знает разработчик.

                                    Предположим разработчик ничего не знает, или хочет делать не «по быстрому», а надолго и всерьез.
                                    Для «качественно» — при помощи средств, позволяющих гибко оперировать абстракциями. Например — Java. Хотя и Java ещё есть куда расти.

                                    Вы всерьез предлагаете делать логику заказов/поставок/платежей на pure java? А еще же фронт надо будет писать.

                                    Для автора же важно понять хотя бы одну область, где его детище окажется явно лучше других. Ну и показать эту область остальным.

                                    Собственно мы сейчас и обсуждаем область — ERP системы. На данный момент, лидером в этой области (именно ERP-платформ) в РФ является 1С. Даже не касаясь всех его недостатков, у него как минимум есть один достаточно серьезный — он коммерческий. Предложите что-то другое, на чем можно делать продукт быстро, дешево и качественно.
                                    И опять же речь идет не только о ERP. Допустим вам нужно написать какой-то простой процесс, но google docs уже недостаточно. Что Вы возьмете?
                                      +1
                                      >> Вы всерьез предлагаете делать логику заказов/поставок/платежей на pure java?

                                      Логика — это как раз pure язык ХХХ. То есть чистая логика (чистые алгоритмы) пишутся на чистых языках (без библиотек и прочего).

                                      >> А еще же фронт надо будет писать.

                                      Есть GWT.

                                      >> Допустим вам нужно написать какой-то простой процесс

                                      Простой обычно означает — надо дёшево и быстро. Поэтому просто использую то, что знаю.
                                        0
                                        Логика — это как раз pure язык ХХХ. То есть чистая логика (чистые алгоритмы) пишутся на чистых языках (без библиотек и прочего).

                                        Хорошо, вот логика — есть миллион заказов, нужно посчитать их сумму. Как это сделать на pure языке XXX? Загрузите через ORM все в память? Или будет уже использовать не pure язык XXX, а pure язык YYY? Сколько в итоге языков надо знать, чтобы разработать простенькое приложение?
                                          +1
                                          Как это сделать на pure языке XXX?

                                          dbContext.Orders.Sum(o => o.Amount)

                                            –2
                                            Это слишком простой случай. В более сложном на LINQ вы уже не сможете сделать. Но это и не важно. Приведите пример на не проприетарной технологии.
                                              +2
                                              Это слишком простой случай.

                                              Какой попросили.


                                              В более сложном на LINQ вы уже не сможете сделать.

                                              На LINQ — не смогу, смогу на чем-то другом.


                                              Приведите пример на не проприетарной технологии.

                                              А с чего бы вдруг такое ограничение?

                                                +1
                                                LINQ как часть dotnet core выложена на гитхабе под MIT.
                                                  +1

                                                  Вообще говоря .net полностью опенсорсный и с более либеральной лицензией, чем у вас. Процитированный код можно выполнить вообще без единой строчки закрытого кода, если запускать на Linux против опенсорсной ДБ типа постгри.

                                                    0
                                                    Прошу прощения, давно не следил за LINQ. Майкрософт действительно в последнее время стал значительно более открыт к общественности. Подскажите, пожалуйста, а как LINQ сейчас запустить с PostgreSQL и Linux? Кто поддерживает?
                                                      +1

                                                      Entity Framework Core запускается на Linux из коробки и поддерживается Microsoft. Провайдер для PostgreSql поддерживается сообществом, но вполне продакшн-реди

                                                        0
                                                        А можно ссылку на провайдер для PostgreSQL. А то у меня Google выдает ссылку только на коммерческие, а также те, которые уже не поддерживаются.
                                                        Кроме того, а что насчет IDE? Есть какая-нибудь бесплатная IDE для разработки на LINQ?
                                                          0
                                                          Кроме того, а что насчет IDE? Есть какая-нибудь бесплатная IDE

                                                          VS Code.

                                        +1
                                        быстро, качественно и дешево делать ERP продукты?

                                        Вы же помните про "вычеркните одно" (а лучше два)?

                                          0
                                          На одном и том же языке можно разрабатывать в нескольких стилях. При этом сумма стилей охватит все заявленные требования.
                                            0

                                            Не одновременно. Ну не выйдет обмануть этот треугольник.

                                              0
                                              Теоретически выйдет, если будет предложена радикально новая технология, так например обогащение урана разорвало треугольник ТЭК, где одна из вершин была — логистика огромного количества топлива, а сегодня твэл можно условно на легковушке привезти, и так во всем.
                                                0
                                                Не существует абсолютных понятий «быстро», «качественно» и «дешево». Есть только быстрее, качественнее и дешевле. Так что тут все зависит от рынка. Если, например, на определенном рынке только 2 игрока и один лучше его по всем параметрам, то быстро, качественно и дешево возможно.
                                                  +1
                                                  Если, например, на определенном рынке только 2 игрока и один лучше его по всем параметрам, то быстро, качественно и дешево возможно.

                                                  Нет, это только быстрее, качественнее и дешевле. При этом это может быть плохо (просто конкурент делает еще хуже), медленно (то есть не успевает за ситуацией в бизнесе) и дорого (дороже, чем бизнес может себе позволить).

                                                    0
                                                    Вообще действительно — нужны критерии. Сравнительные преимущества вы отвергли, тогда нужно понять, что такое абсолютные показатели.

                                                    В плане «не одновременно» я согласен с комментарием выше — только до появления некоторого важного расширения, для которого вполне есть место.
                                              0
                                              Проблема в том, что заказчики, как правило, ничего не хотят вычеркивать. А маркетологи поставщиков всегда утверждают, что у них все три. И на заключении сделки все думают, что все будет так, как они считают.
                                              Но мне было просто интересно, что же предлагает использовать автор комментария для этой цели.
                                                0
                                                Проблема в том, что заказчики, как правило, ничего не хотят вычеркивать

                                                И чем же вы пожертвуете ради их желаний? Ни скоростью, ни качеством нельзя. Своим доходом, чтобы было дешево?


                                                А маркетологи поставщиков всегда утверждают, что у них все три.

                                                И врут. Не уподобляйтесь.

                                                  –1
                                                  И врут. Не уподобляйтесь.

                                                  К сожалению, тогда проиграете. Много раз проходили. Вы говорите все честно, а конкуренты врут по-черному. В итоге заказчик выбирает конкурента, так как три больше двух.
                                                  Потом конечно матерится по-черному, но поезд ушел. У нас мало кто готов выбросить то, за что уже заплачены деньги.
                                                    +1
                                                    К сожалению, тогда проиграете.

                                                    Или выиграете в долгосрочной репутационной перспективе. Это тоже работает.

                                                      0
                                                      >> выиграете в долгосрочной репутационной перспективе

                                                      Не могли бы вы представить примеры?

                                                      На мой взгляд репутации сегодня нет ни у кого. Хотя здесь можно поговорить о моём максимализме :)
                                                        +1
                                                        Не могли бы вы представить примеры?

                                                        Я боюсь, что мой личный опыт работы с какими-то поставщиками услуг, которые честно говорили сроки, и придерживались (в противовес тем, которые говорили "завтра" и были посланы), вам ничего не скажет.


                                                        На мой взгляд репутации сегодня нет ни у кого.

                                                        Репутация есть у всех.

                                            0
                                            Зря вы заменили привычные названия на вам приятный новояз.

                                            Там дело в том, что «чистая функция» — это функция. Она не подразумевает хранение значений, а свойство — подразумевает, то есть это может быть и поле и метод (а в современных языках такая штука называется именно свойство).
                                            В целом наблюдаю картину стандартной эйфории из серии «мы строили, строили, и вот — построили!». Понятно, что в такие моменты возникает желание срочно слетать на Марс и именно на только что построенном Запорожце. И не беда, что к старому запору придётся прикрутить космическую ракету, ведь главное — как нам нравится наш великий замах!

                                            Поверьте 12 лет разрабатывать что-то на голом энтузиазме или вере невозможно. Это можно за полгода склепать поделку, и пребывать в состоянии эйфории, но уже ко второму году это быстро проходит, дальше наступает холодный расчет. Особенно, когда вы работаете на самом жестком рынке — разработки информационных систем, где чтобы заставить сменить чужую систему вы должны доказать, что вы на несколько порядков лучше. Но как показала жизнь есть простой способ это сделать. Просто быть на несколько порядков лучше — когда вы можете набирать разработчиков с открытого рынка: людей без IT-образования, тем самым вы можете набирать в том числе лучших (иначе вы проиграете в споре за них крупным модным компаниям), когда эти люди могут делать по 5 задач в день, когда их можно свободно переключать между задачами из разных областей и т.д., тогда у вас остаются только два конкурента лень и жадность (даже глупость отступает на второй план). Поэтому, что касается вопроса про деньги, поверьте мы уже достаточно зарабатываем просто за счет того, что скажем на рынке FMCG-ритейла Беларуси (а это второй по деньгам сегмент после банков) у нас практически не осталось конкурентов (все выбирают либо между нами, либо остаться с тем что есть).

                                            Но вообще я так понял вы до сайта не добрались, там всего три страницы, с ответами на все ваши вопросы.

                                            Это кстати, а не функциональный, а комбинаторный подход (функциональный это передача параметров в качестве функций, а этого тут нет, точнее есть, но в слегка извращенном виде в виде метакодов).

                                            В любом случае я не совсем понял ваш тезис:
                                            a) Такой язык уже есть. Можно тогда пример хоть одного близко похожего.
                                            б) У него нет преимуществ. Специально для этого мы сделали сайт из 3-х страниц, где есть краткие преимущества, подробные преимущества и их сравнение. Если вы считаете что каких-то преимуществ нет, они не существенны, что же, можем здесь подискутировать. Хотя да я знаю, что на Хабре не принято отсылать на сторонние сайты, так что если нет, давайте просто дождемся следующих статей прежде чем делать выводы.
                                              +2
                                              >> Она не подразумевает хранение значений, а свойство — подразумевает

                                              Функция, возвращающая всегда одно и то же значение, как раз выполняет то, что вы считаете невыполнимым. Это по сути константа, но для чистоты функционального подхода все константы определены именно в виде функций, возвращающих одно и то же значение.

                                              >> вы до сайта не добрались

                                              Да, не посмотрел сразу. Но исправился :) Там в части «Разрабатывайте просто» такой аргумент «против всех»:

                                              >> Одна парадигма. Один язык

                                              Или вот такое (в «Разрабатывайте быстро»):

                                              >> Завершенная ERP-система среднего уровня сложности всего в 25K строках кода

                                              Здесь на самом деле «всё не так просто (с)». Один язык и одна парадигма появились начиная с машинных кодов. Да, машинные коды позволяют написать и OLTP и OLAP, но вы же понимаете, что реальность внесёт здесь некоторые коррективы.

                                              Ну а размер «средней ERP» — это вообще нечто абсолютно произвольное. Под такое определение подойдёт, например, 1С-Управление, требующее для написания ноль строк, ибо «всё украдено до нас (с)». И вы, получается, опять в проигрыше.

                                              Я бы рекомендовал вам, хотя бы примерно, вспомнить — а зачем вы придумали ту или иную фичу в вашем языке? И от этого вам станет яснее, какие конкретно преимущества вы ожидали, изобретая данный кусок языка. Ну а поняв, вы сможете указать другим — вот так вот делается быстрее потому что…

                                              Ну и в целом сам язык тоже не на пустом месте появился. Точнее — идея нового языка. Во введении вы его сравнили с SQL, и в целом очевидно, что раньше писали системы преимущественно на хранимых процедурах, так вот и покажите, что в сравнении с хранимками вы увидели вот такое-то преимущество, вот так-то облегчающее работу программиста. Хотя здесь стоит заметить, что хранимки — это обычно довольно убогий процедурный язык, не позволяющий гибко работать со сложными абстракциями, а потому даже небольшое повышение уровня абстракции языка даёт ту самую важную гибкость, ну и поэтому (весьма вероятно) вам понравилась идея нового языка. Только ведь в таком разрезе есть альтернатива — изучить другой, более мощный при работе с абстракциями язык. Вы сравнивали свой язык в этом отношении с более универсальными? И что вас не устроило? То есть, часто люди поддерживают нечто своё не потому, что оно лучше других вариантов, а просто потому, что оно понятнее этим людям, ведь они написали это нечто с нуля и знают много важных деталей, которые помогают им наращивать эффективность в отдельных местах. Только эти «отдельные места» быстро заканчиваются. То есть писать по шаблону формочки для ERP — это одно, а вот если потребуется усложнение алгоритма — здесь могут быть страшные засады.

                                              В общем — я бы сказал, что хотелось бы увидеть некую теоретическую базу, показывающую, что вы действительно сдвинули с места ту или иную проблему программирования, а не, в общем-то, рекламные слова про «среднюю ERP».

                                              Для примера — в функциональных языках привычной является функции типа fold, zip, которые агрегируют переданные в них данные, примерно как ваша конструкция GROUP, но отличие такое — эти функции можно написать самому, а не использовать библиотечные. То есть гибкость здесь в том, что не нужен новый язык для реализации важного алгоритма агрегирования данных. А у вас нужна новая конструкция, которую нельзя написать средствами языка. У вас гибкость, получается, заметно меньше.
                                                0
                                                Там в части «Разрабатывайте просто» такой аргумент «против всех»: "Одна парадигма. Один язык"

                                                … что многие разработчики, кстати, сочтут недостатком.

                                                  –1
                                                  В среде разработчиков вообще очень часто луддизм встречается. В этом случае простота в принципе недостатком считается.
                                                    0

                                                    Вопрос в том, простота ли это.

                                                  0
                                                  Ну а размер «средней ERP» — это вообще нечто абсолютно произвольное. Под такое определение подойдёт, например, 1С-Управление, требующее для написания ноль строк, ибо «всё украдено до нас (с)». И вы, получается, опять в проигрыше.

                                                  А вы вообще видели 1С-Управление торговлей и их код? Скачайте посмотрите. Там на порядок больше 25К строк кода, даже если не включать туда замаскированный в визуальное программирование код. Старое доброе процедурное програмирование с формированием SQL команд в строках.
                                                  Я бы рекомендовал вам, хотя бы примерно, вспомнить — а зачем вы придумали ту или иную фичу в вашем языке? И от этого вам станет яснее, какие конкретно преимущества вы ожидали, изобретая данный кусок языка. Ну а поняв, вы сможете указать другим — вот так вот делается быстрее потому что…

                                                  Вы не поняли, все фичи шли от потребностей. Фактически мы строили самолет в воздухе вместе с пассажирами. И у нас была очень эффективная метрика, если в конечном итоге заказчик звонит руководству, это значит очень важная проблема. То есть, если что-то тормозит, если разработчик долго работает над задачей, если особенности языка приводят к ошибкам, если разработчик не понимает чего то в языке, это все повод что-то пофиксить / доработать язык. И в конечном итоге мы его стабилизировали.
                                                  Только ведь в таком разрезе есть альтернатива — изучить другой, более мощный при работе с абстракциями язык. Вы сравнивали свой язык в этом отношении с более универсальными? И что вас не устроило?

                                                  Так нет ни одного даже близко похожего языка. Еще раз, SQL — используется в полной мере, lsFusion — надстройка над ним. Как вы вообще представляете, вот разработчик пишет:
                                                  FOR selected(Item i) DO
                                                  price(i) < — price(i) + 1;
                                                  Выполнять это императивно очень неэффективно. Но это можно скомпилировать в:
                                                  price(Item i) < — price(i) + 1 WHERE selected(i); и выполнить одним UPDATE запросом.
                                                  При этом разработчик не в курсе ни про SQL ни про сервер БД ни про сервер приложений. Чист как лист. Какой язык вы сюда прикрутите? И это мы еще до логики представлений не дошли.
                                                  В общем — я бы сказал, что хотелось бы увидеть некую теоретическую базу, показывающую, что вы действительно сдвинули с места ту или иную проблему программирования, а не, в общем-то, рекламные слова про «среднюю ERP».

                                                  Собственно и в статье есть про теоретический базис, и на сайте есть 32 преимущества (со ссылками на документацию и how-to), и есть сравнение этих преимуществ со всем, что есть на рынке по каждому из этих 32 преимуществ, чего там еще не хватает?
                                                  Для примера — в функциональных языках привычной является функции типа fold, zip, которые агрегируют переданные в них данные, примерно как ваша конструкция GROUP, но отличие такое — эти функции можно написать самому, а не использовать библиотечные. То есть гибкость здесь в том, что не нужен новый язык для реализации важного алгоритма агрегирования данных. А у вас нужна новая конструкция, которую нельзя написать средствами языка. У вас гибкость, получается, заметно меньше.

                                                  А например, там еще есть GROUP LAST InvoiceDetail i ORDER number(i) BY invoice(i);
                                                  Которые на самом деле если есть индекс по number будет выполняться на SQL-сервере как SELECT LIMIT 1, а еще туда надо возможно предикат с invoice протолкнуть, чтобы субд не посчитал подзапрос для всей базы, а потом join'л. Как тут эти fold'ы и zip'ы помогут?
                                                    0
                                                    Как тут эти fold'ы и zip'ы помогут?

                                                    Через разбор дерева выражений. Дальше все то же самое.

                                                      0
                                                      Через разбор дерева выражений. Дальше все то же самое.

                                                      И чем тогда это лучше языка? С языком хотя бы completion проще прикрутить, да и язык читабельнее.

                                                      Собственно мы поначалу пытались, но реально порог вхождения сильно повышался. Люди, которые до этого в IT не были, реально не въезжали.
                                                        0
                                                        И чем тогда это лучше языка?

                                                        Тем, что лучше обобщается. Только противопоставление неправильное, потому что это и есть язык. Чем один язык лучше другого?.. Ну, по-разному бывает.


                                                        С языком хотя бы completion проще прикрутить, да и язык читабельнее.

                                                        Мне кажется, вы что-то другое понимаете под деревом выражений.


                                                        sum seq = seq |> fold ((acc, x) -> acc + x)


                                                        Вот то, что в скобках после fold — это дерево выражений, которое можно преобразовывать во что угодно по вашему вкусу. Хотите — преобразуете в SQL.

                                                          –1
                                                          sum seq = seq |> fold ((acc, x) -> acc + x)


                                                          Давайте так, если я это покажу любому из наших разработчиков, он подумает что я вызываю демона.
                                                          Ну серьезно, для системных программистов допустим, но для прикладников…
                                                            0
                                                            если я это покажу любому из наших разработчиков, он подумает что я вызываю демона.

                                                            Это всего лишь вопрос привычки к синтаксису. Вот вам то же самое на чистом ванильном C# (там, правда, будет проблема со сложением, но мы сделаем вид, что ее нет): seq.Aggregate((acc, x) => acc.Plus(x))


                                                            Ну серьезно, для системных программистов допустим, но для прикладников

                                                            А "прикладники" пишут seq |> sum (ну или seq.Sum()), и у них все хорошо. До тех пор, пока им не надо написать какую-то вещь, которую вы на уровне базовой библиотеки не стали, и тогда они достают fold и пишут сложнее.


                                                            (Вообще, конечно, это весьма оскорбительное противопоставление)

                                                              0
                                                              То есть по вашему синтаксически:
                                                              seq.Aggregate((acc, x) => acc.Plus(x))

                                                              Лучше чем:
                                                              GROUP SUM seq(x)
                                                              Серьезно?

                                                              Да, привычнее, для большинства программистов работавших с функциональным программирования. но большинство «прикладников» пишут не seq |> sum, а на ABAP, 1C или на Delphi+Oracle/MSSQL. Во всяком случае на постсовке. И на западе ландшафт еще веселее, там всякие Cache, Progress и куча еще кого обитает и им такой синтаксис тоже не близок.

                                                              В любом случае та же группировка это вершина айсберга, с событиями, ограничениями и агрегациями что делать? И это еще до логики представлений не дошли.
                                                                0
                                                                То есть по вашему синтаксически: seq.Aggregate((acc, x) => acc.Plus(x))
                                                                Лучше чем: GROUP SUM seq(x)
                                                                Серьезно?

                                                                Нет, я этого не говорил нигде. Сравнивать надо seq.Sum() против GROUP SUM seq(x), и разница окажется исключительно вкусовой.


                                                                Нет, первое хорошо тем, что я могу написать


                                                                seq
                                                                .Aggregate(((sum, cnt), x) => (sum.Plus(x),cnt.Plus(1)))
                                                                .Select((sum, cnt) => sum/cnt)

                                                                если в базовой библиотеке нет расчета среднего, и это все равно можно будет оттранслировать на уровень выполнения.


                                                                с событиями, ограничениями и агрегациями что делать?

                                                                Разбираться по месту. Я отвечал на вопрос, как в случае использования ФВП решать проблемы оптимизации.

                                                                  0
                                                                  Разбираться по месту. Я отвечал на вопрос, как в случае использования ФВП решать проблемы оптимизации.

                                                                  К сожалению, по моему опыту лида, «разбираться по месту» — это значит «иди к лиду/архитектору, пусть скажет как делать». А хочется, чтобы была четкая схема, по которой человек (не лид) сам знал как делать, не отвлекая более дорогих людей.
                                                                    0
                                                                    А хочется, чтобы была четкая схема, по которой человек (не лид) сам знал как делать, не отвлекая более дорогих людей.

                                                                    Это "разбираться по месту" делается на этапе разработки платформы, а не имплементации бизнеса. Поэтому тем, кто имплементирует бизнес (собственно, прикладным программистам) уже выдаются готовые рельсы, как конкретно реализуется каждая типовая задача.


                                                                    Собственно, вопрос "ФВП против готовых агрегатов" — это тоже периода проектирования платформы, на этом промежутке еще нет людей, которые не "лиды/архитекторы".

                                                                      0
                                                                      Извините, но не очень понял — какой платформы? Можете все-таки примеры из жизни? То есть для разработки простенького бизнес-приложения, нужно сначала разработать платформу, а потом на ней само приложение?
                                                                      Давайте может больше конкретики. Конкретные платформы, например.
                                                                        0
                                                                        Извините, но не очень понял — какой платформы?

                                                                        Той самой, которая дает вам GROUP SUM.


                                                                        То есть для разработки простенького бизнес-приложения, нужно сначала разработать платформу, а потом на ней само приложение?

                                                                        Либо взять готовую. Либо разрабатывать вместе с приложением, но получится рискованно.


                                                                        Конкретные платформы, например.

                                                                        Ваша, например. .NET, например.

                                                                          –2
                                                                          Сравнивать нашу и .Net — это тоже самое, что сравнивать 1С и .Net. Как бы абсолютно разного уровня платформы для абсолютно разных разработчиков и целей. Вы согласны, что большинство 1С программистов вы не сможете переучить на .Net?
                                                                          Назовите, пожалуйста, бесплатную высокоуровневую платформу (то есть где вам не надо заморачиваться общением клиента и сервера, сервера и базы данных) которую сможет освоить, грубо говоря, бизнес-аналитик?
                                                                            0
                                                                            Сравнивать нашу и .Net — это тоже самое, что сравнивать 1С и .Net.

                                                                            А я их и не сравниваю. Вы просили привести пример платформы, чтобы было понятно, о чем я говорю, когда говорю о "разработке платформы" — вот вам пример. Не более того.


                                                                            Назовите, пожалуйста, бесплатную высокоуровневую платформу (то есть где вам не надо заморачиваться общением клиента и сервера, сервера и базы данных) которую сможет освоить, грубо говоря, бизнес-аналитик?

                                                                            А почему, собственно?

                                                                              –1
                                                                              Потому что именно это и является одним из преимуществ платформы lsFusion. Ее может освоить человек, который не может освоить .Net, так как она значительно проще. Это является конкурентным преимуществом. Разве нет?
                                                                                +1
                                                                                Ее может освоить человек, который не может освоить .Net, так как она значительно проще.

                                                                                Это утверждение, скажем так, нуждается в доказательстве. Но не суть.


                                                                                Это является конкурентным преимуществом. Разве нет?

                                                                                Да, простота в освоении является конкурентным преимуществом. Я с этим нигде не спорил. Я даже не спорю, что у lsFusion есть определенное количество преимуществ по сравнению с .NET.

                                                                              0

                                                                              Допустим, в lsFusion мне не нужно


                                                                              заморачиваться общением клиента, сервера и базы данных.

                                                                              Значит ли это также и то, что я не могу этим заморочиться, так как эти процессы не находятся вне моего контроля?

                                                                                0
                                                                                У вас есть определенные рычаги управления. Например, можно задавать каким образом логика будет отображаться в базу данных. Тут точно также, как вы не можете напрямую влиять на планы выполнения запросов в базе данных, но в некоторых можете давать hint'ы.

                                                                                И в целом Вы конечно можете всегда напрямую обратиться к базе данных через EXTERNAL или из Java кода через JDBC и делать что хотите под свою ответственность.
                                                                                  –1
                                                                                  У вас есть рычаги, но более высокоуровневые. Скажем в базе вы управляете материализацией свойств (определяете, что хотите хранить, а что хотите — вычислять), и раскладыванием их по таблицам (можете вообще все свойства в одну таблицу положить, а можете наоборот каждое свойство в отдельную) и индексами. Плюс есть хинты всякие, но они остались от древних времен, когда оптимизаторы не были настолько умными, поэтому мы их пока даже в документации не описывали, но может в будущем опишем (логика такая же как в хинтах СУБД)

                                                                                  Что контролировать в общении клиента и сервера у меня пока фантазии не хватает. Ну общаются и общаются. Все сделано так что максимум один round-trip, особо улучшать некуда. Есть пару мелких настроек с асинхронностью впрочем, но ничего особенного.

                                                                                  Ну и всегда Java в вашем распоряжении, на ней все что угодно реально бесшовно подключается во все стороны и смотрится как родное.
                                                                        0
                                                                        Нет, я этого не говорил нигде. Сравнивать надо seq.Sum() против GROUP SUM seq(x), и разница окажется исключительно вкусовой.

                                                                        Ок, а как h(y) = GROUP SUM seq(x) IF g(x) = y будет выглядеть?
                                                                        Нет, первое хорошо тем, что я могу написать

                                                                        Ок, тут это будет выглядеть как:
                                                                        (GROUP SUM seq(x))/(GROUP COUNT seq(x))
                                                                        И хоть это не самый оптимальный пример для lsFusion, все равно проще. И выполняться он будет одним запросом / подзапросом (платформа их сольет в один).
                                                                        Разбираться по месту. Я отвечал на вопрос, как в случае использования ФВП решать проблемы оптимизации.

                                                                        Я имел ввиду с точки зрения синтаксиса, этих абстракций в других языках в принципе не существует.
                                                                          0
                                                                          Ок, а как h(y) = GROUP SUM seq(x) IF g(x) = y будет выглядеть?

                                                                          h y seq = seq |> filter (x => g(x) == y) |> sum


                                                                          Ок, тут это будет выглядеть как:

                                                                          Угу. А теперь fold ((agg, x) => agg*x). Всегда будет операция, которую вы не предусмотрели.


                                                                          этих абстракций в других языках в принципе не существует

                                                                          Ограничения точно есть (см. design by contract). Остальное, если его нет, реализовывать как абстракцию платформы (у вас, в общем-то, тоже, не понятно, это функциональность языка или платформы).

                                                                            –1
                                                                            h y seq = seq |> filter (x => g(x) == y) |> sum

                                                                            А теперь возьмите человека «с улицы» и попробуйте ему объяснить это по сравнению с:
                                                                            h(y) = GROUP SUM seq(x) IF g(x) = y
                                                                            Вы очень сильно удивитесь.
                                                                            Угу. А теперь fold ((agg, x) => agg*x). Всегда будет операция, которую вы не предусмотрели.

                                                                            А как вы ее в SQL выполнять собрались?
                                                                            Ограничения точно есть (см. design by contract). Остальное, если его нет, реализовывать как абстракцию платформы (у вас, в общем-то, тоже, не понятно, это функциональность языка или платформы).

                                                                            Ограничения именно в виде, что заданная функция должна всегда должна возвращать NULL? Поймите в высокоуровневых абстракциях реально с языком проще. Это нормально, человек привык к языку, он его использует в каждодневной жизни в конце концов.
                                                                              0
                                                                              А теперь возьмите человека «с улицы» и попробуйте ему объяснить это по сравнению с:

                                                                              Меня не очень интересуют люди с улицы. Точнее даже совсем не интересуют.


                                                                              А как вы ее в SQL выполнять собрались?

                                                                              Во-первых, не собирался, в этом и прелесть. Во-вторых, курсором.


                                                                              Ограничения именно в виде, что заданная функция должна всегда должна возвращать NULL?

                                                                              Нет, ограничения в виде "данное условие всегда должно быть верно". Что, заметим, намного понятнее в терминах естественного языка.

                                                                                0
                                                                                Меня не очень интересуют люди с улицы. Точнее даже совсем не интересуют.

                                                                                Вам повезло, а если бы вам надо было сводить бюджет расхода с расходами и рисками, очень бы интересовало.
                                                                                Во-первых, не собирался, в этом и прелесть. Во-вторых, курсором.

                                                                                Ну то есть все данные загоняем на сервер приложений и там вычисляем итерируясь по одному? У меня для вас плохие новости. Курсор тоже самое сделает на сервере БД, и думаете это сильно лучше?
                                                                                Нет, ограничения в виде «данное условие всегда должно быть верно». Что, заметим, намного понятнее в терминах естественного языка.

                                                                                Согласен. Но это когда у вас ровно одно значение, тогда можно ограничивать что условие всегда верно. Но когда у вас есть параметры у условия, то нельзя ограничивать, что для всех существующих параметров условие должно быть верно (так как количество всех возможных параметров бесконечно). Можно только ограничивать что ни для одного из существующих параметров данное условие не будет верно. Хотя к нашему спору про Language vs Library это имеет мало отношения.
                                                                                  0
                                                                                  Вам повезло, а если бы вам надо было сводить бюджет расхода с расходами и рисками, очень бы интересовало.

                                                                                  Ну, я подозреваю, что моим нанимателям (и, опосредованно, их клиентам) это вполне себе было нужно, и они тоже людьми с улицы не интересовались никогда на моей памяти.


                                                                                  Ну то есть все данные загоняем на сервер приложений и там вычисляем итерируясь по одному?

                                                                                  Не все, а нужное поле. Не все, а батчами, которые влезают в память.


                                                                                  У меня для вас плохие новости.

                                                                                  У вас нет для меня новостей хуже, чем "этот расчет нужно выполнить". Его все равно нужно выполнить, вопрос только в том, как я его запишу.


                                                                                  нельзя ограничивать, что для всех существующих параметров условие должно быть верно (так как количество всех возможных параметров бесконечно). Можно только ограничивать что ни для одного из существующих параметров данное условие не будет верно.

                                                                                  Мне казалось, "ни для одного из существующих параметров данное условие не будет верно" превращается в "для всех существующих параметров условие будет верно" операцией отрицания.


                                                                                  Возьмем, чтобы далеко не ходить, ваш же пример:


                                                                                  CONSTRAINT hostTeam(Game team) = guestTeam(team)

                                                                                  Достаточно написать Invariant(hostTeam(Game team) != guestTeam(team)), все равно вам надо проверять "при изменении у игры команды хозяев или команды гостей [...] условие равенства этих команд".


                                                                                  Или я что-то упускаю?

                                                                                    0
                                                                                    Ну, я подозреваю, что моим нанимателям (и, опосредованно, их клиентам) это вполне себе было нужно, и они тоже людьми с улицы не интересовались никогда на моей памяти.

                                                                                    Ну значит ваши наниматели нашли «жирные» проекты, где заказчики готовы платить много и без особых претензий. Молодцы.
                                                                                    У вас нет для меня новостей хуже, чем «этот расчет нужно выполнить». Его все равно нужно выполнить, вопрос только в том, как я его запишу.

                                                                                    А если этот расчет будет выполняться тысячи раз, у вас забьется сеть, может начаться фрагментация памяти на сервере приложений, с процессорами начнут возникать вопросы и т.п. SQL по идее это может выполнить гораздо эффективнее.
                                                                                    Мне казалось, «ни для одного из существующих параметров данное условие не будет верно» превращается в «для всех существующих параметров условие будет верно» операцией отрицания.

                                                                                    Вообще говоря, не совсем. Дело в том, что NOT hostTeam(game)=guestTeam(game), это не тоже самое что hostTeam(game)!=guestTeam(game). Если у вас guestTeam(game) — NULL, то первое условие вернет TRUE так как hostTeam(game)=guestTeam(game) это NULL (все операторы сравнения возвращают NULL когда один из операндов NULL), а hostTeam(game)!=guestTeam(game) вернет NULL (по той же причине).
                                                                                    Фактически NOT hostTeam(game)=guestTeam(game) — TRUE для всех game которые не IS Game (например если в game передать Team). Тут конечно может возникнуть обманчивое впечатление, что раз задан класс параметра то NOT hostTeam(Game game) = guestTeam(game) должен проверять на то что game IS Game, но это не так. Фактически класс параметра это чисто фишка resolve'га (поиска свойства при парсинге) на сами вычисления он никак не влияет (собственно платформа кроме поиска этот класс никак не использует), то есть в lsFusion смешанная типизация — хочешь задавай класс хочешь нет (мы кстати первые лет 7 во всех проектах работали на неявной типизации, потом перешли на явную). Этот момент меня самого немного смущает, и возможно в будущих версиях мы все же сделаем что указание класса будет неявно добавлять IF game IS Game.
                                                                                      +1
                                                                                      Ну значит ваши наниматели нашли «жирные» проекты, где заказчики готовы платить много и без особых претензий.

                                                                                      Нет, я подозреваю, что мои наниматели считают, что риски от человека с улицы выше. Но, конечно, не могу уверенно сказать.


                                                                                      SQL по идее это может выполнить гораздо эффективнее.

                                                                                      Прекрасно. Напомню, что все началось с (вашего же!) вопроса "как это на SQL считать". Если может — будет посчитано на SQL.


                                                                                      все операторы сравнения возвращают NULL когда один из операндов NULL

                                                                                      Это у вас так. А в других языках совершенно не обязательно так.


                                                                                      Фактически NOT hostTeam(game)=guestTeam(game) — TRUE для всех game которые не IS Game

                                                                                      Опять же, это в вашем языке так. А в некоторых других языках это просто невозможное утверждение, потому что это условие никогда не может быть применено к объекту, который не приводим к Game.

                                                                                        0
                                                                                        Опять же, это в вашем языке так. А в некоторых других языках это просто невозможное утверждение, потому что это условие никогда не может быть применено к объекту, который не приводим к Game.

                                                                                        А вы вообще много языков знаете, где можно бегать по всем не NULL значениям функции? Это я к assertion'ам для функций с параметрами, а не просто для значений.
                                                                                        Только не надо приводить в пример приводить dbContext.table, потому что здесь уже я начну придираться, что это особенность платформы, а не языка.
                                                                                          0
                                                                                          А вы вообще много языков знаете, где можно бегать по всем не NULL значениям функции?

                                                                                          Я даже не знаю, что вы имеете в виду, что уж говорить о языках.


                                                                                          я начну придираться, что это особенность платформы, а не языка.

                                                                                          А смысл? Мы уже выяснили, что у вас это неразделимые вещи.

                                                                                            0
                                                                                            Я даже не знаю, что вы имеете в виду, что уж говорить о языках.

                                                                                            Ну допустим вы объявили f(a,b) в каком-то синтаксисе. Чтобы потом можно было сделать:
                                                                                            FOR f(a,b) DO
                                                                                            MESSAGE a + ',' + b + '->' + f(a,b);
                                                                                            А смысл? Мы уже выяснили, что у вас это неразделимые вещи.

                                                                                            Ну вы утверждали что в других языках есть похожие на ограничения абстракции. Я сказал что для этой абстракции нет аналогов в других языках, потому как для этой абстракции нужна операция итерации по всем не null значениям функции, а ее в других языках нет. И тут вы могли бы исхитриться и привести в пример dbContext.orders.Sum(). На что уже я бы возразил что это не часть языка.

                                                                                              0
                                                                                              Чтобы потом можно было сделать:
                                                                                              FOR f(a,b) DO
                                                                                              MESSAGE a + ',' + b + '->' + f(a,b);

                                                                                              Я, опять-таки, не понимаю, что это должно сделать (это, кстати, иллюстрация к понятности декларативного синтаксиса).


                                                                                              Я сказал что для этой абстракции нет аналогов в других языках, потому как для этой абстракции нужна операция итерации по всем не null значениям функции

                                                                                              Для абстракции — не нужна, в том-то и дело. Для ее конкретной реализации — возможно.

                                                                                                0
                                                                                                Я, опять-таки, не понимаю, что это должно сделать (это, кстати, иллюстрация к понятности декларативного синтаксиса).

                                                                                                Пробежать по всем не NULL значениям. Ну то есть на реальных примерах.
                                                                                                FOR selected(team) DO
                                                                                                     MESSAGE 'You selected ' + name(team);
                                                                                                FOR invoice(invoiceDetail) = invoice DO
                                                                                                     MESSAGE 'This invoice detail '+invoiceDetail+'  is in ' + invoice;
                                                                                                

                                                                                                Странно конечно, при обучении вот с чем с чем, а с пониманием итерации по всем значениям функции проблем не было. Не композиция конечно, но и не рекурсия.
                                                                                                Для абстракции — не нужна, в том-то и дело. Для ее конкретной реализации — возможно.

                                                                                                А вы много знаете абстрактных языков, то есть без единой их реализации?
                                                                                                  0
                                                                                                  Пробежать по всем не NULL значениям.

                                                                                                  Вот есть функция f(x, y) = x+y, где x, y — целые числа. Множество ее значений — все целые числа, NULL там нет. Что такое "пробежать по всем значениям"?


                                                                                                  FOR selected(team) DO
                                                                                                       MESSAGE 'You selected ' + name(team);

                                                                                                  Если selected — это функция, возвращающая множество, то вы описали тривиальный итератор, которых в языках навалом. И?..


                                                                                                  Странно конечно, при обучении вот с чем с чем, а с пониманием итерации по всем значениям функции проблем не было.

                                                                                                  Это еще одна иллюстрация к вопросу о том, можно ли объективно определить простоту обучения чему-то.


                                                                                                  А вы много знаете абстрактных языков, то есть без единой их реализации?

                                                                                                  Вы первым начали разговор об абстракциях.

                                                                                                    0
                                                                                                    Вот есть функция f(x, y) = x+y, где x, y — целые числа. Множество ее значений — все целые числа, NULL там нет. Что такое «пробежать по всем значениям»?

                                                                                                    В этом случае, платформа выдаст сообщение что количество значений бесконечно. Как и в случае скажем при итерации по sum:
                                                                                                    sum(d) = GROUP SUM sum(invoice) IF date(invoice) < d;
                                                                                                    FOR sum(d) DO // будет ошибка
                                                                                                    MESSAGE 'D: ' + d;

                                                                                                    Но при этом скажем
                                                                                                    // пробежит во всем sku для которых определена последняя дата поставки
                                                                                                    FOR sum(lastSuppliedDate(sku)) DO
                                                                                                    MESSAGE 'Sum of all invoices before last supplied date for sku : ' + name(sku) + ' is : ' + sum(lastSuppliedDate(sku));

                                                                                                    Можно кстати написать хитрее:
                                                                                                    FOR s=sum(lastSuppliedDate(sku)) DO
                                                                                                    MESSAGE 'Sum of all invoices before last supplied date for sku : ' + name(sku) + ' is : ' + s;

                                                                                                    То есть если операцию теоретически можно выполнить она выполнится.
                                                                                                    Если selected — это функция, возвращающая множество, то вы описали тривиальный итератор, которых в языках навалом. И?..

                                                                                                    Нет selected это не функция возвращающая множество это обычная функция возвращающая значение, то есть например:
                                                                                                    selected = DATA BOOLEAN (Team); // вводится
                                                                                                    // вычисляется
                                                                                                    selected (Team t) = countPlayers(t) > 5;

                                                                                                    Вы первым начали разговор об абстракциях.

                                                                                                    Ок, замените слово абстракция в этих предложениях на понятие или термин, и перечитайте заново.
                                                                                                      0
                                                                                                      В этом случае, платформа выдаст сообщение что количество значений бесконечно [...] Но при этом [...] пробежит во всем sku для которых определена последняя дата поставки

                                                                                                      Вот я про это и говорю: я не понимаю, что вообще вы имеете в виду. Вот есть запись FOR f(x) DO. Что в ней такое x? Просто идентификатор, на который потом можно ссылаться? Или что?


                                                                                                      Нет selected это не функция возвращающая множество это обычная функция возвращающая значение

                                                                                                      Тогда извините, но значений у этой функции ровно два: true и false. Так что вы явно имеете в виду итерацию не по значениям функции, а по чему-то другому.


                                                                                                      Ок, замените слово абстракция в этих предложениях на понятие или термин, и перечитайте заново.

                                                                                                      А ничего не изменится. Для понятия "ограничение" (или "инвариант", что то же самое, судя по вашим описаниям) не нужна "операция итерации".

                                                                                                        0
                                                                                                        Тогда извините, но значений у этой функции ровно два: true и false. Так что вы явно имеете в виду итерацию не по значениям функции, а по чему-то другому.

                                                                                                        Имелась ввиду итерация по всем наборам аргументов этой функции, для которых функция возвращает не NULL.
                                                                                                          0

                                                                                                          Рассмотрим функцию f(x), где x — целое число. Как это сделать? Теперь возьмем x как строку. Что-то изменилось? Теперь возьмем x как пользовательский тип. Что-то изменилось?

                                                                                                            0
                                                                                                            Это работает для ограниченных конечных множеств, что не включает в себя множества целых чисел и строк. А вот для наших пользовательских типов это работает, потому что количество объектов этих типов в нашем случае конечно.
                                                                                                              +1

                                                                                                              Эм.


                                                                                                              Пример №1: type Login: string. Простейший пользовательский тип (фактически, просто синоним), имеющий все поведение строки. Что будет, если "итерироваться по всем наборам аргументов" функции len(x), где x имеет тип Login?


                                                                                                              Пример №2: type Coord {int x, int y}. Тоже очень простой пользовательский тип. Что будет, если "итерироваться по всем наборам аргументов" функции dist(x, y), где x и y имеют тип Coord?

                                                                                                                0
                                                                                                                Второй случай в lsfusion будет выглядеть так:
                                                                                                                CLASS Coord;
                                                                                                                x(Coord c) = DATA INTEGER(Coord);
                                                                                                                y(Coord c) = DATA INTEGER(Coord);
                                                                                                                
                                                                                                                dist(Coord x, Coord y) = ...;
                                                                                                                

                                                                                                                Итерация пойдет по всем парам существующих объектов класса Coord для которых dist не NULL. То есть в нашем случае (если мы в dist просто берем евклидову норму) для тех объектов, для которых x и y установлены не в NULL.

                                                                                                                По первому примеру не совсем получится один в один, потому что у нас нельзя наследовать пользовательские классы от встроенных (в нашей терминологии строковые типы — встроенные). У нас это будет класс Login + свойство, принимающее на вход объект этого класса и возвращающее строку. Ну и итерация пойдет по всем объектам класса Login, у которых это свойство задано (в предположении, что len — это вычисляемое свойство, возвращающее длину строки, если строка не NULL).
                                                                                                                  0
                                                                                                                  Итерация пойдет по всем парам существующих объектов класса Coord

                                                                                                                  Что такое "существующие объекты"? Почему можно итерироваться по всем "существующим объектам" для координат, но нельзя итерироваться по всем "существующим объектам" для целых чисел (хотя вторых меньше чем первых)?

                                                                                                                    0
                                                                                                                    Что такое «существующие объекты»?

                                                                                                                    Созданные ранее объекты класса. В статье есть немного информации по операторам NEW/DELETE. Грубо говоря, объект в нашем случае — это запись в таблице БД (а, например, DATA-свойства — поля). Итерироваться мы будем по лежащим в базе объектам класса Coord (и их конечное число). На пустой базе при первом старте системы их будет 0, к примеру.
                                                                                                                      0
                                                                                                                      Итерироваться мы будем по лежащим в базе объектам класса Coord

                                                                                                                      То есть никакого магического "бегать по всем значениям функции", а есть простое "применить функцию ко всем объектам из некоего множества" (где множество по соглашению принимается равным множеству объектов в БД).


                                                                                                                      И тогда это все сводится к банальному map: db(Coord) |> map f (ну или Db<Coord>.Select(f), если вам трубы не нравятся). И это, да, достаточно тривиальный (по нынешним меркам) итератор.

                                                                                                                        –1
                                                                                                                        Не ко всем объектам, а только к тем объектам, для которых свойство не равно NULL. И речь не только про одиночный объект, где все довольно тривиально, а про наборы (кортежи) объектов, если у свойства больше одного параметра. Да, и объекты при этом могут лежать в разных таблицах (иерархии классов). Тут Select будет уже не такой простой.
                                                                                                                          0
                                                                                                                          Не ко всем объектам, а только к тем объектам, для которых свойство не равно NULL.

                                                                                                                          Вы не поверите:


                                                                                                                          db(Coord) |> choose f

                                                                                                                          При этом если choose нет, его легко написать:


                                                                                                                          def choose f = map f >> filter Option.isSome

                                                                                                                          Тут Select будет уже не такой простой.

                                                                                                                          Вот ровно для этого и полезна композиция функций.


                                                                                                                          Но речь-то о том, что ничего принципиально невозможного, сложного или магического нет, не более чем синтаксический сахар.

                                                                                                                            0
                                                                                                                            Я понял, что сам увел обсуждение не в ту степь. Предлагаю пока забыть то, что я писал про записи в таблицах, это физическая модель, детали реализации. В lsfusion можно проитерироваться по всем объектам любого пользовательского класса. Можно ли, например, в C# проитерироваться по всем «живым» объектам, к примеру, класса System.IO.File?
                                                                                                                              0
                                                                                                                              Можно ли, например, в C# проитерироваться по всем «живым» объектам, к примеру, класса System.IO.File?

                                                                                                                              Нет, потому что там нет концепции "живого объекта" — в общем случае она бессмысленна: например, что конкретно понимать под "живым" объектом класса System.IO.File, и что будет служить identity этого объекта?


                                                                                                                              Однако, если речь идет об экземплярах конкретного подмножества типов (а у вас сделана именно такая оговорка), это несложно реализовать.

                                                                                                                                +2
                                                                                                                                >> сам увел обсуждение не в ту степь

                                                                                                                                Ну почему же? Вы как раз наглядно показали корни вашей системы. Всё началось с активной работы с БД, далее вам захотелось «расширить и углубить (с)», ну и вы начали сочинять навороты вокруг стандартной реляционной модели, использующей под капотом довольно сложные алгоритмы для выполнения запросов и эффективного хранения данных на диске. В итоге получилась надстройка над SQL, которая в SQL же и конвертируется (как я понял). Отсюда все эти абстрактные заявления про «множество объектов» по которым что-то «пробегает». На самом же деле речь идёт о банальном выполнении селектов по таблицам, возможно in memory tables, но суть-то не меняется.

                                                                                                                                Вот начали бы вы описание языка именно с такой подачи — это простая настройка над БД, транслирующая наш псевдо-код в SQL выражения и работающая с данными из БД неявным образом. Далее же надо показать — зачем вы прячете от разработчика всё то, что получается в результате трансляции в селекты. Этим вы убиваете понимание происходящего, но что вы при этом добавляете? Мне это совершенно непонятно. Минус (огромный) вижу. Плюсы — сомнительны.
                                                                                                                                  0
                                                                                                                                  Ага, а SQL это простая настройка над машинным кодом транслирующая ваш псевдо-код в машинные инструкции. И зачем он прячет все эти машинные инструкции от разработчика, убивая понимание как это работает. Этим вы убиваете понимание происходящего ну и т.д.
                                                                                                                                    0
                                                                                                                                    Про работу SQL знают все, а про работу вашей платформы — ну человек 10 знают. Поэтому вы всё прячете, а SQL ничего не прячет. Просто сравните количество людей, сходу понимающих суть SQL и вашего языка.
                                                                                                                                      0
                                                                                                                                      Про работу SQL знают все

                                                                                                                                      Издеваетесь? Вы вообще в курсе, что там под штук 40 разных типов узлов плана? Я уверен что даже вы их не знаете. Абсолютное большинство SQLем как большим черным ящиком пользуются.
                                                                                                                                      Просто сравните количество людей, сходу понимающих суть SQL и вашего языка.

                                                                                                                                      Я сравнивал, когда пытался людям из не IT в мониторе процессов объяснять, что что это за магическое заклинание, и что такое таблицы и JOIN'ы. Они смотрели на меня как баран на новые ворота, что за херню я им втираю.
                                                                                                                                        0
                                                                                                                                        >> Вы вообще в курсе, что там под штук 40 разных типов узлов плана?

                                                                                                                                        Ну не надо так страшно пугать. Если встретились с незнакомым алгоритмом джойна — читаем доку и через пол-часика оптимизируем без проблем. Главное — общее понимание процесса.

                                                                                                                                        >> Я сравнивал, когда пытался людям из не IT…

                                                                                                                                        Ну вам всегда нужны уточнения типа «берём выборку из всё-таки хоть как-то причастных людей»?
                                                                                                            0
                                                                                                            Вот есть запись FOR f(x) DO. Что в ней такое x? Просто идентификатор, на который потом можно ссылаться? Или что?

                                                                                                            Угадали. Параметр f, на который потом можно ссылаться.
                                                                                                            Тогда извините, но значений у этой функции ровно два: true и false. Так что вы явно имеете в виду итерацию не по значениям функции, а по чему-то другому.

                                                                                                            Да ровно два true и null. И в платформе можно проитерироваться по всем значениям, для которых эта функция true. А можно просто ее вызвать.
                                                                                                            А ничего не изменится. Для понятия «ограничение» (или «инвариант», что то же самое, судя по вашим описаниям) не нужна «операция итерации».

                                                                                                            Для реализации нужна. Для определения возможно и нет. Мы же это уже выяснили вроде.
                                                                                                              0
                                                                                                              И в платформе можно проитерироваться по всем значениям, для которых эта функция true.

                                                                                                              Нельзя, если xint. Ну, если верить вашим словам.


                                                                                                              Но самое важное уже обсудили выше: это ничем не отличается от применения функции к множеству, просто у вас это множество имплицитно и предоставляется платформой.


                                                                                                              Для реализации нужна.

                                                                                                              Для вашей реализации. Для других — не обязательна.

                                                                                                                0
                                                                                                                Нельзя, если x — int. Ну, если верить вашим словам.

                                                                                                                Можно, там человек не совсем правильно выразился. На самом деле нужно чтобы множество было конечно.
                                                                                                                То есть например вот такая штука работает:
                                                                                                                iterate(i,from,to) = RECURSION i=from STEP i=$i+1 AND i<=to;
                                                                                                                FOR iterate(i, 1, 5) DO
                                                                                                                MESSAGE i;

                                                                                                                Хотя здесь i — int;
                                                                                                                В принципе я думаю в ближайших версиях сделаем, чтобы и эта штука работала:
                                                                                                                FOR i >= 1 AND i <= 5 DO
                                                                                                                MESSAGE i;

                                                                                                                Для вашей реализации. Для других — не обязательна.

                                                                                                                Я честно не знаю как это реализовывать без такой операции. Даже теоретически не представляю. Как только увижу такую реализацию, сразу признаю что не прав. Готов съесть то, что скажете.
                                                                                                                Но самое важное уже обсудили выше: это ничем не отличается от применения функции к множеству, просто у вас это множество имплицитно и предоставляется платформой.

                                                                                                                Ценю вашу тягу к формализмам, но жизнь на этом рынке заставила меня стать практиком и использовать более простые понятия. Быть ближе к людям так сказать. :)
                                                                                                                  0
                                                                                                                  Можно, там человек не совсем правильно выразился.

                                                                                                                  "В этом случае, платформа выдаст сообщение что количество значений бесконечно". Помните, кто это сказал?


                                                                                                                  Я честно не знаю как это реализовывать без такой операции. Даже теоретически не представляю.

                                                                                                                  Ну вы на Code Contracts посмотрите, например. Или, хотя бы, на CONSTRAINT в обычной РСУБД.


                                                                                                                  жизнь на этом рынке заставила меня стать практиком и использовать более простые понятия

                                                                                                                  Дадада, "бегать по всем значениям функции" — это намного более простое понятие, чем "вычислить значение функции для всех элементов множества". А главное, можно делать вид, что это уникальная возможность.

                                                                                                                    –2
                                                                                                                    «В этом случае, платформа выдаст сообщение что количество значений бесконечно». Помните, кто это сказал?

                                                                                                                    Да, но это не привязано к тому примитивный или объектный параметр. Точнее так с примитивным параметром количество значений может быть как конечным, так и бесконечным. С объектным кстати тоже NOT hostTeam(game) — TRUE на бесконечном множестве значений и FOR по нему выдаст ту же ошибку.
                                                                                                                    Ну вы на Code Contracts посмотрите, например. Или, хотя бы, на CONSTRAINT в обычной РСУБД.

                                                                                                                    Они только на первичные данные (поля) работают. CONSTRAINT на View нельзя повесить, как и Validation на любой метод.
                                                                                                                    Дадада, «бегать по всем значениям функции» — это намного более простое понятие, чем «вычислить значение функции для всех элементов множества». А главное, можно делать вид, что это уникальная возможность.

                                                                                                                    Ок, вычисление множества всех не NULL значений любой функции — уникальная возможность?
                                                                                                                      0
                                                                                                                      Они только на первичные данные (поля) работают.

                                                                                                                      Ну да. И что?


                                                                                                                      Ок, вычисление множества всех не NULL значений любой функции — уникальная возможность?

                                                                                                                      Смотря как оно сделано. Если перебором значений параметров, то это весьма тривиально. Если анализом кода — то интересно, но я все равно не думаю, что уникально.


                                                                                                                      А у вас как?

                                                                                                                        –1
                                                                                                                        Ну да. И что?

                                                                                                                        То что они работают в одном небольшом частном случае вас не смущает.
                                                                                                                        Смотря как оно сделано. Если перебором значений параметров, то это весьма тривиально. Если анализом кода — то интересно, но я все равно не думаю, что уникально.

                                                                                                                        Конечно не перебором, издеваетесь что ли? Можно считать, что да, анализом кода, хотя архитектурно там все конечно сложнее. И учитывая насколько это тяжело реализовать, я практически на сто процентов уверен, что уникально.
                                                                                                                          0
                                                                                                                          То что они работают в одном небольшом частном случае вас не смущает.

                                                                                                                          Нет, не смущает. Code Contracts работают в большем числе случаев.


                                                                                                                          Конечно не перебором, издеваетесь что ли?

                                                                                                                          Да нет, не издеваюсь.


                                                                                                                          Вот у вас есть функция f(x) = md5(x). Как именно вы определите множество ее значений?

                                                                                                                            –2
                                                                                                                            Вот у вас есть функция f(x) = md5(x). Как именно вы определите множество ее значений?

                                                                                                                            Дискуссия идёт к вопросу о майнинге криптовалюты, но мы не про такие вычисления)
                                                                                                                              0

                                                                                                                              Ну то есть все-таки нет "вычисления множества всех не NULL значений любой функции".

                                                                                                                                –2
                                                                                                                                Ну если запрограммируете это дело, то есть шанс завалить Золотого Тельца, мы не брались
                                                                                                                                  0

                                                                                                                                  Так все-таки, есть в lsFusion "вычисление множества всех не NULL значений любой функции", или нет?

                                                                                                                                    0
                                                                                                                                    Так все-таки, есть в lsFusion «вычисление множества всех не NULL значений любой функции», или нет?

                                                                                                                                    Я напомню, что ERP работает со значениями, существующими базе данных.
                                                                                                                                    Да, для любой дискретной функции в области её допустимых значений.
                                                                                                                                    Это по моему опыту как я делал на практике, не являясь теоретиком данного подхода.
                                                                                                                                      0
                                                                                                                                      Да, для любой дискретной функции в области её допустимых значений.

                                                                                                                                      Функция md5(x) — дискретная?

                                                                                                                                        0
                                                                                                                                        Функция md5(x) — дискретная?

                                                                                                                                        В области определения, например, файлы в директории сервера, ее область допустимых значений безусловно дискретная.
                                                                                                                                          0

                                                                                                                                          Прекрасно. Из этого вытекает, что в lsFusion есть «вычисление множества всех не NULL значений» ее значений. Правильно?