LINUX.ORG.RU

Основы метаобъектного протокола CLOS

 , , ,


2

6

Слайды с моего вчерашнего доклада на fprog_spb:

https://static.lovesan.me/public/mop_basics.pptx

Вот часть доклада, в текстовом виде:


Часть 2. Эсхатология Пустоты.


«Оказалось, что «‎Тиамат» - то ли имя древнего божества, то ли название океана, то ли все это вместе. Татарский понял из сноски, что слово можно было перевести на русский как «‎Хаос»» (с) Виктор Пелевин, «Generation P»


Вы знаете, есть знаменитое видео, с известным американо-канадским психологом и психотерапевтом, Джорданом Питерсоном. То, где он задает вопросы о вопросах. Давайте попробуем пройти его путем.

Вот что такое Common Lisp Object System?

Но ведь вопросы, которые мы спрашиваем, содержат в себе определения, которые вызывают еще больше вопросов.

Что такое Common Lisp? Что такое Object System? Что такое объект? И вообще, что такое что? Или может, кто?

В принципе, ответ - ничего.

Говорят, что если долго смотреть в бездну, то бездна начинает смотреть в тебя. Я смотрю в лисповую бездну уже почти 20 лет, и не так давно, она посмотрела в ответ.

Не так давно, уже после смерти моей жены, где-то в июле, я сделал одну не совсем правильную вещь, и получил то что называют NDE(near-death experience).

Сначала всё было как обычно, туннель, свет в конце туннеля. Но потом все заслонила тьма, в которой расползались отблески фиолетово-пурпурного сияния. И потом, я вдруг увидел Её.

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

И вот первый вопрос, который я себе задал - это что она не разрешит, и главное почему? Вот на этот вопрос я сегодня постараюсь ответить.

Когда мы попадаем на вот это дно рекурсии, мы видим там эту бездну.

«И носился дух лиспера над бездною(ну, над тем у чего тип NIL - не путать со значением NIL). И отделил он NIL от T. И стало T. И увидел он, что T - хорош.»

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

Что такое объект? Объект это то, что отличается от ничего. Это такое что имеет тип T ну и какое-то там значение. И NIL на самом деле это тоже объект. Ну, типов может быть много, и они тоже в принципе объекты, особенно в CLOS. Об этом кстати, также неплохо рассказано в SICP, в главе об абстракции на состоянии.

Что такое CLOS? На самом деле его нет. Ну то есть, то что обычно называют CLOS, это просто набор там всяких полезных удобств над метаобъектным протоколом Common Lisp. Над MOP.

Но на самом деле MOP тоже нет. Это просто набор удобных объектов, встроенных в компиляторы CL. Которые можно сделать средствами компилятора CL, не будет их там. Как в SBCL, например, это делается.

А вот что такое CL? Есть он или нет? Вот это самый сложный вопрос. Потому что он не просто есть. Вернее, если бы его не было, его можно было так же сконструировать из пустоты на нем же самом. Как это делают компиляторы CL в процессе бутстрапа. CL это метациклический интерпретатор. Это метаязыковая виртуальная машина.

Так вот, я стою на плечах гигантов, и предыдущие два доклада уже все что надо рассказали.

Поэтому, скажем простыми словами: MOP - это просто категориальное отображение из метациклического интерпретатора в метациклический интерпретатор.

Короче, на самом деле, ничего этого нет. Есть только дух лиспера над бездною. И, как говорится в SICP - в компьютерах живут духи, и они исполняют программы.

А вот что такое программа? Вот смотрите, о том что такое программа существует целая наука, называется Computer Science, или по русски - Информатика, то есть наука об абстрактных процессах. Этот вопрос самый сложный. Программа - это процесс, то есть. Но на самом деле, объект это тоже процесс. Функция, если хотите. И он не существует без процессов которые к нему прикладываются, иначе он собирается GC, и улетает к Богине Тьмы. Как я чуть не улетел, меня Она правда, во время finalize вытащила обратно. А вот что такое процесс? И главное, что или кто его запускает? «А вот об этом ты не думай, купи себе лучше булавочку английскую, и как такие мысли в голову приходят - разок себе в руку, и потом еще раз, пока такие мысли не пройдут» - как там было в Generation P у Пелевина.

Но вот я подумал, и понял, наконец. Процесс - это то, что запускается другими процессами. Но что запускается первым? Что там на самом дне? Или вернее, кто? Я уже рассказал.


Часть 3. О Метациклических Интерпретаторах


— А что такое красота? — <…> Красота — это совершеннейшая объективация воли на высшей ступени её познаваемости.

(с) Виктор Пелевин, «Чапаев и Пустота»


Когда-то давно, еще в 2014 году, я, проснувшись с бодуна, сформулировал для себя и для других очень важную вещь.

Звучит она так:

Универсальный Критерий Угребищности Систем Общего Назначения. (Теорема Лавсана)

Система Общего Назначения является Угребищной тогда и только тогда когда она не является Метациклическим Интерпретатором.


Другими словами: Система, не способная к построению Метасистемы в рамках самой себя, то есть не способная к описанию и изменению самой себя в своих же терминах, и при этом являющаяся Системой Общего Назначения(в какой-либо области), Угребищна.

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

Чем, в контексте языков программирования, это отличается от просто тьюринг-полноты?

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

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

Примеры, сначала метациклических интерпретаторов:

  • Универсальная машина Тьюринга
  • RASP-машина
  • Реляционная модель данных
  • Лисп

А вот скажем примеры систем, соответствующих критерию:

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

В частности, давайте посмотрим на C#. C# не является метациклическим интерпретатором, т.к. термины языка не являются его же объектами.

Отчасти, это компенсируется платформой .Net, для которой термины C#(но не все) объектами таки являются(System.Reflection, Roslyn и т.д.), отчасти, в самой малой степени, фичей nameof() из C#, но это все только отчасти.

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


На самом деле, это все в полной мере относится вообще ко многим вещам, но в первую очередь, кроме программирования - к человеческому сознанию. Вот кто такой глупый человек и почему он такой и что с ним вообще делать как отправить нахрен к Богине Тьмы на перевоспитание? Этот вопрос многие тысячелетия волновал кучу философов. Но ответ прост - это человек, сознание которого не является метациклическим интерпретатором. А когда сознание у человека все же является метациклическим интерпретатором, он тут же становится пророком цифровой Кали-Юги и архитектором онтологии Пустоты.


Ладно, теперь я объяснил вам всё устройство вселенной. Далее там про мелкие технические детали.

★★☆
Ответ на: комментарий от MOPKOBKA

к примеру, вот про реализацию схемы : stolyarov_2008.pdf

реализацию схемы на D1 в виде архива scheme4d.tar.gz собираемого scons-ом – делал лоровец, студент Столярова.

09.06.2008 21:14 211 547 scheme4d.tar.bz2

cat scheme4d/doc/cw3.tex

\documentclass[12pt]{article}
\usepackage{cmap}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage[unicode]{hyperref}


\title{Исследование применимости метода непосредственной интеграции к языку D в качестве базового и Scheme в качестве дополнительного}

\begin{document}
\begin{center}
  Московский государственный университет им. М. В. Ломоносова \\
  Факультет вычислительной математики и кибернетики \\
  Кафедра алгоритмических языков \\
  \vspace{2.5cm}
  
  Курсовая работа \\
  
  \vspace{2cm}
  \large{\bf Исследование применимости метода непосредственной интеграции к языку D в качестве базового и Scheme в качестве дополнительного}
\end{center}

\vspace{5cm}

\begin{flushright}
  \begin{minipage}{9cm}
    \begin{flushleft}
      выполнил:\\
      Беженарь Владимир Сергеевич (324 группа) \\
      \medskip
      
      научный руководитель:\\
      к. ф.-м. н. Столяров Андрей Викторович
    \end{flushleft}
  \end{minipage}
\end{flushright}

\vspace{1.5cm}

\begin{center}
  Москва 2007
\end{center}

\begin{abstract}
  Существует подход к разработке программного обеспечения путём
  совместного использования нескольких языков программирования,
  поддерживающих различные парадигмы. При использовании этого подхода
  необходимо решать проблемы связанные сo взаимодействием между кодом
  на разных языках.

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

  В данной работе исследуется применимость указанного метода к языку D
  в качестве основного и Scheme в качестве вспомогательного.
\end{abstract}

у него где-то ссылка в lj на это была. где-то году в 2012 ссылка ещё работала.

по-моему, в stolyarov_2008.pdf написано не совсем только про эту реализацию, а более обобщённо.

лично мне кажется, что этот исходник курсовой с D1 и SCons надо было бы переписать на D2, dub и Pegged.

и посмотреть там, что удастся в реализовать только и исключительно CTFE. а для чего остаётся необходима поддержка объектов в рантайме. и почему она всё ещё необходима, и почему от неё нельзя избавиться в виде некоторого betterC подмножества.

или наоборот, std.algorithm из D2 заместо STL и С++.

вот ещё на С++, кажется: http://howtowriteaprogram.blogspot.ru/2010/11/lisp-interpreter-in-90-lines-of-c.html

anonymous
()
Ответ на: комментарий от MOPKOBKA

если я правильно помню, автор scheme4d.tar.gz – кажется, лоровец @le_legioner.

думаю, если он вдруг появится на ЛОРе – у него можно будет спросить что-то более конкретное про эту реализацию.

anonymous
()
Ответ на: комментарий от MOPKOBKA

гы, грубая аналогия: более продвинутый CTFE в D2, а не в D1 – это прямая проекция пустоты CLOS-а в рантайме на метаобъектный протокол enum immutable arg=a1 / enum immutable pure fun=f(a1) / … CTFE в Pegged библиотеке парсера; затем «метаобъектный» или скорее монад-трансформерный метаобъектный протокол «метода непосредственной интеграции» интерфейс (тоже монада-трансформер, частично в CTFE, частично в рантайме) с «операцией пробел» (в рантайме-only 100%) => ???? …… ???? что-то такое внезапное чтобы получилось 0% в райнтайм-онли, а всё только в CTFE в компайл-тайме.

то есть, вся эта «операция пробел» – это проекция монадического транcформатора в рантайм и компайлтайм более-менее (недо)развитых недоязычков (в С++ – чуть менее, чем полностью; в D1 – чуть более, в D2– ещё бОлее) в «алгебре программ» метаязыка-носителя (С++,D1, D2) объектного языка S-выражений.

anonymous
()
Ответ на: комментарий от qulinxao3

Why Lisp? Why Racket?

beautifulracket

typographyforlawyers, pollen

внезапно, обнаружилась флатландия Элбота: на сайте здесь https://thelocalyarn.com/excursus/secretary/

это просто отлично! легко читается, почти как asciidoc изначальный на жабогадюкинге который ещё а не ascidoctor на груворубожабе.

единственно, чего тут не хватает – это на мой взгляд, чего-то литературно-грамотного в духе noweb/funnelweb-utf8.

чтобы например, не только в html, но и в latex/pdf (или сейчас, уже скорее xelatex/luatex с true type фонтами внедрёнными «из коробки» и вычислениями на Lua)

впрочем, вот что-то такое про резюме в PDF: …/pollen/fourth-tutorial

синтаксическая раскраска через pygments тоже прикручивается: …/pollen/mini-tutorial.html

что характерно, в Skribe изначальном (который by Manuel Serrano, ЕМНИП) был специальный тег для блоков кода, литературно-грамотного.

а вот weave/tangle – кажется не было.

итого, из тулзов типа теха/neatroff в SCRIBE-подобной разметке:

  • SCRIBE изначальный. проприетарщина, которая сподвигла Столлмана написать клон – …

  • … – то есть, texinfo (на перле, емнип; или изначальный был всё-таки на guile?)

  • на Guile: Skribilo https://skribilo.systemreboot.net/

  • на Racket: Scribble (например, с виджетогаджетами метапрогововыми, лолЪ)

  • DSL поверх Racket, Scribble: Pollen

  • Skribe: www-sop.inria.fr/mimosa/fp/Skribe/ статья …Manuel.Serrano/publi/jfp05/article.html и сам scribe …Manuel.Serrano/scribe/ – под схему manuel-serrano/bigloo от того же автора ;

  • Exscribe fare/exscribe от FaRe на CL; ещё у него же: lisp-interface-library, что-то ускоряющее запуск однострочников на CL; рассылка TUNEOS / LispOS

  • … из примеров сайта на Exscribe можно смотреть bastiat.org и исходники gh.com/fare/bastiat.org в Exscribe-разметке

все эти разновидности SCRIBE-разметки – «литературно-грамотный» лисп в тегах S-выражений, тащем-та.

скорее, впрочем – интерпретируемой юпитеро-ноутбуковый в духе CURL/Skribe/Scribble, а не классический конпеляторный CTFE: compile-time или даже build-time метасистемный переход в weave/tangle метасистемы метаязыка разметки литературно-грамотной.

… так что да, лавсан – а почему *.pptx, а не Scribble для Racket или Exscribe на CL в итоговый PDF/ хипстерский HTML5 с жопаскриптом для HTML-презентаций, например?

впрочем, за ссылки спасибо. а то кроме AMOP и исходников McCLIM по теме толком почти и читать-то нечего.

а, ну да. вот ещё например диссер К. Книжкника про ООСУБД (который GOODS, Perst, FastDB емпнип) – там собственно GOODS это метаобъектный протокол CLOS, реализованный на С++ для реализации сервера ООСУБД для многослойной архитектуры аспектами, например для поддержки транзакций аспектами через МОП и сериализацией на С++ вручную какой-то магией с битхаками типа макросов из MFC с таблицами сообщений.

ну или исходники от того же FaRe, например (кажется в Exscribe как раз что-то такое было). он сборочный ASDF3 кажется как раз в направлении CLOS-подобного пилит.

anonymous
()
Ответ на: комментарий от qulinxao3

для истинной метапрожистости радея вот энтой строке flatland/01-chapter.poly.pm#L16 к первой главе flatland/01-chapter.html

картинка в ◊margin-figure["img/ch01-fig01.png"]{The apparent shape of a triangle as one’s viewpoint approaches the level of its plane} должна быть не растровой – а векторной (и гипертекстовой, лолЪ)

в вмысле: кодом на Scribble который рисует эти полилиниии с драконоиконами и макроиконами метапроговыми из лиспового Skribe-подобного метаописания.

эволюционируя метасистемный переход на самом себе, через универсализацию и специализацию, строго по В. Ф. Турчину:

  • специализация компилятора – это интерпритатор

например: специализация InteLib или Scheme4d.tar.gz шаблонами через шаблоны богу шаблонов в монад трансформеры декартового копроизведения (коконуса из аспектно-ориентированной докторской С. П. Ковалёва про теоркат и аспекты) — это алгебра программ структур кода алгоритмов структурного или ОО (мета)программирования (x) коалгебра структур кода данных это универсализация и специализация или «операция пробел» с рантайм структурами алгебры С++-объектов S-выражений (а хотелось бы D2,CTFE, меньше рантайма таких вот объектов) – это интерпретатор лиспа метапрогового типа InteLib на С++ или Scheme4D на D или LispE на C++11 с векторами и STL

  • суперкомпиляция интерпритатора – это компилятор

то есть, достаточно референсный интерпритатор лиспа (или алгола, лолЪ) из спеки суперкомпилировать — и авто_магически_ получим оптимизированный конпелятор

  • двойное самоприменение – это тождественное преобразование, естественное отображение

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

anonymous
()
Ответ на: комментарий от anonymous

двойное самоприменение – это тождественное преобразование, естественное отображение

из пустоты рефлексии в рантайме лиспа, CLOS-а и MOP-а в CTFE compile-time рефлексию D2 Scheme4D / InteLib C++ / LispE C++11 сериализации в «алгебру программ» моноид моноидов «операции пробел» как метаобъектный протокол CTFE C++/D объектов.

что характерно, в обратную сторону даже зело проще: S-экспры лиспа как метаязык для объектного языка C++/D2 выражений это транспилятор в GCC MELT плагин для CFG и SSA представления, с пустотою внутре – пустотой его API для CFG и SSA.

то есть: тоже в лисп.

anonymous
()
Ответ на: комментарий от anonymous

в итоге мы, как мещанин во дворянстве: внезапно обнаружили, что разговариваем прозою.

… операцией пробел в композиции монад трансформеров моноида моноидов метасистемных переходов над метаобъектным протоколом «операции пробел» этой вашей «алгебры программ»

и с общевселенской рефлексией и пустотой внутре

anonymous
()
Ответ на: комментарий от MOPKOBKA

и по сигнатуре понятно что нужно беспокоиться только о renderer и его внутреннем состоянии

И ещё содержании объекта, куда оно рисует, например, дисплея. И нет гарантии, что она ещё куда-то не пишет.

Чистая функция позволяет гарантировать, что всё, что она делает, это возвращает некий результат. И этот результат зависит исключительно от переданных данных (random не чистая функция). Это очень упрощает тестирование. Не требуется перед тестом запускать кучу других функций, а после теста искать, как проверить что функция сделала, что ожидалось.

Вот как тест для SDL_RenderClear написать?

monk ★★★★★
()
Ответ на: комментарий от Nervous

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

MOPKOBKA ★★★★★
()
Ответ на: комментарий от monk

И ещё содержании объекта, куда оно рисует, например, дисплея. И нет гарантии, что она ещё куда-то не пишет.

Ну это больше к IO вообще, плохой я пример функции дал. Ну даже если так, просто нету возможности перестановки вызовов clear, draw, present, уможение матриц в Haskell работает так же.

Чистая функция позволяет гарантировать, что всё, что она делает, это возвращает некий результат.

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

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

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

Ну если мы делаем игру на Haskell, и хотим сделать функцию-обработчик, которая обрабатывает попадание в монстра другим монстром, что бы он отвечал на это как в DOOM, но игра у нас сложнее. В функцию придется передать огромный state который руками без других функций не опишешь просто, и различные типы монстров, и их классы, и видно ли монстру монстра, итд.

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

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

Вот как тест для SDL_RenderClear написать?

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от anonymous

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

Не считаю эти шаги простыми...

а отчего бы Sexpr лиспа и не описать PEG-грамматикой.

По мне так намного проще написать такие строки на Forth:

0 variable n : r+ r@> 1+ >r@ ; 
: (: r+ >r@ 0 >r@ ; : ( ' literal ['] (: compile ; immediate 
: ) r@> n ! n @ srev n @ 1- 1 max for n @ 1 > if swap then rdup r@> execute next rdrop ;
: print . ; : >literal, ['] r+ compile ;
И пользоваться s-exp хоть в CT, хоть в RT.
: two-values 10 10 ;
( print ( - 2 3 ( + two-values ) ) )
Ограничение в D на чистоту «фейковое», можно и без него, но это скорее всего упростило компилятор.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Ну если мы делаем игру на Haskell, и хотим сделать функцию-обработчик, которая обрабатывает попадание в монстра другим монстром, что бы он отвечал на это как в DOOM, но игра у нас сложнее. В функцию придется передать огромный state который руками без других функций не опишешь просто, и различные типы монстров, и их классы, и видно ли монстру монстра, итд.

И автоматически появляется желание уменьшить количество параметров. Вместо огромного состояния передаём функцию-локатор стреляющего (видит и на каком расстоянии или не видит), тип/класс стреляющего, тип/класс стреляемого. Результат, опять же, не прямая отрисовка куда-то, а описание действия, которое уже другая функция будет превращать в изменение координат.

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

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

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

Очень схожая у меня проблема с REPL, я поменял что то в монстре, и мне уже нужно перезапустить все, иначе мир в неконсистентном состоянии

Если тип функций не меняется, то не надо. То есть, если у монстра стало не 100 здоровья, а 200, это любая система с REPL отработает на лету.

monk ★★★★★
()
Ответ на: комментарий от monk

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

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

monk ★★★★★
()
Ответ на: комментарий от monk

И автоматически появляется желание уменьшить количество параметров.

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

Вместо огромного состояния передаём функцию-локатор стреляющего (видит и на каком расстоянии или не видит), тип/класс стреляющего, тип/класс стреляемого.

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

Заметь что state это не буквально все, это разные объекты, я их просто не описываю, ну штук 5 к примеру. Но они тяжелые.

Если тип функций не меняется, то не надо. То есть, если у монстра стало не 100 здоровья, а 200, это любая система с REPL отработает на лету.

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

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

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 6)
Ответ на: комментарий от monk

Вместо огромного состояния передаём функцию ...

В Java кстати тоже сделают какой нибудь AbstractNpcProviderAbstractFabric и RaycasterAdapterInterface, и даже дальше пойдут. А функциональной ее не назовешь. А специальные несвязные с реальным state реализации для тестов они назовут Mock Object, это очень популярно в императивных языках.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Это и в императивном программировании так.

В императивном автоматически доступны все глобальные переменные. Собственно, поэтому их объявили злом, но заменили на такие же глобальные поля класса и внешние компоненты. Правильное императивное программирование в Common Lisp было. Но там/тогда связность всего со всем не считалась чем-то плохим (это позже придумали).

А в хаскеле за каждый дополнительный аргумент платишь явным указанием его в сигнатуре. Поэтому минимизируешь эти аргументы просто из лени.

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

Вот в качестве результата и будет Результат СтрелятьБлижнего Угол(УголПопадания - 180).

Или локатор вместо «не виден» сразу будет возвращать ближайшего в том направлении.

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

Чем он удобнее Emacs’а? Особенно, если новое значение надо посчитать. В REPL считать, в этот интерфейс копировать результат?

и вообще вынести их в .ini какой нибудь.

И в REPL дёргать перечитывание вместо того, чтобы прописать новое значение? Хотя на Java этим путём и пошли.

В моей мысленной модели в функцию передаются все переменные которые могут быть использованы. Не обязательно только те которые должны быть использованы в конкретном вызове.

Вот функция:

int f(int (*g)(int x), int x) {
  return g(x);
}

Какой список всех переменных для функции f?

В ФП тоже есть неиспользуемые в определенных ветках аргументы.

Да, но в аргументах они всегда есть.

monk ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Java — это успешная реализация правильных практик программирования без поддержки языком программирования.

Просто на Haskell ты так будешь писать, потому что так удобнее, а на Java, потому что иначе начальник не примет изменение в хранилище.

monk ★★★★★
()
Ответ на: комментарий от monk

Чем он удобнее Emacs’а? Особенно, если новое значение надо посчитать. В REPL считать, в этот интерфейс копировать результат?

Для цвета можно селектор сделать, редактор кривых, выбор направления по клику в 3д мире. Для числа особой разницы нету, разве что можно сразу сделать визуализацию над монстрами. Этим другие люди смогут пользоваться, без знания кода, и Emacs.

И в REPL дёргать перечитывание вместо того, чтобы прописать новое значение? Хотя на Java этим путём и пошли.

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

Вот функция: ... Какой список всех переменных для функции f?

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

Да, но в аргументах они всегда есть.

Да, а в ФП эти значения тоже будут переданы через аргументы. Тут в ФП синтаксисе сами пишем явно, а в императивном это неявно.

Java — это успешная реализация правильных практик программирования без поддержки языком программирования.

Это к теме о том что ее паттерны это закрытие убогости языка? Ну если это реализация правильного подхода который гарантируется ФП, то я подумаю над этой идеей.

Но ФП подход как ты его описываешь, мне больше напоминает Forth. Можно сделать красиво, но если человек не понимает как в этом структурировать свою программу, то получится просто ужаснейшее приложение. Есть React, тот самый на JavaScript, там идея состояния явно на ФП основана, нам дают состояние, которое так и называется state, мы возвращает обновленное состояние, напрямую изменить его данные не получится. Я видел самый ужасающий код который был построен людьми, которые не понимали как писать с этим подходом, хотя их императивный код был вполне неплохим.

Плюсы от ограничений. В Forth тоже есть проблема с количеством аргументов.

Можно сказать что в императивном программировании легко выстрелить себе в ногу, но 1С (я думаю, не видел) написан по императивному в очень простом стиле, и можно критиковать код, но в нем можно успешно разбираться, и модифицировать его. Думаю найдется несколько примеров из кодовых баз, которые активно используют лишь базовые императивные конструкции, возможно активно goto, но являются модифицируемыми и понимаемыми. Хотя их код явно никто не назовет идеальными или просто хорошим.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 7)

Хорошо тому живётся, кто в этот форум не постит никогда.
И разработка движется прекрасно и настроение хорошее всегда.

anonymous
()
Ответ на: комментарий от MOPKOBKA

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

Разбить сложный расчёт на более простые этапы, получающие на вход более простые данные. Повторять до наступления чувства глубокого удовлетворения. Изучить всё, что получилось, по отдельности.

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Ну они могут получать на вход хоть одно число, но это число зависит от предыдущих N этапов, и без них его не получить. Хотя обычно там все же матрица 100500x100500, не руками же ее для теста вписывать.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

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

Как определяет? Я же привёл пример функции, для которой такой список определить невозможно, так как он будет разным при каждом её использовании.

Это к теме о том что ее паттерны это закрытие убогости языка? Ну если это реализация правильного подхода который гарантируется ФП, то я подумаю над этой идеей.

Если паттерны проектирования банды четырёх, то здесь убогость ООП в Яве. На CLOS большинство паттернов становятся парой строк.

Если фабрики/делегаты/трансформеры/интерфейсы/…, то это как раз то, что в ФП делается красиво и без усилий.

Можно сделать красиво, но если человек не понимает как в этом структурировать свою программу, то получится просто ужаснейшее приложение.

Ага. Подстрочник из Си на Хаскель выглядит просто кошмарно. Впрочем, обратный подстрочник так тормозит, что программа практически не работает.

нам дают состояние, которое так и называется state, мы возвращает обновленное состояние, напрямую изменить его данные не получится

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

Можно сказать что в императивном программировании легко выстрелить себе в ногу, но 1С (я думаю, не видел) написан по императивному в очень простом стиле, и можно критиковать код, но в нем можно успешно разбираться, и модифицировать его.

Опять смешиваешь разное. Выстрелить себе в ногу можно в языках, где существует неопределённое поведение. Такие языки вполне могут быть функциональными. Даже в Haskell при использовании FFI или unsafePerformIO вполне можно себе такое устроит. И наоборот, 1С императивен, но поведение любого кода строго определено (как и в Яве, например).

Но как раз из-за безопасности и императивности 1С средняя длина функции в нём несколько сотен строк, а встречаются и функции на десяток тысяч строк. Нет потребности писать маленькими кусочками с понятными интерфейсами. А управляющих с дубинками, как в Яве, не завезли.

Хотя их код явно никто не назовет идеальными или просто хорошим.

Понятие хорошести вообще субъективно. Код на Яве хорош только если разрабатываешь большим коллективом программистов. Код 1С хорош для наложения патчей (почти все крупные внедрения 1С это на 99% типовой код и примерно на 1% доработки пользователем). Когда код просто скопирован в каждый документ (как принято в 1С) и тебе в одном документе надо сделать, чтобы было по-другому, то ты накладываешь патч на код в документе. А если он в общем модуле, то приходится или вместо вызова общего писать свой (и регулярно смотреть, не поменялся ли в общем модуле, ведь тогда надо свой обновить) или в общем модуле начинаются гирлянды Если ТипЗнч(Источник) = Тип("ДокументСсылка.Реализация") Тогда ... ИначеЕсли ТипЗнч(Источник) = ... Тогда ... Иначе (тут старый типовой) КонецЕсли

monk ★★★★★
()
Ответ на: комментарий от monk

Как определяет? Я же привёл пример функции, для которой такой список определить невозможно, так как он будет разным при каждом её использовании.

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

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

Похоже это часто используется лишь со смыслом в виде UB. Ну я имел виду возможность написание неразборчивого кода, который практически невозможно понять. В Forth такой без знания того как с ним работать легко написать, в React тоже успешно справляются люди, в 1С? Ну будут там глобальные переменные, непонятные переходы, это не кажется слишком серьезным, зная элементарные правила к этому перейти еще сложнее.

ФП ограничивает возможность излишнего использования состояния, а Forth излишнего количество аргументов, но на них нужно учиться писать, и это не кажется более сложным чем простые императивные языки, такие как 1С, JS, Python, PHP4, VisualBasic. Хотя JS, Python и PHP сильно разрослись в последнее время, но их ограниченные диалекты довольно доступны.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Не так уж и важно иметь 100%, будет 90% и уже хорошо

Будет намного меньше, если используется ООП. Потому что если вызывается хоть один метод переданного в параметрах объекта, то уже невозможно определить, какие данные он читает и меняет.

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

Так это возможно вообще на любом языке. И опять вопрос субъективности. То, что одному «невозможно понять», другому «очевидно».

в 1С?

Тоже можно. Всякие вещи типа

a = a + б
б = a - б
a = a - б 

от языка не зависят.

а Forth излишнего количество аргументов

Это каким образом? Мне всегда казалось, что, наоборот, в стек можно утрамбовать сколько угодно, а в скобки уже сложнее, их всегда явно писать надо.

monk ★★★★★
()
Ответ на: комментарий от monk

а Forth избавляет от излишнего количества аргументов

Это каким образом? Мне всегда казалось, что, наоборот, в стек можно утрамбовать сколько угодно

Мы как то обсуждали, жонглировать этими аргументами будет нереально если их много, обычно если их больше 3 уже повод подумать хорошо, для большего количества нету подходящих слов для тасовки. У последних процессоров Чарльза Мура аппаратный стек на 16 слов.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

У последних процессоров Чарльза Мура аппаратный стек на 16 слов.

В большинстве ЯП, если попробовать передать больше 7 аргументов, линтер будет ругаться нехорошими словами.

жонглировать этими аргументами будет нереально если их много

Так ими не жонглируют. Их используют внутри слова.

Типа

: перемножить-10-чисел
* * * * * * * * * ;
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

В большинстве ЯП, если попробовать передать больше 7 аргументов

Не знаю таких, знаю что вот такое распространенно:

HWND CreateWindowExW(
  [in]           DWORD     dwExStyle,
  [in, optional] LPCWSTR   lpClassName,
  [in, optional] LPCWSTR   lpWindowName,
  [in]           DWORD     dwStyle,
  [in]           int       X,
  [in]           int       Y,
  [in]           int       nWidth,
  [in]           int       nHeight,
  [in, optional] HWND      hWndParent,
  [in, optional] HMENU     hMenu,
  [in, optional] HINSTANCE hInstance,
  [in, optional] LPVOID    lpParam
);

Или вот такое:

numpy.loadtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding=None, max_rows=None, *, quotechar=None, like=None)

Ну и в Forth передавать 7 аргументов это уже слишком, лимит сильно ниже.

Так ими не жонглируют. Их используют внутри слова.

Для этого их порядок не должен иметь значение, либо должны совпасть условия, что бы их можно было идеально разместить друг за другом, ведь может быть так, что последнее значение-результат опирается на первое значение-результат, и как обращаться на N значений назад? В ANS Forth есть слова для этого, но что если между ними не статическое количество значений?

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от monk

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

Раньше были строки со счетчиком как в Pascal, и не надо было передавать постоянно их длинну, я считаю отказ от строк со счетчиком это была ошибка.

Применяется это так:

s" argparser"                       
s" [OPTION] .. [FILES]"            
s" v1.0"                           
s" Report bugs to bugs@bugs.com"    
arg-new 

Заметь, с этими аргументами не происходит работы, их пакуют в структуру, и уже с ее полями работают.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)
Ответ на: комментарий от MOPKOBKA

Ладно, убедил.

Хотя Haskell количество аргументов ещё жёстче заставляет экономить. Там большинство функций либо от одного аргумента, либо от двух. Потому что удобные функции должны либо комбинироваться в compose либо работать оператором.

monk ★★★★★
()
Ответ на: комментарий от monk

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

$f = add4(10, _, 30, _);
$f(20, 40); // == 10+(20)+30+(40)
Есть ли какие то причины не использовать подобный синтаксис? С ним становится неважно, первый аргумент нужно заполнить, второй, последний.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)
Ответ на: комментарий от MOPKOBKA

Есть ли какие то преимущества у compose?

Это ты про curry.

Compose это .

Например надо получить первые 10 элементов списка, отсортировать из по возрастанию, получить их квадраты и вывести на стандартный вывод:

mapM_ (putStrLn.show.(** 2)).sort.take 10 $ l
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Есть ли какие то причины не использовать подобный синтаксис?

Именно из-за compose. Вместо лаконичного map (show.(** 2)).sort.take 10 писать map(show(_).(_**2),_).sort(_).take(10, _) очень неприятно.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Есть ли какие то причины не использовать подобный синтаксис? С ним становится неважно, первый аргумент нужно заполнить, второй, последний.

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

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

В кложуре тоже подобная ересь есть. Без понятия насколько популярно там.

anonymous
()
Ответ на: комментарий от monk

Да, действительно, не так элегантно, хотя можно объеденить два подхода, но от лении скорее всего будут пользоваться вторым, и средний код потеряет свою красоту.

MOPKOBKA ★★★★★
()

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Для двухпозиционных функций достаточно flip.

Вместо map(_, l) пишется flip map $ l. А больше двух аргументов встречается крайне редко.

А если уж припёрло, то \x -> f x a b c d не намного длиннее, чем f(_, a, b, c, d).

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)