LINUX.ORG.RU

Избранные сообщения alienclaster

Ищу шедулер для питона, умеющий очень много задач

Форум — General

Задача такая:
Распределить случайным образом десяток тысяч задач в рамках времени около часа и чтобы они запускались по расписанию без опозданий.
Задачи динамически генерирующиеся , поэтому просто крон не подойдёт.
Пробовал appscheduler (https://apscheduler.readthedocs.io/en/stable/index.html) - не справляется, опаздывает. Есть ли что-то что умеет запускать четко по времени?

 ,

Qwentor
()

Вопрос про гигиенические макросы

Форум — Development

Реализация у меня - guile. Мне интересен аналог вот такого гипотетического макроса на CL:

(defmacro define-handler (name
                          ;; далее идут опции кодогенерации
                          ;; обычно это так называемый plist
                          ;; где значения у ключей это символы, T, NIL
                          (&rest code-options)
                          &body code)
  (let ((binds
          ;; тут довольно сложная обработка опций, где я частенько пользуюсь
          ;; и CLOS
          (handle-code-options code-options)))
    `(let ;; тут let чисто для компактности, хотя результат обработки опций
         ;; может потребовать подключения let*, flet и прочего с приличным
         ;; количеством генерируемого кода
         ,(handle-code-options code-options)
       ,@code)))

Какие формы надо изучать, чтобы писать такое на гигиенических макросах?

 ,

ados
()

Что такое ООП

Форум — Development

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

Конечно, можно все-таки выделить характерную черту ООП — это семантика передачи сообщений. Остальное все в тумане...

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

Потому что ответ лежит на поверхности, поэтому его никто не видит.

Ответ заключается в том, что любая другая парадигма — есть частный случай ООП, в том смысле, что может быть выражена в терминах ООП, но не наооборот. Само название ООП тут не слишком удачно, кстати, поскольку объект в ООП — это скорей субъект. Карл Хьюитт придумал гораздо более подходящее название — акторы.

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

Простой пример. Возьмем подстановочную модель, ака ФП. Центральным объектом данной парадигмы является функция. С точки зрения ООП, функция — это актор. Например, у нас есть функция, которая имеет такой вид sum(1, 2) // 3 Мы могли бы выразить это как-то так

Sum = new Function
Sum.code = a + b
theSum = new Sum 
theSum.setArguments(a: 1, b: 2)
theSum.call

Это некоторая декомпозиция, которая нам показывает, что функция — это просто сахар объекта.

То есть, грубо говоря, если мы берем некоторую полноценную ООП/Акторную семантику, и ограничиваем ее выразительные возможности, мы получаем на выходе другую парадигму. Такие вот дела:)

 ,

somequest
()

Вопрос про обобщенные функции

Форум — Development

Пишут, что это необычайно мощная весчь. Только я вот чего не пойму. Что если я вместо такого

(defgeneric collide (x y))

(defmethod collide ((x asteroid) (y asteroid))
  ;;астероид сталкивается с астероидом
  )

(defmethod collide ((x asteroid) (y spaceship))
  ;;астероид сталкивается с космическим кораблем
  )
;;......
Напишу просто функцию, что-то вроде
(define (collide x y)
 (cond
   ((and (instance? x asteroid) (instance? y asteroid)) 
     ("астероид сталкивается с астероидом"))
   ((and (instance? x asteroid) (instance? y spaceship))
     ("астероид сталкивается с космическим кораблем"))
 ;;;..............
       ))
; И вызываю так
(collide asteroid asteroid)

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

Полиморфизм (времени выполнения) это сердце ООП. В этой связи, как ни странно, квинтэссенция ООП это мультиметоды

.

Я не спорю, я, вероятно, чего-то не понимаю. Пока я вижу только небольшой сахарок над «функция + ветвление». Хотелось бы увидеть пример применения, который не может быть выражен банальной конструкцией типа «функция + ветвление».

Иными словами, в чем сила?

Спасибо.

 , , ,

somequest
()

ido vs helm

Форум — Development

Использую ido + ido-flx + smex, но поглядываю на helm (много восторженных отзывов). Раза два пытался переползти, возвращался на ido. Есть ли для helm что то более вменяемое чем helm-smex? Какие преимущества у helm?

 

robotron5
()

ООП без множественного наследования.

Форум — Development

А не кажется ли вам, господа, что, сабж попахивает противоречием. Например, Вася работает слесарем, а в свободное время играет в футбол. В множественном мы просто наследуем его от футболиста и слесаря, а как без него это выразить? Миксины я не беру в расчет, они в Ъ не катят. А как еще? Никак.

Допустим, я насколько знаю, в Смоллтоке нет сабжа. Я честно говорю, уважаю Алана Кея, он говорит много правильных вещей, его позиция и взгляды мне близки. Но тут ИМХО, он сделал ошибку. Как вы считаете?

 ,

javaQest
()

TeX: прошлое, настоящее и будущее

Новости — Open Source
Группа Open Source

Аксакал и активист TeX-движения Франк Миттельбах написал небольшое эссе на тему недостатков изначального TeX с анализом ситуации и возможных путей их решения, а также указаний на уже принципиально разрешённые проблемы.

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

>>> Подробности

 ,

Evgueni
()

Посоветуйте упоротых игр

Форум — Talks

Сегодня увидел трейлер Revenge of the Sunfish 2:

https://www.youtube.com/watch?v=lOaacVZDejk

Вспомнилась LSD на ps1, Yume Nikki, Neko Mario, немного квестов - Полная Труба, Neverhood, Ядерный Тидбит, Demonophobia.

Посоветуйте чего-нибудь еще из этого направления.

 ,

aidan
()

Классификация операторов в Lisp

Форум — Development

Разработчики Common Lisp не зря поменяли традиционный термин «специальная форма» на «специальный оператор». Например, выражение как целое (quote a) является специальной формой, а quote обозначает специальный оператор. Необходимо еще немного прояснить терминологию. Итак, оператор — любой объект, который может находится в голове формы. Ниже представлена классификация операторов:

                           Операторы
             _________________/\__________________________________________
	    |                              |                              |
         Функции                Специальные операторы                  макросы
       ____/\____              ____________/\________________ 
      |          |            |                              |
 встроенные   замыкания     Примитивные                 реификаторы
                        специальные операторы       ________/\_________
                                                   |                   |
                                               частичные             полные

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

В соответствии с этой теорией должен быть изменен apply/eval интерпретатор:

(eval выражение окружение продолжение)
(apply-operator оператор аргументы окружение продолжение)
(apply-function функция аргументы продолжение)

Функция eval лишь вычисляет голову формы, а результат (оператор) классифицируется функций apply-operator, которая либо передает управление функции, которая реализует соответствующий специальный оператор, либо функции apply-function. Кроме того, что специальные операторы применяются к невычисленным аргументам (cdr формы), они еще имеют доступ к полному контексту вычислений. Макросы не имеют такого доступа, поэтому, я не классифицирую их как специальные операторы. Функции не имеют доступа к лексическому окружению момента вызова. Следовательно такие функции как set из CL должны рассматриваться как нарушение теории и исключены из Лиспа. Функция set может быть, но только если она будет принимать дополнительный аргумент: (set символ значение окружение).

Частичные реификторы. nlambda получает только cdr формы, vau — cdr формы и окружение.

Полные реификаторы. В работах Brian Cantwell Smith были предложены 3-х аргументные fexpr'ы. В реификаторы Смита передаются не только сырые аргументы и окружение, но и продолжение. Полные реификаторы неотличимы от встроенных специальных операторов.

 

komputikisto
()

Два подхода к контекстам

Форум — Development

В этом посте я собираюсь рассмотреть различия в объектной модели Smalltalk и CLOS и как эти модели связаны с понятием контекста. Поклонники статической типизации могут не читать. С открытием CLOS возникли споры о том, CLOS — это что? ООП или не ООП? Становление новой науки неизбежно приводит к терминологическим спорам. ООП — термин расплывчатый и ИМХО, его следовало бы избегать. Как CLOS, так и Smalltalk реализуют одну важную фичу — ad hoc полиморфизм. Эта фича крайне важна для программирования, т.к. позволяет настраивать уже существующий код без изменения его исходного текста. Модель Smalltalk имеет ограниченный ad hoc полиморфизм, т.к. фактически позволяет производить диспетчеризацию лишь по одному аргументу. Однако, кроме ad hoc полиморфизма есть еще одна вещь, связанная с ООП — инкапсуляция. Итак, кратко опишем две ОО модели:

  • Инкапсуляция и ad-hoc полиморфизм (Smalltalk).
  • Ad-hoc полиморфизм без инкапсуляции (CLOS).

Далее я покажу, что эти два подхода противостоят друг другу. В Smalltalk объект — самодостаточная сущность. Чтобы распечатать объект (получить его внешнюю репрезентацию) необходимо послать объекту соответствующее сообщение. Это означает, что внешняя репрезентация объекта зависит только от него, и в минимальной степени зависит от внешнего контекста вызова. В CLOS внешняя репрезентация объекта целиком и полностью зависит от текущей обобщенной функции print-object. Теоретически у одного экземпляра Lisp системы может быть много различных обобщенных функций print-object.

Обычно естественные языки имеют лишь одно текстовое представление. Это не так для иероглифических языков, и скорее всего мы придём со временем к ситуации, когда один и тот же язык будет иметь множество проекций на текст. К этому же идет и эволюция языков программирования. Так, в Perl 6 функция load принимает на вход грамматику, которая описывает Perl 6 и написана на Perl 6. Далее свойство независимости от внешнего представления мы будем называть синтаксической абстракцией. ЯП, наиболее полно поддерживающий синтаксическую абстракцию — Lisp. Программы на Lisp записываются в виде S-выражений, но скобки тут нужны только для того, чтобы указать ридеру структуру. В Lisp текстовая репрезентация программы называются выражениями, а вычислитель работает не с выражениями, а с формами. Термин форма подчеркивает абстрактную природу вычислителя. Я ранее уже писал о том, как можно усилить синтаксическую абстракцию символов в Lisp.

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

Контекст — крайне важное понятие. Игнорирование контекста и абсолютизация понятий приводит к проблемам. Как определить тип объекта? Широко известен спор между Платоном и Диогеном. «Человек, - сказал Платон, - это двуногое животное без перьев». Тогда Диоген ощипал петуха и со словами: «Вот твой человек». Платону пришлось сделать уточнение: «Двуногое животное без перьев и имеющее ногти». Понятно, что тип объекта зависит от наблюдателя. Языки программирования начали с простой идеи — к каждому объекту прилеплен бейджик с его типом. В Smalltalk человеком является тот, кто на вопрос «ты кто?» отвечает — человек. Какой может быть типизация, которая учитывает контекст? Она носит название предикатная типизация. Еще иногда эту идею называют утиной типизацией. В этом подходе тип объекта зависит от того, кто и какие вопросы задает объекту. Платон может определить человека по отсутствию перьев и наличию ногтей, а Диогену нужен фонарь, чтобы определить, кто есть человек.

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

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

 , ,

komputikisto
()

Покер планирования, кто использует?

Форум — Development

Сегодня узнал, что такая штука существует. Кто не в теме - можете почитать в википедии. Может кто-то из тут присутствующих использует на практике? Расскажите, что думаете по этому поводу.

 , ,

Deleted
()

SCRUM в сфере разработки ПО

Форум — Development

Кто нибудь понимает нафиг это надо и реально использует? Все эти итеративные, спиральные, каскадные модели? Или чем груминг беклога отличается от обзора итогов спринта?

Как я понимаю знание этих методик обязательно для прожект-менеджмента и тим-лидера.

 

Liz812
()

Liskov substitution

Форум — Development

Это про что? Зачем это нужно в С++?

 

Liz812
()

Какие ЯП вас в своё время впечатлили и чем?

Форум — Development

С кратким комментарием — что именно произвело впечатление. Вот меня, например, в следующем порядке поразили:

1) Haskell в институте — системой типов, по сравнению с другими языками казалось неземной красотой, что можно написать всякие Show a, Num a.

2) Prolog тоже в институте — первый опыт настоящей декларативности — пишешь не то, что нужно делать, а что нужно получить и доступные для этого действия. Описал задачу, а система сама все решила.

3) Forth — тем, что самый мощный язык может быть написан в несколько сотен строк.

4) Io и Lua — Io чистотой и элегантностью концепции, а Lua тем, что интерпретатор мощного (на уровне JS) и простого языка может ещё и быстро работать.

5) Lisp — дата-код-дата, полноценная среда для вычислений в компайл-тайме и рантайме.

Ну и по-мелочи:

1) Python — 2D-синтаксисом.

2) colorForth — цветной синтаксис.

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

4) D — тем, что из C++, в принципе, мог получиться нормальный язык.

5) Pascal ещё в школе, тем что можно было работать с динамической памятью, использовать арифметику указателей и вообще все эти двусвязные списки и т.п.

Может потом ещё что вспомню...

 

y-combinator
()

Как юнит-тестить распределенные проприетарные системы?

Форум — Development

Вот допустим у вас есть клиент, которого разрабатываете вы (ваша команда). И есть сервер, который разрабатывает заказчик. Сервер разрабатывается «как есть», без всяких прототипов и симуляторов. Вам доступны только спеки протокола уровней с сессии до приложения нашей всеми любимой модели OSI/RM, причем, не всегда исчерпывающие, периодически в реализации протоколов находят баги, которые фиксят. Нет контроля за тем, какие данные выдает сервер. Если нужно покрыть определенную часть кода, нужно связываться с заказчиком и просить «а вот такие вот данные отправьте, пожалуйста».

Как в таких условиях можно выработать хоть сколько внятную стратегию тестирования? Получается только «попробовал по спекам реализовать клиента вот так вот -> протестировал -> фейл -> забагрепортил проблему заказчику -> пофиксил. Т.е. нужен специальный чел, который будет дописывать юнит-тесты постфактум, когда проблема уже решена. Т.е. ни о каком test-driven-development при таком подходе к разработке не может идти речи. Или?

 , ,

seiken
()

Racket v6.0

Новости — Open Source
Группа Open Source

Новая версия 6.0 Racket доступна уже сейчас! Racket — мультипарадигменный язык программирования общего назначения, принадлежащий семейству Lisp/Scheme.

Racket 6.0 вышел с обновлённой пакетной системой (уже доступны сотни пакетов).

Изменения в пакетной системы с момента беты (Racket 5.3.4):

  • Можно использовать github репозитарий как пакет.
  • Новый интерфейс для пакетного менеджера.
  • Сама сборка Racket была разделена на 200 пакетов (присутствует минимальная сборка 1/10 от полной).
  • Действия перед сборкой пакета — компиляция байткода, сборка документации.

Другие изменения:

  • Улучшена HTML документация.
  • Включёно в документацию официальное руководство по стилю оформления.
  • JIT-компилятор поддерживает ARM архитектуру.
  • Поддержка retina на Mac.
  • Производительность компилятора Typed Racket улучшена на 50% на некотором числе программ.
  • Новый профайлер для контрактов сообщает как долго проверяются контракты.

>>> Подробности

 

anonymous
()

Релиз стабильной версии Ninamori CMS

Новости — Open Source
Группа Open Source

Выпущена стабильная версия системы управления контентом веб-сайтов Ninamori CMS, ранее известной под названием MLUG CMS. Старое название проекта было решено сменить на новое, более анимушное.

Система распространяется на условиях GNU AGPL третьей версии. Все остальные условия не менялись, но для простоты и удобства были вынесены из основной лицензии. Текст основной лицензии теперь является точной копией текста лицензии с сайта GNU.

Главные Фичи:

  • Основана на свежей версии Django с долговременной поддержкой и минимумом зависимостей.
  • Не делает лишнего. Никаких модных кнопок «сделать охренительный сайт», сайт придётся делать как положено.
  • Приложение для рассылки почты подписчикам с подтверждением, отпиской, настройками, списками рассылок и отложенной отправкой.
  • Приложение голосований с инновационной системой жетонов для доступа к приватным голосованиям.
  • Генераторы QR-кодов с жетонами для подписки на приватные рассылки и для приватных голосований.
  • Всё ещё переполнена добротой и любовью.
  • Полностью переведена на русский и английский языки и готова к переводу на любые другие.
  • В качестве отступов в коде не используются пробелы (за исключением некоторого заимствованного кода, до которого не добрался рефакторинг).
  • Написана и поддерживается лично мной в свободное время, коммерческая поддержка для серьёзных организаций возможна.

Новые фичи с прошлой новости на ЛОРе:

  • Нормальный requirements.txt вместо текстового описания.
  • Readme теперь написан на Markdown.
  • Шаблоны вынесены в отдельный репозиторий, картинки и прочая статика вынесены из репозитория в файловый архив.
  • Настройка цветовой схемы шаблона вынесена в конфиг.
  • Поддержка заголовка HTTP_X_REAL_IP.
  • Полноценная поддержка мультиязычного контента (пока кроме голосований).
  • Использованные для голосования жетоны теперь хранятся в базе данных и выводятся скрытым элементом рядом с опцией в голосовании.
  • Теперь есть приватные почтовые рассылки с доступом по приглашениям.
  • Соответствие писем стандарту RFC2822.
  • Облагорожен код псевдошаблонизатора, теперь используется джанговский шаблонизатор везде где надо.
  • Ключ доступа к управлению подпиской теперь можно поменять.
  • Сообщения теперь не обязательно составлять в четырёх экземплярах (HTML, текст, русский, английский). Если каких-либо вариантов нет, будет использован ближайший подходящий.
  • Конкурентная блокировка страниц в панели администрирования (спасибо Stijn Debrouwere за идею и начальную реализацию).
  • Приложение обратной связи.
  • Теперь можно добавлять произвольные ссылки в меню сайта.
  • Ссылки и страницы теперь сортируются не как выдаст база данных, а в заданном администратором порядке.
  • Очень много всего починено, ещё больше реорганизовано и написана большая куча тестов. Теперь тесты покрывают почти весь код.

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

Демо: https://mlug.ru

>>> Репозитариум

 , , ,

Goury
()

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

Форум — Development

Доброй пятницы, ЛОР.

Вопрос в первую очередь тем, кто погружался в исходники компиляторов: gcc, clang, rustc, fpc, go… Используют ли они универсальные инструменты для лексического анализа и разбора — все эти flex, bison и др., которые рекомендуют учебники?

Или же там для разбора исходников написано что-то своё, более низкоуровневое?

И второй вопрос — что посоветуете человеку, который хочет что-то вытаскивать из написанного людьми (*) кода на C или C++? Пойти по классике и упороться flex-ом или?..

В первую очередь интересен первый вопрос, особенно в части gcc и clang. Жду рассказов людей, которые туда погружались и выплыли. :)

(*) - так-то понятно, что можно повесить вывеску «принимается только код, обработанный бьютифаером» и по-бырому сделать «парсер» на регулярках, а то и вручную. И «для себя» и даже для небольшого коллектива это будет вполне нормальное инженерное решение, даже в чём-то юниксвейное. А вот если задаться целью сделать как следует…

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

castxml globals.cpp --castxml-gccxml -o ./out.xml -I ../core -I /usr/include/qt4

Upd2: gcc-xml, предшественник CastXML, тоже поддерживает ключ -I, но в имевшемся у меня мане он не описан. Выходной файл в этом случае задаётся ключом -fxml=...

Всем спасибо за помощь.

 , ,

hobbit
()

Более лучший Лисп

Форум — Talks

Что бы вы изменили и каким образом в своем любимом/нелюбимом диалекте Лиспа, чтобы он стал ещё лучше? Расскажите обо всех своих грязных фантазиях.

Лиспосрач (и те два унылых анонимуса) не приветствуется.

Перемещено tazhate из development

 

y-combinator
()

[scheme][haskell][oop][fp] Мысли вслух

Форум — Development

Была на ЛОРе такая тема — [Haskell] простой вопрос. Хотелось бы немножко её развить и высказаться на тему предпочтения того или иного языка при изучении ФП (графомания mode on :)).

У Scheme есть довольно давняя история использования в качестве подопытного языка в курсах изучения ФП. Я не знаю чем это вызвано, но факт остаётся фактом — есть известный курс у MIT (или был?) и разные полезные книжки — SICP, HTDP, PLAI, OOPLAI, которые обычно и советуют читать если нужно ознакомиться с ФП.

Касательно SICP — одним из сторонников использования ML для целей изучения ФП была написана статья (http://www.cs.kent.ac.uk/people/staff/dat/miranda/wadler87.pdf) в которой достаточно хорошо освещены некоторые недостатки Scheme. Если говорить про Haskell, то тут всё так же. Далее, по пунктам (опуская кое-что из того что уже было в той статье).

Более явный синтаксис

Вместо

(define (foo x y z)
  (if (> (+ x (* y z) 1) 7) (print (+ x y)) (print (- x y))))

будет

foo x y z = if x + y * z + 1 > 7 then print $ x + y else print $ x - y

при этом по-прежнему можно написать выражение в префиксной форме:

(if' ((>) ((+) x ((*) y z) 1) 7) (print ((+) x y)) (print ((-) x y)))

почти как в Scheme. То есть, кроме префикса также есть (расширяемый пользователем) инфикс (в том числе функции вроде ($) и (.) позволяющие в некоторых случаях опускать лишние аргументы у функций и некоторые скобки в выражениях) и разные специальные формы (вроде if, let, лямбды и т.п.). Во всём что не касается макросов это более удобно. S-выражения обретают свой особый смысл только когда доходит до их цитирования:

'(if (> (+ x (* y z) 1) 7) (print (+ x y)) (print (- x y)))

и разбора с целью написания макросов. Тем не менее, для изучения именно ФП эта возможность незначительна (ФП не про макросы, в SICP и HTDP не ни слова про макросы, в PLAI есть только немного, в OOPLAI — побольше). Про то как правильно (ну, _правильно_, то есть без использования s-выражений) организовывать символьные вычисления (вроде дифференцирования из SICP) также расказывается в упомянутой статье.

Каррированные функции

Такое определение, например:

(define add
  (lambda (n)
    (lambda (m)
      (+ m n))))

заменяется простым

add = (+)

так как все функции уже каррированные (позволяют частичное применение). Фактически, в хаскеле функция с n аргументами сразу задаёт n разных функций (выбор конкретной функции осуществляется во время компиляции и не имеет эффекта во время выполнения). Некаррированные функции это функции от кортежей (те и другие переводятся друг в друга с помощью ФВП carry/uncarry).

Частичное применение, секции, pointfree запись

add2 = (+ 2)

add2 5
7

вместо

(define add2 (add 2))

(add2 5)
7

Мутабельные замыкания

Это единственная вещь которая есть в Scheme и которую можно не увидеть сразу в хаскеле (и про неё нет в той статье). Тот тред был как раз про них. Чтобы прояснить этот момент, ниже приводятся некоторые примеры из OOPLAI и их аналоги на хаскеле.

Простейший вариант:

(define counter
  (let ((count 0))
    (lambda ()
      (begin
        (set! count (add1 count))
        count))))

(counter)
1
(counter)
2

аналог:

counter = (=~ (+ 1)) <$> new 0

тут (=~ (+ 1)) играет роль мутирующего «метода», а (new 0) — мутируемого «объекта», (<$>) — применение «диспетчера» (тут — просто единичный анонимный «метод»). Вся конструкция функториальная (не монадическая). Использование:

ctr <- counter      -- Инстанцирование класса counter объектом ctr.
ctr                 -- Применение единственного метода ((=~ (+ 1)) который).
1                   -- Результат.
ctr                 -- Снова.
2                   -- Другой результат.

Чуть более сложный пример:

(define counter-
  (let ((count 0))
    (lambda (cmd)
      (case cmd
        ((dec) (begin
                 (set! count (sub1 count))
                 count))
        ((inc) (begin
                 (set! count (add1 count))
                 count))))))

(counter- 'inc)
1
(counter- 'dec)
0

Для начала определим имена методов dec и inc:

data CounterMethod = Dec | Inc

это не символы и не строки (так что код не будет ill-typed как в примере на Scheme, иначе говоря, применение несуществующего метода не пройдёт компиляции). И теперь функцию:

counter' = dispatch <$> new 0
  where dispatch obj Dec = obj =~ flip (-) 1
        dispatch obj Inc = obj =~ (+ 1)

тут dispatch играет роль диспетчеризирующей функции которая получает объект (obj) и имя метода, а затем изменяет объект (как того требует метод). (new 0) — начальный объект.

Пример:

ctr <- counter'     -- Инстанцирование класса counter' объектом ctr.
ctr Inc             -- Применение метода Inc на объекте ctr.
1
ctr Inc
2
ctr Inc
3
ctr Dec             -- Тут уже метод Dec.
2
ctr Dec
1
ctr Dec
0

Тут применение (ctr Inc) весьма похоже на каноничное, через точку, obj.method и является, по сути, посылкой сообщения объекту.

Третий пример:

(define stack
  (let ((vals '()))
    (define (pop)
      (if (empty? vals)
          (error "cannot pop from an empty stack")
        (let ((val (car vals)))
          (set! vals (cdr vals))
          val)))
    (define (push val)
      (set! vals (cons val vals)))
    (define (peek)
      (if (empty? vals)
          (error "cannot peek from an empty stack")
        (car vals)))
    (lambda (cmd . args)
       (case cmd
         ((pop) (pop))
         ((push) (push (car args)))
         ((peek) (peek))
         (else (error "invalid command")))))) 

(stack 'push 1)
(stack 'push 2)
(stack 'pop)
2
(stack 'peek)
1
(stack 'pop)
1
(stack 'pop)
; cannot pop from an empty stack

аналогично:

data StackMethod = Pop | Push | Peek

stack = dispatch <$> new []
  where
    dispatch x Pop _  = get x >>= (x =~ tail >>) . return . head
    dispatch x Push y = x =~ (y :) >> return y
    dispatch x Peek _ = head <$> get x

и пример:

stk <- stack :: IO (StackMethod -> Int -> IO Int)
                    -- stack это параметрически-полиморфный класс, в данном
                    -- случае берётся его спецификация когда элементы стека
                    -- имеют тип Int (можно взять что-то более общее).
                    -- Этот специфичный класс инстанцируется объектом stk.
mapM_ (stk Push) [1, 2, 3]
                    -- (stk Push) это применение метода Push на объекте stk,
                    -- с помощью ФВП mapM_ оно производится для всех элементов
                    -- списка.
repeat 4 $ stk Pop __ >>= print
                    -- 4 раза вызывается метод Pop, элементы печатаются.
                    -- Последний раз вызывается исключение (так как стек пуст).
3
2
1
*** Exception: Prelude.head: empty list

тут точно так же — в StackMethod перечислены нужные методы для стека, функция stack определяет класс, то есть объединение данных и функций с нужным поведением, она имеет тип IO (StackMethod -> a -> IO a), то есть принимает метод, элемент стека и возвращает элемент стека (в IO, мутабельно), сама функция в IO (вся структура данных ведёт себя мутабельно).

Дальше в OOPLAI начинают использовать макросы для придания более удобоваримого вида этим конструкциям. В настоящем (ну, _настоящем_ :)) ФП этого не нужно — примитивные ООП конструкции объединяющие данные и функции выглядят естественно и так, и являются частным случаем использования ФВП, IO и ADT с паттерн-матчингом (последние два — для удобства). Использование макро-системы может иметь смысл разве что если действительно нужно реализовать сложную ООП систему (например, со множественным наследованием и изменяемой иерархией классов, впрочем, обойтись одними функциями тут тоже можно, просто придётся делать больше механических действий).

Ещё пример:

-- | Данные — конструктор и аккессоры.
data Point = Point
  { x :: Double
  , y :: Double
  } deriving ( Show, Eq ) -- ad-hoc перегруженные функции.

-- | Методы привязываемые к данным (это уже _не_ ad-hoc перегруженные функции).
data PointMethod = Pos | Mov

-- | Класс (= функция), объединяющий данные и методы.
pointClass :: Double -> Double -> IO (PointMethod -> Double -> Double -> IO Point)
pointClass initX initY = dispatch <$> new (Point initX initY)
  where
    -- | (Динамический) диспетчер по методам. Он принимает объект (Var Point),
    -- имя метода (PointMethod, т.е. статическое, в данном случае, сообщение)
    -- и два опционных аргумента для методов (Double -> Double). Эту функцию
    -- можно помещать непосредственно в Point.
    dispatch :: Var Point -> PointMethod -> Double -> Double -> IO Point
    dispatch obj Pos _ _ = get obj
    dispatch obj Mov x y = obj =: Point x y
pnt <- pointClass 2 4         -- Инстанцирование класса pointClass объектом pnt
                              -- с начальными значениями полей 2 и 4.
:t pnt
pnt :: PointMethod -> Double -> Double -> IO Point
pnt Pos __ __                 -- Вызов метода Pos на объекте pnt.
Point {x = 2.0, y = 4.0}
pnt Mov 3 5                   -- Вызов метода Mov.
Point {x = 3.0, y = 5.0}
pnt Pos __ __                 -- Положение изменилось:
{x = 3.0, y = 5.0}

Нужно заметить, что это всё довольно примитивные конструкции (простые функции и IO). В случае использования ADT для имён методов получится динамическая диспетчеризация с фиксированным набором методов (well-typed), если же переписать функцию dispatch с завязкой на хэш-табличку (которая должна быть переменной в данных класса), то будет динамическая диспетчеризация с пополняемым набором методов и перегруженными методами (одни и те же сообщения можно посылать разным инстанцированным объектам, их dispatch будет их искать в хэш-таблице и обрабатывать, это уже ill-typed, то есть с исключениями вида «нет такого метода»). Разные прочие вещи вроде наследования и self точно также можно изобразить (аггрегация данных, представление иерархии классов в данных (в переменной или нет, в зависимости от возможности менять иерархию) и более сложная функция dispatch), но как-то не интересно.

P.S.

Код на хаскеле использует такие упрощения:

import Prelude hiding ( repeat )
import Data.IORef
import Control.Applicative
import Control.Monad

type Var a = IORef a

new :: a -> IO (IORef a)
new = newIORef

get :: IORef a -> IO a
get = readIORef

(=~) :: IORef a -> (a -> a) -> IO a
x =~ f = modifyIORef x f >> get x

(=:) :: IORef a -> a -> IO a
x =: x' = x =~ const x'

repeat :: Monad m => Int -> m a -> m ()
repeat = replicateM_

__ :: a
__ = undefined

P.P.S.

OOP / ООП в контексте данного поста — объектно-ориентированное программирование в духе объединения данных и процедур, то есть в духе C++, Java, Python и т.п. _Не_ ООП в духе классы = структуры, методы = перегруженные функции, наследование = схемы агрегаций и распространения методов (как это в CLOS и классах типов Haskell).

 , ,

quasimoto
()