Alexander Kuklev (akuklev) wrote,
Alexander Kuklev
akuklev

Category:

Снова о моём языке программирования.

О Dyi.



-- Перевод привычных конструкций С/С++ на язык Dyi:

- Блок if:

C:
if (a == 5) {
    printf("success!");
}

Dyi:
if(a! = 5){
    print: "success!" !
} !


Для простоты можно считать, что восклицательный знак просто выполняет роль разделителя, как семиколон в С++. На самом деле это не совсем так.

Восклицательный знак ставится после переменной и означает, что нужно взять текущее значение переменной. Восклицательный знак ставится после действия, указывая, что его нужно выполнить.
После действия восклицательный знак ставится С_ПРОБЕЛОМ.
После переменной восклицательный знак ставится БЕЗ_ПРОБЕЛА.

Пробелов между if и открывающей скобкой условия и между закрывающей скобкой условия и открывающей скобкой блока инструкций НЕ_ДОПУСКАЕТСЯ.

В Dyi для присвоения используется оператор "<-", а для сравнения "=".
В С для этого использовались "=" и "==" соответственно.

Блок if тоже является действием и завершается восклицательным знаком с пробелом.

- Блок if-else:

C:
if (a == 5) {
    printf("success!");
} else {
    printf("fail!");
}

Dyi:
ifelse(a! = 5){
    print: "success!" !
}{
    print: "fail!" !
} !

Пробелов между скобками не допускается.

- Блок while:

C:
while(a > 5){
    a--;
    printf("a is %i", a);
}

Dyi:

while{a! > 5}{
    decrement: a !
    print: "a is |a!|" !
} !

Пробелов между скобками не допускается. Блок условия заключён не в круглые, а в квадратные скобки, т.к. является именно блоком - он эвалюируется не один раз, а на каждом выполнении цикла.

Внутри строк допускается вставка значения. Для эскейпинга используются символы "|".

Каждое действие при выполнении возвращает отклик. Если отклик .done - выполняется следующее действие. Любой иной отклик называется исключением и, если он возвращается, дальнейшие инструкции не выполняются. Весь блок целиком разрешается в исключение.

while умеет обрабатывать исключения .break и .continue. Если блок разрешается в .break, while прекращает своё выполнение, возвращая .done. Если .continue, проверяет условие и начинает следующую фазу цикла.
Остальные исключения while пропускает насквозь.

- Блок do-while:

С:
do {
    a--;
    printf("a is %i", a);
}while(a > 5)

Dyi:

do-while-true{
    decrement: a !
    print: "a is |a!|" !
    a! > 5
} !

Блок, разумеется, разрешается в последнюю инструкцию и, если её результат - .true, блок повторно выполняется.

Блок обрабатывает исключения .break, .continue не обрабатывается.

- Обработка исключений:

С:
try{
    code
}catch(exeptiontype exp){
    обработчик
}

Dyi:

{
    code
}! @ (
    .exeption1 => {обработчик} |
    .exeption2 => {обработчик} |
    .exeption-in-line: lnnum => {print: "случилось исключение в строке |lnnum|" !} |
    *
) !

Если обработаны все исключения, последние "| *" не нужны. Этот * означает, что остальные исключения будут пропускаться насквозь.

Если нужно включить универсальный обработчик, делается это так:

{
    code
}! @ (
    .exeption1 => {обработчик} |
    .exeption2 => {обработчик} |
    .exeption-in-line: lnnum => {print: "случилось исключение в строке |lnnum|" !} |
    * => {print: "какое-то исключение случилось" !}
) !

Если нужно их просто поглощать без слов:

{
    code
}! @ {
    .exeption1 => обработчик |
    .exeption2 => обработчик |
    .exeption-in-line: lnnum => print: "случилось исключение в строке |lnnum|" ! |
    * => {.done}
} !


Исключения можно обрабатывать и в обработчике исключений тоже. Вложеность не ограничена.

- Ловля исключений:

Когда обработчик не нужен, а нужна просто ликвидация исключения, это делается так:

catch(.exp){
code
}

Это применяется для создания continue и break инструкций в Java-стиле - до метки:

break в Java-стиле. Т.е. до метки:

catch(.break-a1){
    while{our-variable! > 5}{
         # если где-то тут в коде появится исключение ".break-a1", оно добаблится до catch. #
    }
}

continue в Java-стиле. Т.е. до метки:

while{our-variable! > 5}{
    catch(.break-a1){
        # если где-то тут в коде появится исключение ".break-a1", оно добаблится до catch #
    }
}


- Определение переменных и сокрытие.

Переменные в С определялись так:
int a;

Или, так:
int a = 5;

В Dyi это делается так:

[a := new: Integer !]

Или так:

[a := new: Integer 5 !]

А можно и не указывая тип (хотя, это затрудняет оптимизацию кода):

[a := new !]

Определений переменных через запятую нету.
Область видимости переменной - блок инструкций, в котором она определена и вложеные блоки. Во вложеных блоках переменная может перекрываться новой переменной с таким же именем. Если такое перекрытие случится, к внешней переменной можно получить доступ, как к "^a".

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

При помощи блоков можно выполнять сокрытие:

{
    code1
    {
         [a := new: Integer 1 !]
         code2
    }!
    code1
}

В code2 переменной a видно не будет.

Создаются переменные всегда в куче. Даже челочисленые.
Удаляются из кучи либо сразу, как только на них не ссылается ни одна ссылка,
либо позже в idle-time сборщиком мусора. Это зависит от стратегии поведения VM.

Блок repeat:

repeat(5){
    code
}

Выполняет код пять раз последовательно. Ловит .break и .continue.

- Блок for:

Вот тут проявляется ключевое отличие.
В Dyi блок for предназначен для итерации по элементам последовательности.

Например, следующий код является валидным:

for(files)( file`{
    delete: file !
}) !

Тут file - переменная итерирования, а files - список, по которому итерируют.

Однако, можно применять for и для итерирования по числам:

for[1..10]( i`{
    print: i !
}) !

Напечатает:
1
2
3
4
..
10

Можно также использовать итерацию с шагом:

for[0, 2..10]( i`{
    print: i !
}) !

Напечатает:

0
2
4
6
8
10

for применяет блок к элементам списка последовательно. Ловит .break и .continue.


Для параллельного выполнения используется блок ":all:":

(delete:) :all: files

(Или, эквивалентный код:

(file ` {delete file !}) :all: files

)

Кроме описаного имеется блок ":times:"

Это полный аналог repeat за тем исключением, что выполнение параллельно.

5 :times: {print: "Hi!" !} !



-- Именование действий.
Действия именуют, начиная глаголом со строчной буквы. Скажем:
    print: a
    decrement: a
    add: a :to: b

Аргументы окружаются двоеточиями. Пробелы заменяются на дефисы. Двоеточия не могут отделяться пробелами от частей названия.

А вот пример определения такого действия:

decrement: a := {
    a <- a! - 1 !
}

-- Именование переменных и сущностей:
Переменные и сущности именуют существительным со строчной буквы, либо одной буквой, опционально заглавной.

Примеры:

a
B
circle

Читать следует, как будто они снабжены определённым артиклем.

Сущности или переменные, списки или множества часто именуют во множественном числе.


-- Настоящее значение блоков встроеных определений "[ * := * ]" :

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

print-values: a, b, c := {
    print: str & "|a|" !
    print: str & "|b|" !
    print: str & "|c|" !
    [
        str := "value of it is: ".
    ]
}


Читать такое следует, как:
    напечатать стр конкат а, напечатать стр конкат б, напечатать стр конкат це, где стр равно 'value of it is'.


-- Действия, возвращающие данные.

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

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

Примером может послужить действие "создать новый файл",
возвращающее два типа отклика:

.файл: ф :создан
.невозможно-создать-файл

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

Это будет выглядеть так:

create-new-file ! @ (
    .file: f :created => {write-something-in: f !} |
    .cannot-create-file => {print: "ошибка файловой системы" !}
) !

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

create-new-file ! @ (
    .file: f :created => {write-something-in: f !} |
    *
) !

-- Предостережение. Действия, то есть, штуки, название которых начинается с глагола ВСЕГДА должны возвращать "отклик". То есть, некую штуку, начинающуюся с точки и, возможно, несущую какую-то информацию. Имеется три стандартных отклика: .done, .break и .continue.

Если действие может возвращать .done, очень рекомендуется, чтобы другие виды откликов она возвращала только в том случае, если действие не совершено. Такие случаи называются исключениями.

Если действие при успешном выполнении хочет передать вызывающему некую информацию, она выдаёт особый отклик типа ".создано: то-то". Первый глагол отклика, как правило, соответствует или синонимичен глаголу действия. Если просили создать, ответ, сообщающий об успехе говорит ".создано".

В таком случае, отклик ".done" не используется.

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

Согласно вышеописаному принципу, get-value должно возвращать что-то типа: ".value: v :retrieven"

Правильным именованием тут является просто "value". Существительное.

Вторым примером той-же ошибки является неправильное понимание действия

add: Number :to: Variable

Это действие не складывает значения, а изменяет значение переменной во втором операнде. И ресолвится это действие не в значение суммы, а в .done.

Если вы хотите получить сумму, используйте оператор "+":

5 + a! найдёт сумму пяти и текущего значения переменной а.

Правильным именованием для функции, возвращающей значение суммы было бы:
Number :increasen-by: Number

Начинается оно "виртуально" на существительное "Number". Причастие - increasen-by указывает на то, что с этим числом было сделано.


Первую часть описания пока закончу.

Краткое резюме:
Dyi применяет синтаксис императивных блоков, очень похожую на семантику С и Java, включает в себя полнофункциональные механизмы обработки исключений и параллельных вычислений. При этому, он умещает в рамки семантики C литературный код, который удобно читать. Кроме того, в Dyi используются в привычной и очень удобной форме математические операторы.

Синтаксис Ди представялет из себя мощную систему, сумевшие вобрать в себя самые удобные элементы из C, Smalltalk, Haskell и Prolog. И, главное - языка математики.

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

Следующим номером - описание ООП на Dyi. А сейчас- спать.



Upd: исправлены мелкие ошибки.
Subscribe

  • Прогресс

    Десять дней назад, вторая ступень SpaceX'овского корабля Starship своим ходом слетала своим ходом на десять километров вверх, и усмепшно приземлилась…

  • О водосбережении

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

  • 36

    Традиционный деньрожденный пост. Год выдался необычный. :)

  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 12 comments

  • Прогресс

    Десять дней назад, вторая ступень SpaceX'овского корабля Starship своим ходом слетала своим ходом на десять километров вверх, и усмепшно приземлилась…

  • О водосбережении

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

  • 36

    Традиционный деньрожденный пост. Год выдался необычный. :)