Я думал о некоторых концепциях, лежащих в основе нового языка.Сначала это было что-то вроде игрушки, но теперь мне интересно, может ли это что-то значить.Я отправляю этот вопрос в Stack Overflow, чтобы посмотреть, было ли это сделано раньше, и могу ли я получить какие-либо отзывы, идеи или другую информацию.
Я начал думать об этом в основном после прочтения Джонатана Эдвардапрезентация по декларативному программированию .Затем я смешал это с некоторыми из моих старых идей и тем, что видел в современных языках.
Основная идея декларативного программирования - «что» против «как».Тем не менее, я слышал это очень много раз, так что, похоже, оно почти всегда похоже на слово «интересно», когда оно на самом деле ничего вам не говорит, что расстраивает.
В версии Джонатана Эдварда, хотяСначала он подчеркнул ленивую оценку 1010 *.Это имеет некоторые интересные последствия, а именно функционально-реактивное программирование (FRP) .Вот пример FRP с анимацией (с использованием синтаксиса, который я составил):
x as time * 2 // time is some value representing the current time
y as x + (2 * 500)
new Point(x, y)
Так что здесь значения просто автоматически изменяются при изменении входных данных.На одном из моих любимых языков, D , было различие между «чистыми» и «нечистыми» функциями.Чистая функция - это функция, которая не имеет связи с внешним миром и использует только другие чистые функции.В противном случае это было бы нечистым.Дело в том, что вы всегда можете доверять чистой функции, возвращающей одно и то же значение для заданных аргументов.
Полагаю, здесь применяется аналогичный транзитивный принцип.Наша примесь time
.Все, к чему прикоснулся time
, будучи x
, таким образом y
и, следовательно, new Point(x, y)
, нечисты.Тем не менее, уведомление (2 * 500)
чисто.Итак, вы видите, что это говорит компилятору, где находятся его ограничения.Я думаю об этом, как об упрощении математического выражения с помощью переменных:
(x ^ 2) + 3x + 5
(4 ^ 2) + 3x + 5 = 16 + 3x + 5 = 21 + 3x = 3(7 + x)
Говоря компилятору, что чисто, а что нет, мы можем значительно упростить наши программы.Другим моментом являются нетерпеливые или изменчивые данные.Джонатан Эдвард распознал ввод как изменчивый и нетерпеливый, но вывод как функциональный и ленивый.По сути, с учетом нового ввода программа определила изменение атомного состояния , а затем вывод будет просто функцией текущего состояния .Если вы хотите понять, почему это может быть важно, посмотрите презентацию.Ввод нечист.Ленивая оценка помогает определить изменение состояния атома.Давайте посмотрим, как программа будет написана процедурно:
void main ()
{
String input = "";
writeln("Hello, world!");
writeln("What's your name? ");
input = readln();
writeln("Hello, %s!", input);
writeln("What's your friends name? ");
input = readln();
writeln("Hello to you too, %s!", input);
}
Здесь ключевое слово bind
говорит о том, что следующий код выполняется при изменении begin
.Ключевое слово mutable
говорит, что ввод не ленивый, а нетерпеливый.Теперь давайте посмотрим, как его может представлять «изменение атомарного состояния».
program:
mutable step := 0
bind begin:
writeln("Hello, world!")
writeln("What's your name? ")
++step
bind readln() as input when step = 1:
writeln("Hello, %s!", input)
writeln("What's your friends name? ")
++step
bind readln() as input when step = 2:
writeln("Hello to you too, %s!", input)
Теперь мы видим кое-что, что можно сделать более простым и читаемым для программиста.Прежде всего, это уродливая переменная step
и то, как мы должны увеличивать и проверять ее каждый раз.Вот пример того, как может выглядеть новая и улучшенная версия:
program:
bind begin:
writeln("Hello, world!")
writeln("What's your name? ")
bind readln() as input:
writeln("Hello, %s!", input)
writeln("What's your friends name? ")
yield // This just means the program jumps to here instead of at the beginning
writeln("Hello to you too, %s!", input)
halt
Это лучше.Не идеально, хотя.Но если бы я знал идеальный ответ, меня бы здесь не было, верно?
Вот лучший пример с использованием игрового движка:
class VideoManager:
bind begin: // Basically a static constructor, will only be called once and at the beginning
// Some video set up stuff
bind end: // Basically a static destructor
// Some video shut down stuff
class Input:
quitEvent as handle // A handle is an empty value, but can be updated so code that's bound to it changes.
keyboardEvent as handle(KeyboardEvent) // This handle does return a value though
mouseEvent as handle(MouseEvent)
// Some other code manages actually updating the handles.
class Sprite:
mutable x := 0
mutable y := 0
bind this.videoManager.updateFrame:
// Draw this sprite
class FieldState:
input as new Input
player as new Sprite
bind input.quitEvent:
halt
bind input.keyboardEvent as e:
if e.type = LEFT:
this.player.x -= 2
else if e.type = RIGHT:
this.player.x += 2
else if e.type = UP:
this.player.y -= 2
else if e.type = DOWN:
this.player.y += 2
Мне нравится, что для этого не требуетсяобратные вызовы, события или даже циклы или что-то, и потоки очевидны.Проще сказать, что происходит, и это не просто Python-подобный синтаксис.Я думаю, что это похоже на то, когда разработчики языка поняли, что было только несколько вещей, для которых люди использовали ярлыки и переходы: условные ветви и циклы.Таким образом, они встроили if-then-else, в то время как и для языков, ярлыки и goto стали устаревшими, и компиляторы, а также люди могли сказать, что происходит.Большая часть того, что мы используем, происходит из этого процесса.
Возвращаясь к темам, приятно то, что потоки более гибкие.Если компилятор свободен делать то, что он хочет, потому что мы приблизились к тому, чтобы сказать, что мы хотим, а не как мы хотим, чтобы это было сделано.Таким образом, компилятор может использовать преимущества многоядерных и распределенных процессоров, но при этом все равно компенсирует платформы без хорошей поддержки многопоточности.
Есть еще одна вещь, которую я хотел бы упомянуть.И это мой взгляд на шаблоны.Это было своего рода концептуальное яйцо, которое начало развиваться, когда я начал программировать (на самом деле около 2 лет назад), а затем начал открываться.По сути, это был принцип абстракции, но он простирался дальше, чем классы и объекты.
Это было связано с тем, как я воспринимал функцию.Например:
int add (int a, int b)
{
return a + b;
}
Хорошо, add
вернул int
, но что было это?Это было похоже на int
ожидание, чтобы произойти.Как головоломка без нескольких кусочков.Возможности были ограничены, и подходили только определенные части, но когда вы закончили, у вас был готовый продукт, который вы могли бы затем использовать в другом месте.Это, как я уже сказал, принцип абстракции.Вот несколько примеров того, что я считаю абстракцией + отсутствующими частями -> конкретными отношениями:
- функция + аргументы -> значение
- абстрактный класс + методы -> класс
- класс + значения экземпляра -> объект
- шаблон + аргументы -> функция или класс
- программа + ввод + состояние -> вывод
Они всетесно связаны.Кажется, этим можно воспользоваться.Но как?Опять же, вот почему это вопрос.Но ленивая оценка здесь интересна, поскольку вы можете передать что-то , а его части по-прежнему отсутствуют , во что-то другое.Для компилятора это в основном вопрос разыменования имен вплоть до примесей.Как мой пример, приведенный выше:
(x ^ 2) + 3x + 5
(4 ^ 2) + 3x + 5 = 16 + 3x + 5 = 21 + 3x = 3(7 + x)
Чем больше кусков вы дадите компилятору, тем больше он сможет завершить его и превратить программу в основное ядро.И функция add
, описанная выше, будет автоматически разрешена во время компиляции, поскольку она не зависит от внешних ресурсов.Можно решить даже множество классов и объектов, а также огромные порции программ, в зависимости от того, насколько умен умный компилятор.
На этом пока все.Если вы видели примеры того, что уже сделано, я хотел бы увидеть.И если у вас есть какие-либо идеи, инновации, ресурсы или отзывы, я бы это тоже оценил.