LINUX.ORG.RU

ООП. Как представляете себе идеальную реализацию?

 , ,


2

3

Я знаю как минимум 4 слабо совместимых друг с другом понятия ООП:

  • С++: класс = неймспейc, вызов метода через точку,
  • CLOS: класс = идентификатор + наследование, тело метода определяется по классу всех параметров (а не только первого), методы доопределяются модификаторами :after :before :around.
  • Racket: класс = first-class object, как и функция, соответственно, может доопределяться по месту и не иметь имени.
  • Haskell: классы типов как наборы операций над типам (которые можно считать эквивалентными классам других языков)

Хотелось бы узнать, какой подход вам ближе и почему. А также, можно ли из упомянутых видов получить некую «идеальную реализацию» и какой синтаксис мог бы быть у такой реализации.

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

Само-то понятие «подтип в хаскеле» откуда?

Не из хаскеля. Существует предметная область. Объекты. Мы их для удобства объединяем в множества (типы, классы). Иногда на подмножестве существующего множества надо уточнить операцию (например, множестов многоугольников, подмножество треугольников, операция — вычисление площади). В хаскеле в этом случае создаётся новый тип, на нём определяются уточнённые операции. Внутри языка понятия подтип в хаскеле нет (насколько я знаю).

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

Понятно, ну здесь default-метод можно

Пожалуй не то). Можно, например, ввести класс многоугольников площадь, будет метод polyArea, instance [Coord], instance Triangle (data Triangle = Triangle Coord Coord Coord). Ну и: instance Poly a => Figure a where ... area = polyArea

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

Если мне потребуется сделать тип Многоугольники,

Иногда на подмножестве существующего множества

Если _подмножество_ - значит речь уже идет не о типе, а о классе.

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

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

так «не смогу указать» или «новый тип с уточненными операциями»?

понятия подтип в хаскеле нет

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

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

Простейший пример из Racket я приводил. Есть класc window.Есть пара десятков преобразователей decorared, top-level, 3-button, и т.д., которые к этому окну добавляют всякие кнопки/декораторы/поведение/... .

Mixin'ы это называется.

А что фабрика будет возвращать? У каждой комбинации флагов будет своя функция Render в объекте. Реально варианта 2: или это на самом деле один класс, а всё поведение запихано в одну большую функцию. Или в зависимости от флажков выбирается один из 2^20 (по количеству флагов) классов и возвращается. Второй вариант явно ужасен при количетве флагов больше трёх, первый заставляет лезть внутрь класса, если пользователь библиотеки придумал свой декоратор и хочет его добавить.

Зачем? Компоненты можно добавлять отдельно. Типа такого:

class Window implements Renderable ... {
    ...
    public Window (Renderable subcomponents) { ... }
    ...
    public void add(Renderable component) { ... }
    ...
    public void render() {
        ...
        for (Renderable component : subcomponents) {
            component.render()
        }
        ...
    }
}

В общем, примерно как в JavaFX.

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

Ну не скажи, «препроморфизмы» я (никто?) в дикой природе не встречал, а ленсы снимают необходимость написания 90% кода типичного проекта.

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

написания 90% кода типичного проекта

Таки 90%? Посмотрел пакеты в hackage, что непосредственно зависят, действительно прилично появилось: 46.

(Допустим, возможностей поболе) но разве настолько удобнее data-accessor / data-lens / fclabels; и не бывает, что приходится «разбираться» с class-constaints?

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

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

Не совсем «наподобие», но можешь посмотреть на Vaadin.

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

Если мне надо в Racket сделать окно с нужными предбразованиями я просто создаю экземпляр класса, например (decorated (top-level (3-button window%))). В java, насколько я знаю, приходится писать 2^20 классов

Разве композиция тут не поможет?

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

data-accessor / data-lens / fclabels

Не комбинируются с помощью (.) из Prelude, не умеют полиморфный апдейт.

К тому же lens не столько про ленсы (не смотря на название), сколько про родственные структуры вроде Traversal (очень удобное ползание по структурам данных) и Prism (first-class конструкторы) (и их Indexed варианты). В результате куча кода уже написана за автора очередной библиотеки, остается ее только скомбинировать.

Сообщения об ошибках обычно нерюхаемые, но в #haskell-lens всегда расскажут в чем дело.

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

Компоненты можно добавлять отдельно. Типа такого:

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

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

Разве композиция тут не поможет?

Если на стадии разработки класса window точно известны все места в которых будет вызываться код декораторов, то поможет. А если очередной декоратор должен работать как «вывести фон; вывести остальное окно; вывести поверх ещё что-нибудь», то уже никак: в декортаоре придётся добавлять методы renderBefore и renderAfter и менять render основного класса. Или при наличии декорации может меняться какой-нибудь другой метод (не render): снова надо менять код window и добавлять метод в декоратор.

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

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

Так я и отталкиваюсь от задачи. В задаче понятие подтипа есть. Ну а в хаскеле — да: «instance [Coord], instance Triangle (data Triangle = Triangle Coord Coord Coord)» как бы друг друга не знают. Есть только функция пребразования любого треугольника по месту в многоугольник, что позволяет для треугольника использовать операции многоугольника.

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

Не комбинируются с помощью (.) из Prelude

Есть свои комбинаторы, почему (.) - принципиально?

не умеют полиморфный апдейт

Есть сомнения, что сей апдейт так сильно нужен для (под-)структур, которые не являются функторами (both для (a,a) -> (b,b) если я правильно понял контекст).

К тому же lens не столько про ленсы

Понимаю что комбайн. Но когда интересовался, возникали сомнения, что код будет легко читаем.

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

Так я и отталкиваюсь от задачи. В задаче понятие подтипа есть.

Что за бред? Откуда в «задаче» подтип, когда это средство / часть ее решения (причем не единственное)?

Есть только функция пребразования любого треугольника по месту в многоугольник

И в чем проблема? Ф-ия то будт вызвана автоматически, ее даже прописывать не придется. А ваш «подтип» таки не разновидность monkey-patching? (когда instance перейдет в условие для area: if n == 3 ... else ...)

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

разновидность monkey-patching

В широком смысле весь ООП — разновидность monkey-patching.

Ну возьми в качестве класса типов Widget, а в качестве типов Window, Button, ImageButton (с преобразование в Button). Операция render у каждого своя, но ImageButton можно передать аргументом любой функции, которая принимает Button. Чем не классы?

Откуда в «задаче» подтип

Задача у меня в широком смысле: показать, что системой типов Хаскеля можно изобразить основные понятия ООП (методы, классы, наследование).

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

ну блин очередной X-Y тред, не надоело? Когда ж ты научишься отличать задачу от решения и искать методы решения задачи, а не методы выражения методов решения, которое ты придумал? В треде плохо все начиная с неопределения ООП (хотя бы основного формализма), продолжая описанием твоего видиния классов в haskell слабо соотносящегося с действительностью (hint если уж сравнивать классы с haskell с «привычными» из императивщины вещами, то близким будут интерфесы, постарайся продумать ситуацию заново с учетом этого), некорректностью определения подтипа (корректно это описать семантику его работы).

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

В широком смысле весь ООП

Хочется широкого смысла - есть расширения). Думается тот же IncoherentInstances можно прикрутить к примеру треугольника.

Ну возьми в качестве класса типов Widget, а в качестве типов Window, Button, ImageButton

Ну и зачем лезть со своим уставом? Смотрим на биндинг того же gtk и видим дополнительный слой абстракции: instance ButtonClass CheckButton. Причем он есть в самом gtk, то есть там посчитали разумным _выделить класс_.

Задача у меня в широком смысле: показать, что системой типов Хаскеля

Ой, а разве не «попытаться не допустить показать публике, что не очень разбираюсь в системе типов хаскеля»?

можно изобразить основные понятия ООП

А кто спорит, но как-то странно делать _прямую_ трансляцию.

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

почему (.) - принципиально?

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

Есть сомнения, что сей апдейт так сильно нужен

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

когда интересовался, возникали сомнения, что код будет легко читаем.

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

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

как-то странно делать _прямую_ трансляцию

Ну так топик про ООП, а не про систему типов Хаскеля. Её через классы/методы любого другого языка было бы изобразить ещё страннее.

Просто есть желание получить языконезависимый метод декомпозиции структуры предметной области. Вынес в заголовок методологии декомпозиции, которые мне встречались. Хаскелл упомянул, потому что ООП на него как-то ложится http://www.haskell.org/haskellwiki/OOP_vs_type_classes , но при этом идеология декомпозиции типов при написании изначально на Хаскеле совсем другая.

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

Ну так топик про ООП, а не про систему типов Хаскеля. Её через классы/методы любого другого языка было бы изобразить ещё страннее.

Вот. В этом-то и проблема: про ооп, но включен хаскель. И чтобы ответили те, кому хаскель ближе им придется ориентироваться вне привычных определений. Либо крестик, либо ооп.

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

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

По-моему, это вопрос терминологии. Система классов Racket с mixin/trait/augment настолько же далека от CLOS с defgeneric, насколько типы Haskell от классов C++. Но при этом все перечисленные схемы посзволяют классифицировать объекты и операции над ними, значит в этом контексте их можно сравнивать. Удобство классификации, гранулярность, расширяемость по видам объектов и операций.

monk ★★★★★
() автор топика

тайпклассы Haskell'я тут лишние, ибо вообще к ООП никакого отношения не имеют.

anonymous
()

Идеальная должна быть другой... И вы как-то вырываете из контекста. С++ предоставляет минимум возможностей, CLOS - гибкость и строгость, Racket - изящное простое и гибкое решение, Haskell - эм.. там нет ООП.

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

С++ предоставляет минимум возможностей

Про C++ уже было выше. Чего нет у остальных: перегрузка функций, класс как пространство имён (в нём есть типы, перечисления...), templates (неполные аналоги: макросы Lisp, тайпклассы Haskell).

И такой список «чего нет у остальных» можно написать про каждый из перечисленных.

Haskell - эм.. там нет ООП

http://www.haskell.org/haskellwiki/OOP_vs_type_classes — это по сути ООП. Близкое к C++ (ближе, чем к Racket, например). «Ходит и крякает» также.

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

То есть ООП - проблема? А не инкапсуляция etc, или может любые решение - автоматически становятся ООП?

Вангую ООПГМ.

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

Это в каких?

Очевидный пример:

> awk -P 'BEGIN {length=1}'
gawk: cmd. line:1: BEGIN {length=1}
gawk: cmd. line:1:              ^ syntax error
anonymous
()

Все зависит от сферы применения этого идеального ООП. Для меня идеальным ООП было бы такое:

  • Типы данных отделены от интерфейсов
  • Наследуются только интерфейсы
  • Один интерфейс может иметь несколько реализаций
  • Посредством одного типа данных можно реализовать много интерфейсов
  • Интерфейсы могут иметь невиртуальные методы с реализацией(для NVI)
  • Интерфейсы могут иметь дефолтные реализации методов
  • Виртуальные методы реализованы как указатели на функции(см. С++)
  • Для интерфейсов работают мультиметоды по типам аргументов(см. Open methods от Струаструпа)
  • Есть опциональная интроспекция(интерфейс помечается специальным атрибутом)
  • Возможно нужен синтаксический сахар для «атрибутов» в интерфейсах
  • Конкретные типы не имеют методов, т.е. структура данных + функции(которые можно перегружать статически). Инкапсуляция для них работает на уровне модулей и реализации интерфейсов.

Ну вот почему так не делают? Если кто-то может указать на недостатки - плиз, сделайте это...

Я, сишник, тогда бы использовал такое ООП. Иногда использую С++, т.к., в принципе, вполне годно, на фоне остального.

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

Ну вот почему так не делают?

Очень похоже на структирирование типов в haskell.

anonymous
()

CLOS часто только из-за дженериков и диспетчеризации используют.

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

А вообще, при наличии замыканий, можно в простых случаях и без CLOS обойтись.

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

только из-за дженериков и диспетчеризации

А там что-то ещё есть?

полную интеграцию в систему по части юзерских не-CLOS типов

Не типов а классов. Класс CLOS — по сути просто идентификатор. Для любого значения всё равно придётся делать поиск класса. Для любого объекта в CL класс уже определён. Зачем тебе ещё один класс-тип?

MOP ввести в стандарт нужно

closer-mop можно считать стандартом де-факто

А вообще, при наличии замыканий, можно в простых случаях и без CLOS обойтись.

В Racket вся система классов — всего лишь набор функций и замыканий.

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

А там что-то ещё есть?

Конечно.

Не типов а классов.

Не классов, а типов. У типов базового языка и CLOS разные корни, пересечение с CLOS есть только у базовых типов. Если deftype сделать, то болт.

closer-mop можно считать стандартом де-факто

Да-да, а SBCL - референсной имплементацией. В LW пользовал встроенный MOP, и в ус не дул, но при перетаскивании на другую платформу будут некоторые проблемы.

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

Да смотрел я.

В Go все не так :) Вообще ни о чем язык, еще и скорость почему-то как у явы.

На Rust пока посматриваю, но сыроват еще.

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

Go и тормознее Java бывает=)

Rust, кстати, тоже не шустрый. Может допилят еще.

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

Если deftype сделать, то болт.

Это намеренно. Классы имеют иерархию, типы — нет.

Если бы defmethod принимал deftype, то что делать в ситуации

(defmethod foo ((a (integer 0 10)))
  1)

(defmethod foo ((a (integer 5 15)))
  2)

(foo 8) = ?
monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Если бы defmethod принимал deftype, то что делать в ситуации

Numeric tower обычный, в чём проблема? Сначала 5-15, если call-next-method, то 0-10.

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

начиная от метода для двух классов

Полно реализаций

заканчивая тривиальным :before

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

«идеальным ООП» в рамках данного треда можем считать такой ООП, на который можно перенести любое API, описанное в вышеприведённых стилях.

Зачем?

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

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

UML.

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

Если так писать, то и в крестах сломается - foo из C закрывает все определения из B.

using B::foo;

Спасет отца русской демократии.

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

Как в UML нарисовать defgeneric? Как в нём же указать, что добавляется модификатор :around?

Мультиметоды - это не ООП, это просто динамическая диспетчеризация по значениям/типам аргументов функции.

Модификаторы - это просто сахарок, можно сделать отдельные методы.

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

Haskell'овские typeclass'ы

Они не имеют отношения к ООП.

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

При этом адепты Хацкеля, совершенно не стесняясь, впаривают нам следующее:

The advantages of using Haskell include: increased productivity, access to higher level of abstraction for better code reuse, reliability, and high performance on multicore processors.

Хотелось бы знать, за счёт чего достигается вышеперечисленное

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

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

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

почему так не делают?

Неудобно на практике? Представь себе сложную иерархию классов в каком-нибудь крестовом проекте(ну или даже на жабке) и переведи ее на описанные тобой рельсы. Ад.

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

Сначала 5-15, если call-next-method, то 0-10

Во-первых не tower, потому что 5..15 не входит в 0..10

Пусть даже предлагаешь для чисел сравнивать сначала по левому краю, а потом по правому (для определённости).

Так ведь типы бывают и такие:

(deftype positive-integer ()
  `(and integer (satisfies plusp)))

(deftype even-integer ()
  `(and integer (satisfies evenp)))

Их как будешь сортировать? 6 более чётное или положительное? Это я ещё до типов функций не дошёл.

monk ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.