Сейчас о путешествиях во времени пишут не только фантасты. После размышлений античных философов, формул общей теории относительности, моделей червоточин продолжают появляться новые теории, и даже проекты. Многие из них, правда, требуют для своей работы черные дыры, бесконечно длинные цилиндры, материю с отрицательной массой и прочие артефакты. Приближает ли все это нас к созданию машины времени? Об этом трудно говорить предметно, не понимая сути вопроса – что такое время. За несколько веков это понимание увеличилось, на самом деле, незначительно. Быть может с приходом программирования ситуация изменится? Ведь именно там нас ожидают многие ответы.
Забегая вперед
Вскоре мы убедимся в том, что течение времени и выполнение компьютерной программы – это практически одно и то же. Такая аналогия позволяет иллюстрировать рассказ понятными примерами, и дает много интересных открытий.
Следующая статья цикла: «Путешествия во времени и программирование 2: парадоксы»
0. Время и пространство
Для начала нам нужно в первом приближении определиться с тем, что такое время. Пока без программирования – потерпите немного. Под термином «время» обычно понимают видимый ход событий, процесс непрерывного изменения окружающего мира. При этом упускается важнейшая и очевидная особенность этих изменений. Что происходило во Вселенной от момента большого взрыва до наших дней? Из точки сингулярности поэтапно (вначале элементарные частицы, потом атомы водорода, …) образовалась сложнейшая многоуровневая система, состоящая из галактик, звезд, планет, как минимум на одной из которых находятся разумные существа, читающие хабр с экрана компьютера. Эти колоссальные изменения были бы просто невозможны без неких управляющих механизмов (встроенных в саму реальность). Мир не просто меняется – он поэтапно меняется от простого к сложному. Поэтому,
Время – это фундаментальный механизм развития, управляющий ходом событий.
Сравним Вселенную с детской игрушкой — калейдоскопом. Там мелькают неповторимые картинки – вроде бы тоже постоянное изменение. Причинная связь тоже есть – текущее положение цветных стеклышек (образующих картинку) зависит от предыдущего. В чем же разница? Говоря простым языком – отсутствие развития (обучаемости). Можно крутить калейдоскоп хоть миллион лет – ничего качественно нового в нем не появится. В природе так не бывает: эволюционирует космос, биосфера, развивается наука, общество, любое предприятие, коллектив, человек – да вообще все, любая система. Если бы ход времени был бы «просто движением», то мир оставался бы в состоянии хаоса, в нем не происходило бы никаких качественных изменений.
Если время – это то, благодаря чему происходят целенаправленные изменения, то что есть пространство? Объект этих изменений. Иными словами, пространство – это структура, время – это динамика. Понимаю, что с пространством больше ассоциируется некая «пустота, где все находится» (и это «все» называют «материя»). Но есть и геометрический подход к описанию природы материи, где любые частицы и излучения – это неоднородности пространства. Как рябь на поверхности озера.
Представление о времени, как о физическом поле, участвующем во всех процессах в качестве структурирующего начала можно встретить в теории «причинной механики». Ее автор – советский астроном Н.А. Козырев, и о его героической работе, удивительных экспериментах — нужно писать отдельную статью.
1. Отправная точка
У нас есть с одной стороны время и пространство, с другой — данные и код. Логично соотнести их следующим образом:
Пространство – это данные, время – это код.
Как время меняет пространство, так код меняет данные. Мы уже успели разобраться с фундаментальным свойством времени: оно не просто меняет, а целенаправленно структурирует пространство, снижая его энтропию (хаос). Код, вносящий в данные хаос безусловно встречается (всякие рэндомизаторы), но в большинстве своем, он обрабатывает данные с целью их упорядочивания, или получения новых данных – в общем, на выходе что-то качественно новое. Самый простой пример: сортировка массива. Код описывает динамику приведения структуры массива к более упорядоченному виду. В другом примере, входными данными является облако газа и пыли, за 4.6 млрд. лет некий код преобразовал его в нашу солнечную систему (понятно, что код несколько сложнее алгоритма «пузырька»).
Неотделимость времени и пространства также легко проиллюстрировать.
x; // что x? Нет действия
++; // что инкрементировать? Нет данных
x++; // пример минимальной программы
Попробуйте написать самую короткую (не пустую) программу: в ней обязательно будет некое действие, над некими данными (т.е. «атомом» программы является связка «данные + действие»).
Итак, в реальном мире пространство без времени существовать не может. Как сказал герой рассказа «Машина времени»: «попробуйте представить куб, не существующий ни единого мгновения!» Это важный вывод: пространство (данные) существует только в процессе хода времени (выполнения кода).
2. Базовые понятия
Атом: единицей кода будем считать команду (содержащую действие над данными). Единицей потока времени является событие. Что делает команда? Меняет данные. Что делает событие? Меняет пространство. Команды складываются в код, события складываются в цепочку.
Причинность: команды логически связаны, и кроме того, что выполняются последовательно, результат одной часто является входными данными для другой. Также у события, есть входные и выходные данные (структура пространства до и после).
Мировая линия: в физике это последовательность существования некого объекта. Ей соответствует поток выполнения кода, т.е. код+данные.
Итак, еще раз, проведем аналогию между цепочкой команд и нитью событий. Событие – это «действие над данными», т.е. изменение пространства под управлением времени. Входными данными для него является предшествующее состояние пространства (которое, как мы помним, и есть «данные»), на выходе мы получаем опять-таки измененное пространство.
3. Фрактальность
В виде диаграммы действий можно изобразить мировую линию любого объекта. Например, комната, где сидит человек:
Очевидно, что внутри этого потока параллельно протекает множество событийных цепочек: человек сидит за компьютером, кот спит на стуле, на столе остывает чай и т.п. В каждом из этих потоков точно также есть свои вложенные потоки, можно это проследить вплоть до движения молекул и т.д. Принцип матрешки, или фрактальность, которая применима как к структуре, так и к динамике (и к ним обоим одновременно). Поэтому Вселенная «изначально многопоточна», т.к. содержит бесконечное количество параллельно происходящих событий. И все эти события естественным образом представимы в виде иерархического дерева.
Полная аналогия с программами, большинство из которых не сводятся к линейной последовательности команд и, как правило, состоит из различных структурных единиц, вложенных друг в друга. Функция состоит из вызовов других функций, и каждая из них описывает свой «масштаб деятельности». Алгоритм принятия решения оперирует фактами, вложенные в него вызовы функций вычисляют факты на основе данных, вызывая для этого математические функции… и так далее.
Раз уж мы затронули многопоточность, у вас может возникнуть вопрос про синхронизацию потоков, уровни изоляции транзакций. Рассмотрим на примере «двух писателей». Есть два потока, одна переменная, оба потока пытаются параллельно записать в нее число. Необходимо поставить lock на запись, т.е. запишет сначала первый, только потом второй (ему придется подождать). Да, второй при этом затрет результаты первого. Понятно, что в силу технических особенностей здесь иначе никак… но нет ли у вас ощущения, что реальный мир работает иначе?
Вместо переменной – у нас теперь некоторая область пространства. Два потока, записывающие числа – это попадание туда двух лучей света. Что произойдет? Упрощенно говоря, их энергия суммируется. Нет никакой потоковой синхронизации, работает принцип суперпозиции. Быть может, когда-нибудь появятся компьютеры, в которых при одновременной записи в переменную чисел 1 и 2 результатом будет 3.
4. Машина времени
У времени есть свойства:
1. Принцип причинности – события следуют в строгом порядке, при котором причина предшествует следствию.
2. Принцип альтернативности – у любого события есть несколько вариантов исхода.
3. Принцип цикличности – события повторяются.
С первым, думаю, понятно – без причинной связи «сегодня» никак бы не зависело от «вчера», и последовательность изменений, приводящих к некому результату, была бы невозможна (представьте код, где следующая команда выбирается случайным образом).
Благодаря второму мы живем в мире относительной свободы выбора, а не тотальной предопределенности событий.
О третьем упоминают реже, хотя цикличность времени мы наблюдаем постоянно, абсолютно во всех процессах: удары сердца, дыхание, смена дня и ночи, времена года, подъемы и спады производства, экономические колебания, круговорот воды в природе, и многое другое. Цикличность следует из самой идеи времени: если время – это алгоритм, значит, у него должны быть этапы. И они будут постоянно повторяться. В первом приближении, речь идет о двух стадиях (анализ-синтез, спад-рост, и т.п.), которые мы, опять же, видим во всех процессах (т.к. это универсальный закон).
Уже догадались, да? Приведенные свойства в точности соответствуют главному положению структурного программирования: любой исполняемый алгоритм может быть представлен совокупностью 3-х управляющих структур: последовательность, ветвление, цикл. Это утверждение доказано теоремой Бема-Якопини (иногда ошибочно приписываемая Дейкстре). Будем считать эти три конструкции базовым набором.
Как известно, Дейкстра был ярым противником оператора goto (безусловный переход), и ратовал за использование только базового набора. Однако этот оператор неявно присутствует в коде.
Во-первых, вызовы функций: в стек кладется адрес точки вызова, и происходит переход в подпрограмму (другой кусок кода).
Во-вторых, цикл – это комбинация условного оператора и оператора условного перехода. Да, речь идет о разных уровнях: код на языке Си не будет содержать переходов, они появятся только на уровне машинных инструкций, в которые программа будет скомпилирована. Можно предположить, что подобным образом устроено и само время: кроме базового набора, существует инструкция перехода. Без переходов (хоть и неявных) программа была бы примитивна: просто последовательность команд, без циклов, без возможности выделять код в функции и вызывать их. Аналогичным образом, оператор перехода – необходимая составляющая механизма времени. Если продолжать аналогию, то без него в природных процессах отсутствовала бы цикличность, и возможность многоуровневой (модульной) организации управления процессами. В общем, ничего бы не работало. Путешествия во времени – важный механизм реальности.
Посмотрим, как выглядит перемещение:
На схеме видно, что перемещение происходит не в ту же точку, а со сдвигом по вертикальной оси (пространство), ведь это линии не кода (времени), а пространства+времени (данных+кода).
Стоит отметить, что кроме скачков во времени, существует понятие изменения скорости течения времени. Например, мир вокруг «застыл», и человек может разглядеть крылья летящей пчелы, обогнать звук сказанной им же фразы и т.п. Здесь на помощь приходит опять же причинная механика Козырева. Приток времени в систему делает ее более упорядоченной, т.е. плотность времени (и скорость процессов) выше там, где более упорядоченная структура. Скорость течения времени – это степень упорядоченности данных. Где проще найти книгу – на аккуратной полочке или в сваленной куче? Кто быстрее найдет решение – тот, чьи знания приведены в порядок, или тот, у кого в голове каша? Возвращаясь к примеру с массивом. Пусть массивов теперь будет два: хаотичный и отсортированный. Стоит задача: найти в массиве заданное число. Очевидно, что во втором массиве это будет быстрее (код поиска, понятно, разный). Условно говоря, второй массив более «развит», чем первый, и все процессы происходят в нем быстрее.
5. Потоки времени
Предположим, что пространство-время прекрасно описывается концепцией ООП. Это, среди прочего, будет означать то, что данные могут существовать не только в виде параметров, получаемых и возвращаемых функциями, но и просто храниться в полях объектов. Мы помним, что пространство и время не существуют по отдельности, поэтому пространство (данные) существует только в процессе хода времени (выполнения кода). А это парадигма функционального программирования.
Изобразим последовательность событий (как данные, меняющиеся при прохождении через некоторый код):
int x = 0;
x = x++;
x = x * x;
Код времени – это не классы, не переменные, а множество функций, описывающих преобразования данных (т.е. структуры пространства). Что было изначальными данными – сложно сказать. Когда процесс был запущен, образовалось бесчисленное множество потоков выполнения кода Времени, и видимое нами пространство – это просто срез данных, передаваемых от одной функции к другой.
Увы, я совсем не знаток ФП, но все же попробую изобразить приведенный выше пример без использования переменных:
int f1() { return 0; }
int f1(int x) { return x++; }
int f3(int x) { return x*x; }
Запуск цепочки: f3(f2(f(1))) – вот он, ход времени.
Описание хода времени в виде выполнения программы, написанной на функциональном языке программирования, решает фундаментальный вопрос: где «хранится» прошлое? Правильно ли понимать, что настоящее – это «то, что есть», а прошлое – «его сохраненная запись»? Нет, и еще раз нет. Ничего нигде не хранится, есть просто цепочка функций, через которые проходят данные. Каждый миг времени принимает «входные данные», меняет их, и «передает дальше». Мы это описываем как последовательность выполнения кода, но это не совсем верно, ведь если код – это время, то выполняется он в каком-то «надвремени». Вы читаете эту статью последовательно, но она уже существует вся целиком. Настоящее – это точка нашего восприятия, физически текущий момент ничем не отличается от любого другого, в прошлом или будущем.
Сравнивая цепочки событий с цепочками вызова функций, мы не учитывали возможность разделения (Fork) и слияния (Join) потоков.
6. Дерево миров
Думаю, все знакомы с теорией Эверетта о «делящейся Вселенной». При каждом событии реальность расщепляется на множество копий, воплощая все возможные исходы события. Бросаем монетку, Вселенная тут же разделяется на две: в одной реальности выпадает орел, в другой – решка (условно говоря, конечно). Положение монетки — это будет единственное различие данных реальностей. Впрочем, через некоторое время реальности могут разойтись: например, в зависимости от исхода человек совершает тот или иной поступок, что дальше может повлечь еще большие расхождения. В целом это похоже на дерево со стремительно разрастающейся короной, корень которого – момент создания Вселенной. О том, что должен существовать компенсирующий это разрастание процесс слияния реальностей задумываются не все, а некоторые теоретики вообще отрицают такую возможность. Но мы к ней еще вернемся.
Теория параллельных реальностей имеет две прекрасные иллюстрации в программировании: многопоточность и система контроля версий SVN.
Мы уже говорили о том, что видим вокруг себя множество событий, протекающих параллельно друг другу — они разделены пространством (а могут еще и временем). Например, бросаем сразу две монетки, на них независимо друг от друга что-то выпадает. Теперь же речь о третьем виде разделения событий, когда они занимают одно пространство и время, но находятся в разных ветках реальности. Разделение потока выполнения кода (т.е. создание потоков) в диаграммах деятельности известно как операция FORK.
И вот здесь очень любопытный момент! Потоки в программе создаются гораздо реже, чем встречается оператор if. Быть может, нечто аналогичное происходит и в реальности? Т.е. параллельные миры образуются не на каждое без исключения событие, а лишь в определенные моменты (определяемые кодом, т.е. потоком времени). Иначе было бы странно: там, где мы видим if, на самом деле сокрыт Fork.
Второй пример к теме параллельных миров, это SVN. Для тех, кто не в курсе, краткое пояснение: при разработке программного продукта код хранится на сервере (в репозитории). У каждого разработчика на диске – его локальная копия, и есть две основные операции: Update – выкачать изменения с репозитория к себе, Commit – закачать на сервер свои изменения. Но для нашей темы самое интересное даже не в этом, а в таких штуках как diff и branch. Когда в репозитории происходит изменение (даже если в одном из миллиона файлов поменяли один символ) автоматически создается новая ревизия, с номером на единицу больше текущей. Сервер хранит все ревизии, и позволяет выкачать себе не только последнюю (HEAD) ревизию, но и любую другу. Если бы SVN на каждое изменение делал бы копию всего репозитория, работал бы он очень долго, а место на диске быстро бы закончилось. Вместо этого он запоминает лишь разницу, т.е. в каких файлах какие строки изменились, удалились, или добавились. Вот эта дельта между двумя ревизиями и называется diff. Все, что есть на сервере сейчас – это, в конечном счете, сумма diff’ов всех ревизий. Иногда с основного проекта (называемого trunk) делаются ветки (branch), что позволяет вносить изменения независимо, в транк и в ветки. Судьбу ветки определяют разработчики, исходя из ее задачи. В некоторых случаях она вливается обратно в транк. Инструментарий SVN работает с ревизиями и ветками исключительно в понятиях diff. Мы всегда можем посмотреть diff между ревизиями (т.е. что именно изменилось с предыдущей ревизии). Можем накатить комит, сделанный в одной ветке, на другую ветку – при этом мы опять же будем видеть именно diff.
Что нам дает эта аналогия с SVN?
Природа всегда стремится идти по пути наименьших энергозатрат. Думаю, что одна из причин, почему теория Эверетта тяжело укладывается в голове, заключается в следующем. Идея того, что в результате столкновения двух атомов со Вселенной (которая на несколько десятков порядков больше) снимается несколько полных копий — любому здравомыслящему человеку покажется, как минимум, странной. А программистам здесь все понятно: копируется и «хранится» только разница между Вселенными (diff).
Так что же с JOIN?
Силы природы всегда симметричны, и если реальности могут ветвиться, значит, они могут и склеиваться. Я придерживаюсь теории, что Вселенная возникла в результате большого взрыва, и когда-нибудь (очень не скоро) сменит свое расширение на сжатие (две фазы цикла: анализ и синтез). Здесь мы говорим о пространстве, а еще есть древо параллельных реальностей, расширяющееся вместе с пространством – оно ведь тоже должно в итоге свестись к точке. Без механизма слияния реальностей это было бы невозможно.
Существование силы притяжения, стремящейся соединить реальности можно проиллюстрировать на следующем примере.
Вам нужно приехать по определенному адресу, а путей для этого – множество. Возникает ветвление: в одной реальности вы полетите на флип-пузыре, в другой на телепорте, в третьей вообще пешком. С одной стороны, эти реальности стремятся разойтись, накопить максимальный diff, поскольку одни события тянут за собой другие (пошли пешком, встретили знакомых – пошли в кафе, полетели на флипе, попали в антригравитационную яму – разлили кофе на одежду, полетели домой). С другой стороны, есть цель (попасть в заданную точку), задающая событийный вектор. Отклонения могут оказаться сильнее этого вектора, но далеко не всегда. Скорее всего, тем или иным путем, вы все-таки доберетесь до места – реальности схлопнулись.
Чисто физически слияние реальностей можно описать тем же принципом суперпозиции, как в примере с двумя лучами света. Здесь мы приходим к еще одному интересному выводу: каждая точка пространства-времени является вершиной не только для куста последующих альтернативных событий, но и результатом слияния ряда предшествующих (параллельных) событий.
7. Законы времени
Рассмотрим некоторые интересные особенности работы времени (опять же, глазами программистов).
Парадоксы – это бранчи
Самый лютый парадокс, которого боятся множество фантастических персонажей, это «парадокс дедушки». Рассмотрим его на примере фильма «Назад в будущее». Марти переместился из 1985г. в 1955г., и помешал знакомству своих родителей. Появляется новая ветка (поток выполнения кода, ветка SVN, как угодно), т.е. альтернативная реальность. В ней Марти так и не родится, но в этом мире останется наш Марти. Переместившись назад в 1985 год, он попадет в будущее этого альтернативного мира. Но Марти справился с задачей – он не только познакомил родителей, но и повлиял на их характер. Вернувшись в 1985 год он шокировался позитивным переменами, произошедшим с его семьей. Что произошло? Он не дал бранчу сильно разойтись с транком (в плане своего появления на свет), и еще накатил патч:
===================================================================
— Hill Valley/Семья Макфлаев (revision 1)
+++ Hill Valley/Семья Макфлаев (working copy)
-Нахальный Бифф Таннен — начальник
-Разбитая машина
-Брат в спортивном костюме
+Биф протирает машины
+Тойта Статлер в гараже
+Брат в пиджаке и брюках
===================================================================
Петли времени – это рекуррентные вычисления
В замечательном фильме «День сурка» бедняга Фил сотни раз проживал один и тот же день, пока не довел его до совершенства. Каждый „следующий день“ он начинал не с нуля – он помнил все предыдущие, обладал накопленным опытом (например, игры на пианино), и так продолжалось до срабатывания некого условия.
В одном дне Фил крутился около 10 лет — т.е. „каких-то“ три с половиной тысячи итераций. Теоретически их может быть еще больше, но не бесконечно. Такой вывод диктуется законом сохранения энергии. Петля времени, которая содержит бесконечное количество витков, эквивалентна бесконечной плотности энергии событий и бесконечной плотности информации в данном фрагменте истории. Скорее всего, петля с безусловным переходом просто постепенно „обесточится“, т.к. она будет вне основного потока времени. Если проводить аналогию с программой, то есть есть некий механизм, ограничивающий максимальное число итераций у циклов.
В завершение цикла
А как же, таков закон времени: мы вернулись к отправной точке, но уже на качественно новом уровне. Придерживаться ли ортодоксальных парадигм времени, или использовать любые возможности для новых пониманий — вот выбор.
Почему для программистов здесь не паханное поле? Может быть, создатель Времени был IT-шник до мозга костей? Все дело в логике. Есть базовые задачи, и есть их логичные решения. Хранить diff вместо полной копии. Зациклить набор действий, вместо его тиражирования. С подобными задачами столкнется любой технический специалист, в самых разнообразных областях. Устройство реальности — не исключение, там тоже были подобные задачи, и „кто-то“ их решил. Точнее, все конечно наоборот: это мы, сами того не зная, повторяем уже заложенные в реальность решения (называя это логикой, системным подходом и т.п.).
Так в наши руки попадает ключ к пониманию устройства реальности. О времени можно не только снимать фантастику, или документальные фильмы (по сути вопроса, как правило, бессодержательные). Чтобы прикасаться к тайне времени не нужен БАК, или знание квантмеха (хотя на определенном этапе, возможно, это не помешает). У каждого программиста уже есть все необходимое для исследования природы времени — его способ мышления.
UPD
Мы затронули очень сложную тему, вызвавшую немало обсуждений, результат которых — ответы и акценты на сказанном ранее — предлагаются к прочтению.
1. Основная мысль статьи — не в том, что Вселенная — это компьютер, виртуальная реальность и т.п. Отправная точка статьи в том, что время — это механизм управления процессами. Это то, что выстраивает цепочки событий по определенному алгоритму (поэтому время=код), благодаря чему упорядоченность Вселенной со временем возрастает. Одно из базовых свойств этого алгоритма — цикличность (примеры уже были). ВСЕ ПРОЦЕССЫ во Вселенной носят циклический характер. Да, движение Земли вокруг Солнца связано с гравитацией, и казалось бы, при чем тут время? Этот принцип (наличие центра и вращение элементов вокруг него) мы можем увидеть на любых масштабах: от атома (ядро, электроны), до космоса (звездные системы, галактики, по одной их теории — сама Вселенная, все вращается). Вращательное движение — самое фундаментальное, т.к. все прочие виды движения — его частные случаи. Если в основе физической реальности лежит вращение — то цикличность есть во всем. И это свойство времени (т.к. речь о динамике процессов).
2. Сравнивая время и программу нам нужно забыть о том, что программа выполняется. Сама идея „выполнения“ подразумевает существование некого текущего момента, „настоящего“ — а оно есть только в нашем восприятии. Если взглянуть на вечность, мы увидим протянутые из прошлого в будущее мировые линии объектов, т.е. любая система существует „сразу“ во всех моментах. Если этой мировой линии сопоставить некую последовательность команд кода (время) и данных (меняемых этим кодом) — разве можно сказать, что этот код последовательно выполняется? Т.е. сначала вот это действие, потом это, спустя какое-то время — будет вот это… и вот мы уже говорим о „течении времени во времени“, т.е. описываем время через само себя — замкнутый круг. Над адекватной парадигмой потока времени нужно думать дальше.
3. Много вопросов про цикличность. Еще раз: цикличность — это не повторение тех событий, которые уже были. Это повторение управляющих воздействий времени. Оно идет извне, от той системы, частью чего объект является. Таких воздействий множество — отклик на них рождает ритм событий. Например, стакан стоит на окне, и восход солнца он „встречает“ регулярно, с шагом в день.
4. Многих интересует вопрос собственно перемещений (особенно в прошлое), который полноценно осветить в статье не хватило времени (а будет ли вторая — пока не ясно). Добавлю пару слов.
Попасть в прошлое — это не „восстановить данные“, да и где вы их восстановите? Линия (t)runk'а (т.е. «стрела времени»), и линия бранча, ответвленного от него при переброске в прошлое — все это цепочки не «кода», а «кода + данных». Здесь нужно мыслить в парадигме функционального программирования: данных вне выполнения кода НЕ СУЩЕСТВУЕТ! Нет переменных, состояние которых нужно восстановить. Если Вы перелетели в другую точку „графа выполнения кода“ (т.е. в прошлое), там будут те же данные, что и «были раньше». Как это сделать практически? Рассуждения уведут нас за пределы темы программирования.
5. Небольшое продолжение статьи можно найти здесь.