Магия размерностей и магия Котлина. Часть третья: Смешение магий

Система СИ и взаимосвязи между единицами физических величинСистема СИ и взаимосвязи между единицами физических величин

Это третья, заключительная статья серии, посвящённой библиотеке для работы с физическими величинами системы СИ, а также другими величинами, в Котлин. В ней мы рассмотрим, как фундаментальные математические структуры, которые природа «заложила» в систему СИ, предопределили дизайн DSL. А также о том, как возможности и ограничения Котлина были использованы при разработке библиотеки KotUniL.

Вот список статей серии:

  1. Магия размерностей и магия Котлина. Часть первая: Введение в KotUniL  

  2. Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности  KotUniL

  3. Магия размерностей и магия Котлина. Часть третья: Смешение магий

В каком мире живем?

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

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

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

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

Размерности или типы принято обозначать заглавными латинскими буквами. Так, например, классическая система размерностей, на которой базируется система СИ содержит семь размерностей: LMTIΘNJ (длина, масса, время и т.д.).

Получается, вся физическая информация может быть измерена, рассчитана, предсказана и т.д. в виде набора «размерных величин» — пар из числа и размерности. При этом размерность записывается в виде (если мы работаем с LMTIΘNJ) Lp1Mp2Tp3… где pi —  степень размерности. 

Например, размерность ускорения L1T-2, а ускорение свободного падения тела у поверхности Земли — 9,8 м/сек2.

Другими словами, мы живём в мире, в котором любая объективная физическая информация представима в виде точки из восьмимерного пространства R*D где R — это число, отображающие значения физической величины, а D — пространство семимерных векторов реальных чисел, представляющих степени при соответствующих размерностях.

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

Но для простоты мы далее сосредоточимся на пространстве семимерных векторов.

Какова математическая структура пространства размерностей?

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

Базовое множество пространства размерностей (в системе СИ) — это вектора длинны 7. Элементы вектора — вещественные числа. С каждым элементом (номером элемента) раз и навсегда связана определённая физическая размерность. 

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

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

Операции + и - на паре D*D имеют очень ограниченную область определения: операции определены только если d1 == d2 (на так называемой диагонали произведения D*D).

Результат операций тоже очень необычен:

d1 + d2 = d1 — d2 = d1 = d2

Например, складывая расстояния или вычитая длины, мы всегда остаёмся в размерности L1

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

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

Операции умножения и деления определены повсеместно, т.е. над любыми парами элементов из D. 

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

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

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

Если я не прав, опять-таки попрошу математиков из числа читателей меня поправить.

Следует также упомянуть о применении функций типа синус или интеграл к физическим величинам. Здесь всё просто. Они применяются только к числовым величинам, а вектор размерности не меняют.

Абелевы группы и алгебраические поля с их причудливо определёнными арифметическими операциями нужны нам не только любопытства ради, но и по вполне прагматическим причинам. И вот почему.

Рискну сформулировать лемму:

Лемма: Все физические формулы (по крайней мере, нужные на практике:-) можно представить в виде ориентированного вычислительного графа, у которого узлы имеют один предок и один либо два потомка и интерпретируется как одна из операций с потомками из набора операций; +,-, *, /,** (возведение в степень). 

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

Магия языков программирования заключается в том, что компиляторы умеют строить эти самые ориентированные вычислительные графы. А отдельные языки, например Котлин, позволяют не только определять новые объекты, но и определять над ними операции, которые применяются к обычным числам — всё те же +,-, *, /,** (возведение в степень). 

Это означает, что после этого мы можем новоопределённые объекты так же использовать в формулах, как мы привыкли это делать с числами. 

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

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

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

Блеск и нищета Котлин и дизайн DSL

Итак, мне хотелось разработать специальный Domain Specific Language (DSL) для манипулирования физическими и иными размерными величинами.

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

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

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

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

Начнём с простейших школьных формул:

x = 1 m

y = 1.5 m

s = x * y

Вроде всё предельно просто и знакомо с пятого класса.

Но присмотримся повнимательнее к выражению

x = 1 m

Что на самом деле это значит? Как это можно реализовать на языке программирования?

Фактически эта запись подразумевает использование функции от двух переменных 

f: R, N → М (L)

где R — множество в общем случае вещественных чисел

N — множество имён физических единиц

М (L) — множество физических единиц длинны 

Разумеется, в языке Котлин есть функции, но если мы вместо

x = 1 m

напишем что то вроде

f(1, "m") 

это будет не очень понятно техническим людям.

В Котлине есть инфиксные функции, но они должны иметь два аргумента.

Т.е. можно написать  m   и использовать её в виде:

2 m 5

но нельзя написать функцию инфиксную функцию m чтобы использовать её как 

1 m

В итоге, похоже, нам остаётся один вариант: использовать функции — расширения. Тогда пример

x = 1 m

y = 1.5 m

s = x * y

на нашем языке можно будет записать как:

x = 1.m
y = 1.5.m
s = x * y

А можно сразу

s = 1.m * 1.5.m

Про префиксы, суффиксы и инфиксы в программировании

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

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

Отталкиваемся от фундаментального понятия отображения одного множества пространств в другие. 

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

Больше всего термин «арность» употребляется применительно к функциям и их аргументам. 

В соответствии с определением под функциями понимаются однозначные отображения. А некоторые функции с хорошо определенным результатом принято называть операциями. 

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

В школе нас научили записывать функции в скобочной нотации:

y = f (x1, x2, …xn)

Бинарные функции можно записывать в нотации, которая называется инфиксной:

y = x1 f x2

Знакомые арифметические операции почти всегда так и записывают, например

y = x1 + x2

z = a*y

Унарные операции можно записывать скобочно

y = f (x)

А можно в префиксной нотации

y = f x 

или в постфиксной

y = x f

Известные примеры постфиксных операций это вычисление процентов (20%) или факториала (5!).

Примеры префиксных операций, реализованных в языках программирования: логическое отрицание not a или ! а

а также операции с assign:  

y += 2 или y = ++x
У них есть и постфиксный вариант: y = x++ с немного другой семантикой.

Арифметические операции в префиксной, инфиксной и постфиксной нотации (пред)определены в большинстве языков программирования. 

Тонкий вопрос, можно ли определять собственные подобные операции.

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

Так что пока в KotUniL обходимся без постфиксных функций.

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

Например, можно купить 500 грамм вина. Если выпьете из них 200 грамм, у вас останется 300 грамм. 

Чтобы удовлетворить этим требованиям преступаем ограничения фундаменталистов от объектно-ориентированного программирования и реализуем арифметические операции над размерными величинами:

val rest = 1.5.kg - 200.g 

В этой записи мы имеем три разных инстанции одного типа. 

А в результате умножения или деления получается составной тип, отличающийся от двух исходных. Я в своей библиотеке называю это Expression хотя в это всегда произведение, как и следует из приведённой выше теории. 

Разработчики Котлина большие молодцы, что разрешили использовать в идентификаторах большинство символов Юникода. Это позволило без труда отобразить требования системы СИ к обозначениям некоторых размерностей и размерных префиксов в код.

Также сильным ходом при дизайне языка было разрешение использовать в качестве идентификаторов произвольные символы, заключённые в специальные кавычки:  

val prise = 52.`€`/m2

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

Немного личных впечатлений


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

Итак, уже в античные времена при торговле, взимании податей и строительстве были нужны меры веса и длины. Появились эталонные локальные  артефакты в качестве эталонов. В средние века в Европе на каждой ярмарке и в каждом городе были свои меры длинны и веса (иногда — объёма), что при межгородской торговле создавало неудобства. 

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

Но прошло полтора столетия, прежде чем остальной мир более-менее присоединился к стандарту.

Стандарт СИ совершенствовался и развивался. Об этом хорошо написано в Википедии.

Меня лично поразили два последних изменения стандарта. 

Особенно предпоследнее, принятое Генеральной Конференцией по Мерам и Весам в 2018 году и вступившее в силу в 2019. 

За два последние века наука нашла много физических процессов, которые всегда приводят к одним и тем же результатам. Например — скорость света постоянна и равна 299 792 458 м/с.

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

А в 2018 году решили сделать обратное преобразование. Не константы определять с помощью эталонов, а отказаться от физических артефактов  и измерять физические единицы на базе природных констант. Картинка из Википедии, помещённая в начале статьи, объясняет новую структуру процесса определения единиц системы СИ.

Лично мне потребовалось определённое время, чтобы постичь философское значение этого решения.

Ну, а последнее решение Генеральной Конференции по Мера и Весам не такое фундаментальное, но примечательное. И произошло оно уже после того, как я начал работать над своей библиотекой. 

Совсем недавно точность выражения результатов расчётов и измерений, выражаемая  префиксами (милли-, микро, кило, гекто…) была расширена из-за практических потребностей с интервала степеней (-24, +24) до (-30, +30). Напомню, что префикс «нано», это «всего» -9. Это фантастика, насколько далеко в Космос и глубоко внутрь элементов материи проникла своими измерениями современная наука.

Иллюстрация: История и структура процесса определения единиц системы СИ. Источник: Википедия 

© Habrahabr.ru