Масштабируемые архитектуры программ
Шаблоны проектирования Haskell отличаются от основных шаблонов проектирования одним важным способом:
-
Обычная архитектура: объединить несколько компонентов типа A для генерации «сети» или «топологии» типа B.
-
Архитектура Haskell: объединить несколько компонентов типа A для генерации нового компонента того же типа A, неотличимого по характеру от его заместителей.
Это различие влияет на развитие двух архитектурных стилей по мере роста кодовых оснований.
Обычная архитектура требует абстракции слоев сверху абстракции:
-
О нет, эти Bs не соединяются, поэтому давайте создадим сеть Bs и назовем C.
-
Хорошо, я хочу собрать несколько Cs, так что давайте создадим сеть Cs и назовём это D.
Вымойте, промойте и повторите, пока у вас не будет неуправляемой башни абстракций.
Благодаря архитектуре в стиле Haskell вам не нужно сохранять слои на абстракциях, чтобы сохранить способность комбинировать совместимость. Когда вы объединяете вещи, результат все же сам комбинируется. Вы не различаете компоненты и сети компонентов.
На самом деле этот принцип должен быть знаком любому, кто знает основы арифметики. Когда вы объединяете связку чисел, вы возвращаете число:
3 + 4 + 9 = 16
Нулевое или большее количество цифр поступает и выдается ровно одно число. Полученное число само по себе можно комбинировать. Вам не нужно узнавать о «веб-сайтах» номеров или «веб-сайтах» номеров «Интернет». Вам не нужно узнавать о паутине (сети) чисел или о паутине паутин (сети сетей) чисел.
Если ученики начальной школы могут овладеть этим принципом, то, возможно, мы тоже сможем. Как мы можем сделать программирование более похожим на дополнение?
(+) :: Int -> Int -> Int
... и 0 гарантирует, что мы всегда можем преобразовать менее одного числа в ровно одно число, предоставив подходящее значение по умолчанию:
0 :: Int
Это будет хорошо знакомо программистам Haskell: эти сигнатуры типа напоминают методы класса типа Monoid:
class Monoid m where
-- `mappend` аналогичен `(+)`
mappend :: m -> m -> m
-- `mempty` аналогичен `0`
mempty :: m
Другими словами, класс типа Monoid является каноническим примером этого архитектурного стиля Haskell. Мы используем mappend и mempty для объединения 0 или более ms в ровно 1 m. Получаемый m всё ещё можно комбинировать.
Не каждая абстракция Haskell реализует Monoid, и не нужно, потому что теория категорий берет эту основную идею Monoid и обобщает её на более мощные домены. Каждое обобщение сохраняет тот же основной принцип сохранения комбинируемости.
Например, категория — это всего лишь типизированный моноид, где не все комбинации проверяют тип:
class Category cat where
-- `(.)` аналогичен `(+)`
(.) :: cat b c -> cat a b -> cat a c
-- `id` аналогичен `0`
id :: cat a a
... Монада похожа на моноид, где мы объединяем функторы «по вертикали»:
-- Слегка модифицируем из исходного типа класса
class Functor m => Monad m where
-- `join` аналогичен `(+)`
join :: m (m a) -> m a
-- `return` аналогичен `0`
return :: a -> m a
... и аппликация похожа на моноид, где мы объединяем функторы «по горизонтали»:
-- Очень измененный, но эквивалентный исходный тип класса
class Functor f => Applicative f where
-- `mult` аналогичен `(+)`
mult :: f a -> f b -> f (a, b)
-- `unit` аналогичен `0`
unit :: f ()
Теория категорий полна обобщенных моделей, подобных этим, и все они пытаются сохранить эту базовую интуицию, которую мы получили для добавления. Мы конвертируем несколько вещей в одну вещь, используя что-то похожее на дополнение, и мы конвертируем менее одной вещи в одну вещь, используя что-то похожее на ноль. Как только вы научитесь мыслить с точки зрения этих шаблонов, программирование становится таким же простым, как и базовая арифметика: вступают комбинируемые компоненты, и получается только один комбинируемый компонент.
Эти абстракции масштабируются безгранично, потому что они всегда сохраняют совместимость, поэтому нам больше не нужно наносить дополнительные абстракции сверху. Это одна из причин, почему вы должны изучить Haskell: вы узнаете, как строить плоские архитектуры.
Перевод с английского, автор: Габриэль Гонсалес, 2010 г., оригинал: Scalable program architectures
Опубликовано: 2018.03.24, последняя правка: 2021.06.10 20:38
Отзывы
✅ 2019/01/20 20:10, utkin #0
Давайте практический пример, отличный от факториала и а + b.✅ 2019/01/21 09:03, utkin #1
Посмотрел что такое Моноиды. По сути ближайший аналог из С++ это вектора. Разница только в том, что в Хаскелле они есть из коробки. Функторы тоже есть чуть более, чем везде. То, что они называются процедурным типом, в Паскале ничего не меняет.✅ 2019/01/21 10:56, Автор сайта #2
Габриэль Гонсалес, автор статьи, даст Вам наилучшие практические примеры. Ссылка приведена выше.✅ 2022/04/29 19:32, stein47 #3
Когда-то давно в одном уважаемом питерском ВУЗе я понял, чем кардинально отличается хороший преподаватель от посредственного. Некоторые профессора в очень почтенном возрасте с необыкновенной ясностью и очень простыми словами объясняли сложнейшие вещи. К чему это я... Читал статью и ловил себя на мысли, что ничего не понял. Немного обидно осознавать себя туповатым.✅ 2022/04/30 07:17, MihalNik #4
К чему это я... Читал статью и ловил себя на мысли, что ничего не понял. Потому что сплошь понятия теории категорий. Открываем вики и читаем: https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%BE%D0%B8%D0%B4моноидом называется множество M, на котором задана бинарная ассоциативная операция, обычно именуемая умножением, и в котором существует такой элемент e, что ex=x=xe для любого x ∈ M. Элемент e называется единицей и часто обозначается 1. В любом моноиде имеется ровно одна единица. Например:Неотрицательные числа (Натуральные числа и ноль) образуют коммутативный моноид (моноид с коммутативной операцией) как по умножению, так и по сложению. Множество всех конечных строк с элементами из алфавита Σ образует моноид, обычно обозначаемый Σ∗. Операция определяется как конкатенация строк. Конечно же длинная арифметика сразу дает масштабирование. И то, что можно ей сопоставить: Складывая строки, получаем строки. Считаем пустую строку — строкой. Складывая списки, получаем списки. Считаем пустой список — списком.
Еще, например, считаем единичные символы строками, никакого отдельного типа char.✅ 2022/05/01 16:38, Автор сайта #5
Читал статью и ловил себя на мысли, что ничего не понял. Немного обидно осознавать себя туповатым. Я тоже чувствовал подобное. Не могу сказать, что всё понял. Как раз таки далеко не всё. Но был стимул подумать: «А как Хаскелл способствует масштабированию? Какие варианты могут быть?». И знаете, для себя нашёл кое-что. В скором времени попробую изложить. Не таким научным языком, как у MihalNik, а как интуиция подсказала :)✅ 2022/06/09 16:30, void #6
Я тоже чувствовал подобное. Не могу сказать, что всё понял. Как раз таки далеко не всё. Но был стимул подумать: «А как Хаскелл способствует масштабированию? Какие варианты могут быть?». И знаете, для себя нашёл кое-что. В скором времени попробую изложить. Не дождался Вашей версии => изложу свою. Есть языки "наглядные", есть "странные", "эзотерические". Про которые сначала не совсем понятно становится, для чего они нужны вообще. Например: такие языковые скорее среды, чем просто языки программирования, как: лисп, форт, фактор, смоллток, эрланг, пролог и т.п. «А как Хаскелл способствует масштабированию? Какие варианты могут быть?». И знаете, для себя нашёл кое-что. я нашёл например это: журнал "Практика функционального программирования", год 2010, выпуск 6, стр. 49-53 c описанием метода и последующая тоже интересная статья "Инкрементальные регулярные выражения. Евгений Кирпичев" с реализацией на более мейнстримовой Яве. Я так думаю, из второй статьи должна быть всем понятна практичность задачи и реализация (а из первой -- и описание метода и реализация на хаскеле). В таблице 6.1 на стр. 64 здесь приводятся примеры моноидов: списки, строки, словари, функции, перестановки, и т.п. Если попытаться осознать эти примеры в контесксте реализаций таких языковых сред, как: форт, лисп, пролог, смоллток. Что такое эта самая "языковая среда"? Не столько реализация языка программирования (компилятор/интерпретатор), сколько среда исполнения+система типов+собственнно реализация интерпретатора или компилятора "метациклическая, на себе самой" (читайте по поводу метацикличности SICP, или "Goedel, Escher, Bach" -- либо непосредственно исходники метацикличных реализаций лиспа на лиспе, форта на форте, пролога на прологе (6..8 строк), смоллтока на смоллтоке). Что есть в этой среде? Объектный язык, о котором рассуждаем. Это уровень языковых объектов среды. Сравни с "first class object" понятием в языках программиирования и системах типов. И метаязыковой уровень -- метаязык, тот, на котором рассуждаем об объектном. Ещё есть машина исполнения: вычисление в стеках (форт, фактор), в списках(лисп, схема), в логических выражениях(пролог), в метаклассах/объектах/метаобъектах (смоллток). Что фактически реализует эта машина исполнения? За счёт композиции одних моноидов вычисляются другие. Например: для реализации интерпретатора нужно: 1) парсить синтаксис в CST 2) сопоставить CST -> AST 3) непосредственно вычислять eval AST 4) результат непосредственно распечатать в REPL для реализации компилятора нужно: 1) парсить синтаксис в CST 2) сопоставить CST -> AST 3) транспилировать AST входного языка в AST выходного, достаточно удобного для кодогенерации (например: assembler, стековая машина, регистровая машина, например, LLVM IR) 4) применять оптимизации 5) непосредственно распечатать (emit) результат кодогенерации 6) в виде, достаточно удобном для объектного (*.o, *.obj) линкера Что здесь происходит с рантаймом языка и системой типов? 1) есть входные структурные выражения, ADT, GADT 2) им pattern matching сопоставляются IR и выходные 3) динамические типы реализуются через статические и поддержку в рантайме. Моноид композиции других моноидов тоже моноид. То есть, композиция функций, композиция GADT, композиция списков (в лиспах), стеков (в фортах), строк (в модели языка SNOBOL, например) -- это тоже моноид. То есть: и интерпретатор ("метациклический интерпретатор") и компилятор ("метациклический компилятор" или nanopass framework) некоторого метаязыка -- могут быть написаны в подобной архитектуре, "масштабируемой архитектуре" --алгебры программ-- алгебры каких-то моноидов высшего порядка. И это не просто слова, мы действительно видим примеры таких подходов и реализаций (показываю пальцем): Например, языки: форт, фактор, 8th. Такие реализации как: - * форта -- RetroForth new, old; RevaForth
- * конкатенативного функционального -- фактор, RetroForth new (Nga VM), 8th.
Если немного про "эволюцию языка" и "эволюцию программиста" (то есть, его уровня понимания базовой идеи, концепции некоторого языка, среды, реализации). И где здесь происходит "метасистемный переход, или кибернетическая теория эволюции". Например: форт -> PostScript -> Factor -> RetroForth new (Nga VM). Что мы видим здесь? Есть надстройка -- метаязык форт, на котором удобно рассуждать о базовом объектном языке (например, ассемблере или паскале). То есть: например, реализовать на метаязыке форт свой ассемблер (и частично, метациклически, реализовать сам форт на ассемблере, как например в базовом RetroForth old, примерно до версий 8.х.х включительно) . Как именно здесь развивается реализация RetroForth Vx.y.z, где x.y.z <= 8 ? 1) появляются реализации под разные ОС или даже без них (native, ISO cd image) 2) стабилизируются FFI и привязки к Си библиотекам, интеграция с ОС. 3) появляются более функциональные модули на самом форте. После версий v10 происходит форк RetroForth new. Изначальный автор пишет минималистичную виртуальную машину, даже несколько (Ngaro, Nga, etc). Другой автор делает RevaForth как форк RetroForth old. эволюция метаязыков здесь проявляется в направлении: форт -> PostScript(PS, Display PostScript) -> фактор -> 8th, Nga. Например: PS это функциональный форт, DPS = объектно-ориентированный PS. Автор языка Фактор же говорит, что это другой класс языков, concatenative language (опять вспомним про композицию моноидов, с операцией конкатенацией). И продвигает свой язык как "функциональный форт с батарейками", примерно как CommonLisp "всё включено". При этом разработка в Фактор происходит "в образе", как в CommonLisp, InterLisp и смоллтоке. Реализация смоллтока (синтаксис которого умещается на страницу) на факторе очень компактна: см. запись в блоге http://factor-language.blogspot.com/2009/04/sup-dawg-we-heard-you-like-smalltalk-so.html и саму окончательную реализацию https://github.com/factor/factor/tree/master/extra/smalltalk (немного разрослась и стала больше изначальной про которую в блоге написано It weighs in at only 1217 lines of code, and that includes 423 lines of unit tests. I'm not sure exactly how much time I spent on it, but I'm guessing around 24 hours in total over the last week, and the bulk of that was tweaking the parser to parse various odd syntax. In particular, the AST to Factor compiler was very easy to write. ) Если вчитаться в саму эту реализацию. Почему это работает? - * PEG = Parser Expression Grammar, функции(1) для рекурсивного разбора.
-
* TUPLE для AST, = ADT, функции (2) для построения дерева, -
* eval для интерпретатора или pretty print для компилятора = функции(3) для обхода дерева и REPL и/или, кодогенерации. -
* классы Factor, в которые транслируется разобранное AST, родовые функции и мультиметоды (4) и т.п. то есть: имеем опять-таки композицию моноидов (1)(2)(3)(4). Исходя из этого, безсмысленно спорить какой язык хуже/лучше: смоллток, фактор или функциональный форт. Если вам так уж сильно нужна эта новая метаязыковая ООП среда (смоллтоковая, с ООП и метаклассами) -- её несложно добавить самостоятельно, расширяя базовую метаязыковую среду "ООП форта (фактор)", также как и синтаксис. В этом смысле, что такое это эти "first class object"? объекты языковой среды, реализующей нужную систему типов. например, в функциональном программировании -- лямбды. в логическом -- предикаты высшего порядка и термы. в императивном -- объекты, методы, динамическую типизацию, структурные выражения. например, если бы берём метаязык форт и реализуем на нём базовый объектный язык ассемблер. очевидно, что такой форт-ассемблер более полезен чем обычный: макросы на обычном ассемблере как правило более ограничены, в то время как в форт-ассемблере макросы можно писать на самом форте. в лисп-ассемблере, например -- на самом лиспе. то есть, на уровне метаязыковой среды форта языковые объекты базового объектного языка (ассемблер, его система типов) интегрированы в языковые объекты метаязыка (форта, его системы типов). на уровне метаязыковой среды смоллтока -- аналогично с объектами, классами, метаклассами (и рефлексией). на уровне метаязыковой среды какого-нибудь функционального пост- потомка форта, пост-форта (фактора, 8th или даже PostScript) -- их можно выделить явно, в стиле вот той статьи про хаскель и "составимые"//composable моноиды. опять же, почему функциональное программирование полезно. потому что на уровне функционального языка, его системы типов есть эти GADT, pattern matching, ленивость, частичное применение, полиморфизм высшего порядка, рефлексия по полиморфизму по системе типов. то есть: это всё те "базовые кирпичики", из которых составляется "масштабируемая архитектура".
Не только конечных разрабатываемых в такой среде программ -- но и самих этих метаязыковых сред.✅ 2022/06/09 16:33, void #7
Опять, возвращаясь к форту -- посмотрим теперь на "эволюцию программистов", их уровня миропонимания (архетипичного форта и архитектуры постфорта): - * одна ветка: RetroForth old v8 -> RevaForth -> 8th
- * вторая ветка: RetroForth old v8 -> RetroForth new
(v12, v2022.1) на основе VM: Ngaro, Nga, прочих(Napia и т.п). Автор "классического форта" RevaForth придумал свой коммерческий проект 8th, функциональный постфорт в духе Factor. Автор изначального "классического форта" RetroForth old -- эволюционировал в своём понимании до минималистичных виртуальных машин. По возможностям они примерно сопоставимы: Factor, 8th, RetroForth new. По минималистичности реализации: Nga (и далее, Napia) -- более минималистичны: образ ngaImage занимает всего 44260 **байт** (а не 103Мб как в factor.image или схожее в смоллтоке Pharo) как и сама реалиазация в vm/nga-* на примерно 12 языках программирования, ассемблер для такой машины retro-muri.retro тоже весьма минималистичен). Кстати, в RetroForth есть ещё любопытное минималистичное средство для Literate Programming: Unu. Тоже весьма простое и понятное, наглядное. «А как Хаскелл моноиды способствует масштабированию? Какие варианты могут быть?» Вот вам несколько вариантов, почему это работает: - * смоллток, и моноид метаобъектов (=объектов, классов и метаклассов)
- *
"Форт как образ мышления" (с) Лео Броуди, "лексикон программирования"(с)А.П. Ершов, "Literate Programming"(c)Дональд Эрвин Кнут, "Literate Deployment, Literate DevOps"(c)много современных авторов - * метасистемный переход В. Ф. Турчина
- * Language-oriented Framework как последовательность таких метаязыковых сред и систем
- * составимость (composability)
системы типов в рантайме этой среды в смысле интеграции типов в first class object - *
макросы в компайлтайме (AST макросы лиспа, компилирующие слова форта) - * CTMP макросы в build-тайме (CST макросы в духе
MetaOcaml, Template Haskell, Coverge PL, MetaLua/Terra/Lua) - * реализация nanopass framework в компиляторе
- *
функциональные структуры данных в БД в build-time - * общая концепция "моноидов, измеримых верёвками" для аналитики такой
БД в build/compile/metacompile/run-тайме. такие могут быть варианты. Всё ещё считаете монады, моноиды, форт, смоллток, хаскель -- чем-то абстрактным и недостаточно практичным? Практичность была понятна ещё лет 30..35+ назад, при реалиазации например Паскаля на Форте. Изначальная простая реализация описана в книге(серии статей) Джека Креншоу "Let's build a compiler" -- компилятора паскаля на паскале. Затем (уже позже) появилась реализация компилятора паскаля на форте. Примерно в то же время (если не в середине 80х) С.Н. Баранов разработал свою реализацию паскаля на форте -- в среду менее 16кб помещался компилятор и редактор. Хотя эти реализации не имеют никакого отношения к монадам -- несложно написать аналогичное в духе nanopass framework, и/или приведённой выше реализации смоллтока-на-форте, плюс, используя следующий инструмент для повышения понятности программ -- Literate Programming подход на основе Unu. Такая метаязыковая среда по самому способу своего построения демонстрирует метасистемный переход в духе В. Ф. Турчина: - * понятная и прозрачна, наглядна, инструментальна и открыта
- * выстроена как набор трансляций метаязыков в базовые
- * может расширяться и другими языковыми моделями, например как формальными типа моделей данных так и менее формальными на основе
контролируемого естественного языка. Ещё похожим образом был реализован учебный компилятор паскаля в книгах P.D. Terry. (там правда, используется CoCo/R и C++, ранее модула-2; на CoCo/R реализованы свой ассемблер, vm, монитор и отладчик этой vm, компилятор и кодогенератор; в целом, ничего не мешает здесь вместо модулы-2 или С++ для реализации CoCo/R и прочего -- использовать форт, фактор, Nga, смоллток или тот же хаскель с парсер комбинаторами )
Cуть в том, что метаязыковые среды, такие как форт, лисп, смоллток -- уже содержат в самой среде все требуемые моноиды для составимости/ composability "масштабируемой архитектуры" программ. Нужно только их осознать, эти моноиды. А осознав -- разумно применять.✅ 2022/06/09 16:58, void #8
Архитектура Haskell: объединить несколько компонентов типа A для генерации нового компонента того же типа A, неотличимого по характеру от его заместителей. смотрю на формулы Эйнштейна в тензорной форме, теорию относительности в тензорной форме через теорему Нетёр.
И опять их тут вижу, эти моноиды.
То есть: тензор это моноид, инвариантный относительно линейного преобразования.
Затем смотрю например на описание компонентной модели SOM в OS/2, книжку с идеей и реализацией рантайма языка Visual Age C++.
Да что же это такое. Опять они -- эти моноиды:- Компонентная ООП модель с поддержкой наследования строится на основе гипотез, инвариантов и лемм с теоремами.
Опять же она -- алгебра программ лямбда калькулюса или паттерны проектирования компонентной модели (?? паттерны проектирования паттернов проектирования?? метапаттерны проектирования, паттерны метапроектирования, лол??).
Ну то есть, вот оно, снова -- опять эти моноиды.
Причём же тут именно Хаскель разве что для иллюстрации реализации?
Это же общие алгебраические свойства: составимости/composability эдаких выражений, в упомянутом выше смысле..✅ 2022/06/09 22:51, Автор сайта #9
Не дождался Вашей версии Она появилась, просто Вы её не заметили.
Какое-то время назад Вы оставили в другой ветке комментарий. Я Вас попросил привести написанное в соответствие с правилами русского языка. И не дождался. Теперь опять Вы оставили пространные комментарии, полные того, что надо бы исправить, но от объёма исправлений ощущаю тихий ужас. Ещё раз попрошу Вас исправить свой текст и выслать на почту. Вы в комментариях ссылаетесь на статьи и книги. Вот оттуда можно почерпнуть стиль оформления. Так же хотелось бы, чтобы Вы взяли на вооружение лаконичность, а то нить Ваших рассуждений настолько длинна, что её начало и конец скрываются где-то за горизонтом.✅ 2022/06/09 23:06, void #10
а то нить Ваших рассуждений настолько длинна, что её начало и конец скрываются где-то за горизонтом Всё просто: есть примеры, на которых, по ссылкам видно реализацию и применение.
То есть, осмысление и применение некоторого метода, подхода к программированию.
Этот метод, идея и концепция -- становятся достаточно понятны, если действительно прочитать все эти ссылки, а ещё лучше -- прорешать этим методом аналогично описанным на своих примерах, своих более наглядных языках и фреймворках.
Нужны некоторые "этюды" как наброски, макеты к программированию.
Есть вещи, которые становятся понятны -- если сделать некоторые эксперименты своими руками. Или, если внимательно прочитать -- и ещё осознать -- то, что уже сделали другие.
Если этого не делать -- этот метод, идея и концепция так до вас и не дойдёт.
Те ссылки и примеры которые я привожу. На мой взгляд -- достаточно наглядный иллюстративный пример для осознания и осмысления -- подхода, метода.
Эти примеры требуют своего осмысления, осознания, понимания, символизма как миропонимания, если хотите.
Если это осознание и осмысление не делать -- примеры и тезисы до вас не дойдут.
Если же делать -- самы примеры становятся достаточно очевидны.
Вроде того героя Мольера, который на самом деле, всю жизнь разговаривал прозой.
И только недавно осознал -- что это так называется.✅ 2022/06/09 23:39, Gudleifr #11
Всё просто: есть примеры, на которых, по ссылкам видно реализацию и применение Попробуйте начать с одного примера. Иначе Вам, действительно, придется структурировать документ: составить резюме, дать план работ, по пунктам отчитаться о его выполнении, сделать общий вывод и тиснуть список литературы. Добавить свой отзыв
Написать автору можно на электронную почту mail(аt)compiler.su
|