но в реальности же тебе был нужен вовсе не ООП, а модульность, абстрагирование, инверсия контроля, конкатенирование (когда сложное поведение получается комбинацией независимых, отдельно разрабатываемых кусков)
Здравствуйте, dilmah, Вы писали:
D>но в реальности же тебе был нужен вовсе не ООП, а модульность, абстрагирование, инверсия контроля, конкатенирование (когда сложное поведение получается комбинацией независимых, отдельно разрабатываемых кусков)
В реальности мне нужен был подход позволяющий легко описывать окна и взаимодействие с ним. А вот эти ваши "модульность, абстрагирование, инверсия контроля, конкатенирование" мне на фиг не упали. Я тогда о них и не слышал. Идей из книги по виндовс мне более чем хватило. А когда все эти идеи я увидел в С++ мне не пришлось их объяснять (хотя они были сильно иные). Ну, а то что это назвали ООП-ом меня вообще не колышет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
A>Но в отношение ООП порядок строго обратный: 4, 3, 2, 1!
Предполагаю, что это основная причина одержимости распространенности ООП.
На мой взгляд, три главные причины распространения ООП (в порядке убывания значимости) следующие:
1. На этапе 4, с которого все начинается, все кажется предельно простым, а бенефитов при этом обещают целую кучу!
2. "Новое платье короля": фуу, да у тебя тут говнокодище, ты что, вообще не понимаешь ООП?!
3. Бенефиты таки есть (по крайней мере, для некоторого подмножества задач).
Здравствуйте, artelk, Вы писали:
A>1. На этапе 4, с которого все начинается, все кажется предельно простым, а бенефитов при этом обещают целую кучу! A>2. "Новое платье короля": фуу, да у тебя тут говнокодище, ты что, вообще не понимаешь ООП?! A>3. Бенефиты таки есть (по крайней мере, для некоторого подмножества задач).
Аналогия с шахматами, разбор партии:
Игрок с рейтингом ЭЛО где то в 500 объясняет новичку — "здесь надо не так а вот так"
Эло 1000 — "нет, это отстой и надо вот так"
Эло 1500 — "это отстой, ибо будет мат через 15 ходов"
Эло 2000 — "мата не будет, будет ничья после 10 ходов"
Эло 2500 — "нужно сдаваться, партия уже проиграна"
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, artelk, Вы писали:
A>>1. На этапе 4, с которого все начинается, все кажется предельно простым, а бенефитов при этом обещают целую кучу! A>>2. "Новое платье короля": фуу, да у тебя тут говнокодище, ты что, вообще не понимаешь ООП?! A>>3. Бенефиты таки есть (по крайней мере, для некоторого подмножества задач).
I>Аналогия с шахматами, разбор партии: I>Игрок с рейтингом ЭЛО где то в 500 объясняет новичку — "здесь надо не так а вот так" I>Эло 1000 — "нет, это отстой и надо вот так" I>Эло 1500 — "это отстой, ибо будет мат через 15 ходов" I>Эло 2000 — "мата не будет, будет ничья после 10 ходов" I>Эло 2500 — "нужно сдаваться, партия уже проиграна"
Впечатляет. Только я второй день пытаюсь понять, аналогия к чему это?
Можно для особо тупых разжевать?
Вот сюда смотреть: A>>>1. На этапе 4, с которого все начинается, все кажется предельно простым, а бенефитов при этом обещают целую кучу! A>>>2. "Новое платье короля": фуу, да у тебя тут говнокодище, ты что, вообще не понимаешь ООП?! A>>>3. Бенефиты таки есть (по крайней мере, для некоторого подмножества задач).
I>>Аналогия с шахматами, разбор партии: I>>Игрок с рейтингом ЭЛО где то в 500 объясняет новичку — "здесь надо не так а вот так" I>>Эло 1000 — "нет, это отстой и надо вот так" I>>Эло 1500 — "это отстой, ибо будет мат через 15 ходов" I>>Эло 2000 — "мата не будет, будет ничья после 10 ходов" I>>Эло 2500 — "нужно сдаваться, партия уже проиграна"
A>Впечатляет. Только я второй день пытаюсь понять, аналогия к чему это? A>Можно для особо тупых разжевать?
Аналогия про этапы 1 2 3 4 3 2 1 или как там было. Идейка в том, что понимание растет с опытом, при чем, как водится, растет не линейно а скачками, что естественно дял человека. И на каждом уровне пересматриваешь свои позиции.
У Гегеля это закон отрицания отрицания,т.е. движение по спирали.
Здравствуйте, artelk, Вы писали:
A>Здравствуйте, minorlogic, Вы писали:
M>>Если не путать ООП и ООД , тогда все ок. A>Т.е. можно писать код в ООП парадигме, и при этом дизайн системы может не быть объектно-ориентированным? Оригинально. A>Конечно же, ООП включает себя ООД.
А если поменять "изучение ООП" на "освоение ООП", возражения будут?
"Освоение" включает в себя развитие способности правильно применять ООП при разработке — т.е. чтобы достичь обещанных бенефитов.
И вот накопился у меня вопрос на текущем витке спирали.
Что же есть полиморфизм в ООП?
Из википедии:
Subtype polymorphism, often referred to as simply polymorphism in the context of object-oriented programming, is the ability to create a variable, a function, or an object that has more than one form.
Ага, это Subtype polymorphism. Только нифига непонятно.
Смотрим тут:
subtyping polymorphism (sometimes referred to as dynamic polymorphism) allows a function to be written to take an object of a certain type T, but also work correctly if passed an object that belongs to a type S that is a subtype of T (according to the Liskov substitution principle). This type relation is sometimes written S <: T. Conversely, T is said to be a supertype of S—written T :> S.
Хорошо, только причем тут виртуальные функции, про которые сразу вспоминают, когда заходит речь о полиморфизме ООП?!
Формально, ни причем. Т.е. если в языке есть виртуальные функции — то это уже расширение базовой концепнии ООП (как лямбды в ОО языке C#).
Получается, что под "наследованием" в ООП следует понимаеть исключительно наследование реализации!
Subtype polymorphism зачем-то считается отдельной формей полиморфизма (не ad hoc и не parametric, см. Contents тут).
Выходит, что Gaperton в своем великолепном посте
ошибался, сводя его к параметрическому полиморфизму.
По мне, так это частный случай Bounded parametric polymorphism-а с упрощенным синтаксисом (не нужно явно указывать "генерик" параметр). Но у ООП ведь должен быть свой, особый полиморфизм и свой, особый гуманитарный путь развития!
Здравствуйте, artelk, Вы писали:
A>Т.е. можно писать код в ООП парадигме, и при этом дизайн системы может не быть объектно-ориентированным? Оригинально. A>Конечно же, ООП включает себя ООД.
Легко!
Как известно, ООП — это замыкания для бедных.
Берём какой-нибудь ФП-проект, интенсивно использующий функции высшего порядка и параметрический полиморфизм.
Тупо транслируем ФВП в интерфейс с единственным методом, а параметрический полиморфизм — в интерфейс с набором методов-примитивов (в ООД это называется "паттерн Стратегия" и "паттерн Шаблонный Метод"; чорт, всё-таки ООД пролез сюда! окей, вычёркиваем полиморфизм; или не вычёркиваем, но обфускиваем, чтобы не было в явном виде ни Стратегий, ни Шаблонных Методов).
Вуаля.
Здравствуйте, artelk, Вы писали:
A>И вот накопился у меня вопрос на текущем витке спирали. A>Что же есть полиморфизм в ООП? A>Из википедии:
A>
Subtype polymorphism, often referred to as simply polymorphism in the context of object-oriented programming, is the ability to create a variable, a function, or an object that has more than one form.
<>
Начнём с самого главного: с принципа подстановки Лисков (LSP). Именно по нему проходит граница.
Ад-хок-полиморфизм отличается от параметрического и субтипов тем, что плюёт на LSP.
Как именно это наплевательство реализовано — компилятором ли (перегрузка функций С++, параметры шаблонов С++, классы типов Хаскелла), проверками в рантайме пользователем — printf, if(typeid(p)==...), if(dynamic_cast<T*>(p)), паттерн-матчинг Пролога и Эрланга — это совершенно неважно.
Главное, что набор "съедобных" типов задаётся пользователем и образует плоскую одноуровневую иерархию: "некий аргумент" <- "аргумент конкретного типа".
Параметрический полиморфизм основывается на полиморфизме деталей реализации.
Он не плюёт на LSP, а воспроизводит требования деталей. Если детали функции на субтипах — значит, и вся функция на субтипах и отвечает LSP; если детали только произвольные — значит, и вся функция произвольная.
Полиморфизм субтипов вводит ограничения. Между типами вводится иерархия произвольной глубины (даже если это не классы, а прототипы или диапазоны — только тогда следить за иерархией будет уже не компилятор, а пользователь).
Параметрически полиморфные функции обязаны быть всеядными по LSP — если принимаешь аргумент базового типа, то и любого его наследника примешь.
Переопределения функций (как бы, ad-hoc) — обязаны быть контравариантными по входам и ковариантными по выходам: не зауживать типы аргументов, кроме определяющих, и не расширять тип результата. Тут смысл такой, что у семейства перегруженных функций мысленно есть одна интерфейсная параметрически полиморфная — а она должна отвечать LSP.
Для единственного определяющего аргумента (того, который this) — есть и очень простая (и логически выстроенная!) схема диспетчеризации, и, соответственно, очень простая реализация (таблицы виртуальных функций).
Для нескольких определяющих аргументов (т.е. когда делаем мультиметод) начинаются всякие неоднозначности и страсти.
Мне кажется, именно из-за неоднозначностей мультиметоды и не попали в ООП изначально. А когда нужда в них стала появляться — тут-то и начались все эти фокусы с двойной диспетчеризацией (которая, на самом деле, одна из возможных схем).
Да, ещё один важный момент: для субтипов нет ограничения на количество и ассортимент типов, в отличие от ad-hoc, где количество заранее известно либо ограничено какими-то внезапными факторами (например, подгрузкой модуля, который знает, что делать с данным тэгом варианта).
И, в то же время, есть ограничение на иерархию, в отличие от параметрического.
Здравствуйте, Кодт, Вы писали:
К>Легко! К>Как известно, ООП — это замыкания для бедных. К>Берём какой-нибудь ФП-проект, интенсивно использующий функции высшего порядка и параметрический полиморфизм. К>Тупо транслируем ФВП в интерфейс с единственным методом, а параметрический полиморфизм — в интерфейс с набором методов-примитивов (в ООД это называется "паттерн Стратегия" и "паттерн Шаблонный Метод"; чорт, всё-таки ООД пролез сюда! окей, вычёркиваем полиморфизм; или не вычёркиваем, но обфускиваем, чтобы не было в явном виде ни Стратегий, ни Шаблонных Методов). К>Вуаля.
Я имел ввиду, что изучение/освоение ООП включает в себя изучение/освоение ООД.
Мало с чем согласен (или мало что понял). Хотя бы вот: LSP определяется в контексте отношения тип-подтип (и, на самом деле, определяет это отношение); параметрический полиморфизм никакого отношения к этому отношению не имеет.
К>Начнём с самого главного: с принципа подстановки Лисков (LSP). Именно по нему проходит граница.
Не очевидно.
К>Ад-хок-полиморфизм отличается от параметрического и субтипов тем, что плюёт на LSP. К>Как именно это наплевательство реализовано — компилятором ли (перегрузка функций С++, параметры шаблонов С++, классы типов Хаскелла), проверками в рантайме пользователем — printf, if(typeid(p)==...), if(dynamic_cast<T*>(p)), паттерн-матчинг Пролога и Эрланга — это совершенно неважно. К>Главное, что набор "съедобных" типов задаётся пользователем и образует плоскую одноуровневую иерархию: "некий аргумент" <- "аргумент конкретного типа".
Не понял.
К>Параметрический полиморфизм основывается на полиморфизме деталей реализации.
Это как? К>Он не плюёт на LSP, а воспроизводит требования деталей. Если детали функции на субтипах — значит, и вся функция на субтипах и отвечает LSP; если детали только произвольные — значит, и вся функция произвольная.
Можно пример(ы)?
К>Полиморфизм субтипов вводит ограничения. Между типами вводится иерархия произвольной глубины (даже если это не классы, а прототипы или диапазоны — только тогда следить за иерархией будет уже не компилятор, а пользователь). К>Параметрически полиморфные функции обязаны быть всеядными по LSP — если принимаешь аргумент базового типа, то и любого его наследника примешь.
Ты о всех параметрически полиморфных функциях говоришь или только об огранниченно-полиморфных с ограничемиями вида ТипПараметра <: Тип?
Если о последних, то они по своему определению всеядны по LSP.
<...>
К>Да, ещё один важный момент: для субтипов нет ограничения на количество и ассортимент типов, в отличие от ad-hoc, где количество заранее известно либо ограничено какими-то внезапными факторами (например, подгрузкой модуля, который знает, что делать с данным тэгом варианта).
А для субтипов нет ограничений внезапными факторами (например, подгрузкой модуля, который знает, какие еще субтипы данного типа бывают)?
Здравствуйте, artelk, Вы писали:
A>Здравствуйте, Кодт, Вы писали:
A>Мало с чем согласен (или мало что понял). Хотя бы вот: LSP определяется в контексте отношения тип-подтип (и, на самом деле, определяет это отношение); параметрический полиморфизм никакого отношения к этому отношению не имеет.
Естественно. И ad-hoc, и параметрический полиморфизм не смотрят на LSP.
К>>Начнём с самого главного: с принципа подстановки Лисков (LSP). Именно по нему проходит граница. A>Не очевидно.
Вот именно поэтому граница проходит по LSP. Мы ведь хотим разобраться, чем субтипы так отличаются от всего остального.
К>>Ад-хок-полиморфизм отличается от параметрического и субтипов тем, что плюёт на LSP. К>>Как именно это наплевательство реализовано — компилятором ли (перегрузка функций С++, параметры шаблонов С++, классы типов Хаскелла), проверками в рантайме пользователем — printf, if(typeid(p)==...), if(dynamic_cast<T*>(p)), паттерн-матчинг Пролога и Эрланга — это совершенно неважно. К>>Главное, что набор "съедобных" типов задаётся пользователем и образует плоскую одноуровневую иерархию: "некий аргумент" <- "аргумент конкретного типа". A>Не понял.
Если у нас есть семейство функций вида foo(T), включающие foo(int), foo(string), template<class X> foo(X*)
то получается иерархия "что угодно из перечня (int,string,указатель)" <-- int; string; X*
Если в этом семействе есть универсальная ловушка-заглушка — foo(...) или tempate<class T> foo(T), — то корневым типом будет не вариантный, а "вообще что угодно".
В С++ информацию о типе знает компилятор, в хаскелле — отчасти компилятор, отчасти она передаётся в рантайме, в яваскрипте каком-нибудь — всё в рантайме и на пользователе.
К>>Параметрический полиморфизм основывается на полиморфизме деталей реализации. A>Это как? К>>Он не плюёт на LSP, а воспроизводит требования деталей. Если детали функции на субтипах — значит, и вся функция на субтипах и отвечает LSP; если детали только произвольные — значит, и вся функция произвольная. A>Можно пример(ы)?
Для любых подходящих типов делаем два действия
— T2 operator+(T,T) — как правило, перегруженный или определённый компилятором, т.е. ad-hoc
— ostream& operator<<(ostream&,T2) — наверняка перегруженный, т.е. ad-hoc
То же самое на хаскелле
foo :: (Num a, Show a) => a -> IO ()
foo a = putStrLn (show (a+a))
С той разницей, что С++ генерит воплощение шаблона для каждого использованного типа, а хаскелл — единственное воплощение с двумя неявными параметрами: словарём класса Num и словарём класса Show.
В эту функцию мы можем подсунуть не любой тип, для которого определены + и <<, а только участника иерархии INumShow.
Зато этих участников может быть сколько угодно, хоть из сторонней dll позаимствуем (тогда как в первом случае все типы были известны компилятору ещё до линковки).
Кстати, здесь я схитрил: не стал делать мультиметод plus(INumShow*,INumShow*), который был бы определён для пар одинаковых типов, так, как это сделано в классе Num у хаскелла. Потому что сразу встал бы вопрос: а как он определён для неодинаковых типов?
И здесь мы бы получили жирный-прежирный ад-хок. Ну, я об этом уже говорил раньше.
К>>Полиморфизм субтипов вводит ограничения. Между типами вводится иерархия произвольной глубины (даже если это не классы, а прототипы или диапазоны — только тогда следить за иерархией будет уже не компилятор, а пользователь). К>>Параметрически полиморфные функции обязаны быть всеядными по LSP — если принимаешь аргумент базового типа, то и любого его наследника примешь. A>Ты о всех параметрически полиморфных функциях говоришь или только об огранниченно-полиморфных с ограничемиями вида ТипПараметра <: Тип? A>Если о последних, то они по своему определению всеядны по LSP.
Разумеется, я уже про ограниченно-полиморфные начал. Да, именно "по определению".
И можно заметить, что их "по определению" отличается от "по определению" ад-хок и "по определению" подлинно параметрических
К>>Да, ещё один важный момент: для субтипов нет ограничения на количество и ассортимент типов, в отличие от ad-hoc, где количество заранее известно либо ограничено какими-то внезапными факторами (например, подгрузкой модуля, который знает, что делать с данным тэгом варианта). A>А для субтипов нет ограничений внезапными факторами (например, подгрузкой модуля, который знает, какие еще субтипы данного типа бывают)?
Если не подгрузить модуль с реализацией субтипа, то в системе ничего не изменится, кроме того, что этот субтип нигде не будет фигурировать.
А если не подгрузить модуль с реализацией ад-хок обработчика, то, встретив данный тип, мы пойдём по иному пути. По какому именно? Это уж как захотим, или как получится:
— исключительная ситуация: "неизвестный науке тип"
— ловушка: "by default"
— другой подходящий обработчик
Предвидя вопросы, замечу: в ООП-программах встречаются места с махровым ад-хоком. Это всяческая десериализация (чтение конфигов, фабрики классов по GUID/ProgID/etc...)
К>Для любых подходящих типов делаем два действия К>- T2 operator+(T,T) — как правило, перегруженный или определённый компилятором, т.е. ad-hoc К>- ostream& operator<<(ostream&,T2) — наверняка перегруженный, т.е. ad-hoc
К>То же самое на хаскелле К>
К>foo :: (Num a, Show a) => a -> IO ()
К>foo a = putStrLn (show (a+a))
К>
К>С той разницей, что С++ генерит воплощение шаблона для каждого использованного типа, а хаскелл — единственное воплощение с двумя неявными параметрами: словарём класса Num и словарём класса Show.
Да, придумал пример на голом си, практически по мотивам хаскелла.
/* обе функции размещают результат в malloc */typedef void* (*PLUS_PROC)(void*, void*);
typedef char* (*SHOW_PROC)(void*);
void foo(PLUS_PROC plus, SHOW_PROC show, void* t, FILE* f)
{
void* tt;
char* s;
tt = plus(t,t);
s = show(tt);
fprintf(f, "<< %s >>\n", s);
free(s);
free(tt);
}
/******* пользовательский код *******/void* plus_shorts(void* a, void* b)
{
int* sum;
sum = (int*)malloc(sizeof(int));
*sum = *(short*)a + *(short*)b;
return sum;
}
char* show_int(void* i)
{
char* buf;
buf = (char*)malloc(sizeof(int)*10/3 + 2);
sprintf(buf, "%d", *(int*)i); /* itoa(*(int*)i, buf, 10); */return buf;
}
int main()
{
short x;
x = 12345;
foo(plus_shorts, show_int, &x, stdout);
}
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, artelk, Вы писали:
К>>>Он не плюёт на LSP, а воспроизводит требования деталей. Если детали функции на субтипах — значит, и вся функция на субтипах и отвечает LSP; если детали только произвольные — значит, и вся функция произвольная. A>>Можно пример(ы)?
К>Параметрический, вытекающий из ad-hoc
К>На С++ К>
К>Для любых подходящих типов делаем два действия К>- T2 operator+(T,T) — как правило, перегруженный или определённый компилятором, т.е. ad-hoc К>- ostream& operator<<(ostream&,T2) — наверняка перегруженный, т.е. ad-hoc
Я не согласен что это параметрический. Если мы разделим объявление на декларацию и объявление, то декларация будет параметрической, а объявление — ad-hoc. Чисто параметрический полиморфизм обязан работать для любых типов. А здесь — лишь для тех, для которых определены "+" и "<<".
Вобщем, и не чисто параметрический и не чисто ad-hoc. А чисто часть от того и другого.
К>То же самое на хаскелле К>
К>foo :: (Num a, Show a) => a -> IO ()
К>foo a = putStrLn (show (a+a))
К>
К>С той разницей, что С++ генерит воплощение шаблона для каждого использованного типа, а хаскелл — единственное воплощение с двумя неявными параметрами: словарём класса Num и словарём класса Show.
Здесь чисто bounded полиморфизм. Т.к. про квантор всеобщности мы забыли при объявлении сигнатуры функции.
К>Параметрический, на субтипах К>
К>В эту функцию мы можем подсунуть не любой тип, для которого определены + и <<, а только участника иерархии INumShow.
Да, и потому это не чисто параметрический, который должен принять любой тип. Это именно subtype полиморфизм. К>Зато этих участников может быть сколько угодно, хоть из сторонней dll позаимствуем (тогда как в первом случае все типы были известны компилятору ещё до линковки).
A>>Ты о всех параметрически полиморфных функциях говоришь или только об огранниченно-полиморфных с ограничемиями вида ТипПараметра <: Тип? A>>Если о последних, то они по своему определению всеядны по LSP.
К>Разумеется, я уже про ограниченно-полиморфные начал. Да, именно "по определению". К>И можно заметить, что их "по определению" отличается от "по определению" ад-хок и "по определению" подлинно параметрических
С такой оговоркой часть претензий снимается, кроме разве что примера на Хаскеле. Там bounded и все тут.
К>>>Да, ещё один важный момент: для субтипов нет ограничения на количество и ассортимент типов, в отличие от ad-hoc, где количество заранее известно либо ограничено какими-то внезапными факторами (например, подгрузкой модуля, который знает, что делать с данным тэгом варианта). A>>А для субтипов нет ограничений внезапными факторами (например, подгрузкой модуля, который знает, какие еще субтипы данного типа бывают)?
К>Предвидя вопросы, замечу: в ООП-программах встречаются места с махровым ад-хоком. Это всяческая десериализация (чтение конфигов, фабрики классов по GUID/ProgID/etc...)
Дык суть всего ООП в понимании А. Кея в терминах полиморфизма сводится к ад-хоку времени выполнения. А то что он реализован через сабтайпинг — это детали реализации.
К>>Для любых подходящих типов делаем два действия К>>- T2 operator+(T,T) — как правило, перегруженный или определённый компилятором, т.е. ad-hoc К>>- ostream& operator<<(ostream&,T2) — наверняка перегруженный, т.е. ad-hoc
S>Я не согласен что это параметрический. Если мы разделим объявление на декларацию и объявление, то декларация будет параметрической, а объявление — ad-hoc. Чисто параметрический полиморфизм обязан работать для любых типов. А здесь — лишь для тех, для которых определены "+" и "<<". S>Вобщем, и не чисто параметрический и не чисто ad-hoc. А чисто часть от того и другого.
В смысле, на декларацию и объявление? Может быть, на определение?
Чисто параметрический полиморфизм — это сфероконь.
Хоть что-то же должно превратиться в конкретику, ну хотя бы, управление памятью.
Впрочем, вот пример максимально очищенного от конкретики
template<class T> // по сути, здесь T только для контроля типов нужен - можно даже заменить на void
std::shared_ptr<T> // управление памятью абстрагировано от T, спасибо механизму shared_ptr
pop_back_if_exist(std::vector< std::shared_ptr<T> >& v) // вектору указателей всё равно, что хранить
{
std::shared_ptr<T> f;
if(!v.empty()) { f = v.back(); v.pop_back(); }
return f;
}
Точнее, мы всю конкретику явно прибили гвоздями, не накладывая ограничений на собственно полиморфный тип.
К>>То же самое на хаскелле К>>
К>>foo :: (Num a, Show a) => a -> IO ()
К>>foo a = putStrLn (show (a+a))
К>>
К>>С той разницей, что С++ генерит воплощение шаблона для каждого использованного типа, а хаскелл — единственное воплощение с двумя неявными параметрами: словарём класса Num и словарём класса Show. S>Здесь чисто bounded полиморфизм. Т.к. про квантор всеобщности мы забыли при объявлении сигнатуры функции.
А он нам здесь нужен, квантор всеобщности? forall a вроде как подразумевается?
<>
К>>Предвидя вопросы, замечу: в ООП-программах встречаются места с махровым ад-хоком. Это всяческая десериализация (чтение конфигов, фабрики классов по GUID/ProgID/etc...) S>Дык суть всего ООП в понимании А. Кея в терминах полиморфизма сводится к ад-хоку времени выполнения. А то что он реализован через сабтайпинг — это детали реализации.
Тогда хаскелл — это ООП-язык? Вот, даже слово "класс" присутствует
Здравствуйте, Кодт, Вы писали:
К>Начнём с самого главного: с принципа подстановки Лисков (LSP). Именно по нему проходит граница.
К>Ад-хок-полиморфизм отличается от параметрического и субтипов тем, что плюёт на LSP.
Не могу согласиться.
Во-первых, плевать или не плевать на LSP может только программист, а не конкретная техника или механизм языка.
Я уже писал на эту тему тут: http://www.rsdn.ru/forum/philosophy/4355436.1
(предлагаю прочитать, прежде чем двигаться дальше в этом посте).
Во-вторых, "обычный" полиморфизм через наследование — это просто подмножество параметрического полиморфизма с зашитым предикатом "is_base_of".
То есть следующие два куска кода ничем, кроме времени диспетчеризации (compile time/run time), не отличаются:
template< class T>
enable_if< quacks_as_duck<T> >
f(T& x) {}
А уже обязанность программиста — следить, чтобы типы, которые он функции подсвывает, удовлетворяли принципу Лисков.
В-третьих, ad-hoc-полиморфизм сам по себе никак не противоречит принципу Лисков, потому что представляет собой опять же просто разновидность параметрического полиморфизма. При обычном полиморфизме, когда ты добавляешь типы к множеству допустимых типов, ты ведь одновременно с этим добавляешь и какие-то операции, так? Но ведь ты можешь добавлять не только внутренние операции, но и внешние, например, придумав свой арифметический класс и правильно(!) реализовав для него арифметические операторы. Таким образом твой класс является подтипом типа аргумента оператора+.
Более того, ты можешь пойти дальше и тип, не являющийся подтипом формально, сделать таковым — я говорю здесь о traits и concept_map.
Так что LSP — это крайне широкий принцип, никак не завязанный на механизмы конкретного языка, диспетчеризацию времени исполнения или времени компиляции и т.п. Он не об этом. Он — о логической совместимости типов с неким архетипом, зашитым в предусловиях функции.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, samius, Вы писали:
К>>>Параметрический, вытекающий из ad-hoc
S>>Я не согласен что это параметрический. Если мы разделим объявление на декларацию и объявление, то декларация будет параметрической, а объявление — ad-hoc. Чисто параметрический полиморфизм обязан работать для любых типов. А здесь — лишь для тех, для которых определены "+" и "<<". S>>Вобщем, и не чисто параметрический и не чисто ad-hoc. А чисто часть от того и другого.
К>В смысле, на декларацию и объявление? Может быть, на определение?
Верно
К>Чисто параметрический полиморфизм — это сфероконь.
В C++ — да К>Хоть что-то же должно превратиться в конкретику, ну хотя бы, управление памятью.
В конкретику превращается все, вопрос в том, требует ли превращение в конкретику специального подхода, вот как в операторе "+". К>Впрочем, вот пример максимально очищенного от конкретики К>
К>template<class T> // по сути, здесь T только для контроля типов нужен - можно даже заменить на void
К>std::shared_ptr<T> // управление памятью абстрагировано от T, спасибо механизму shared_ptr
К>pop_back_if_exist(std::vector< std::shared_ptr<T> >& v) // вектору указателей всё равно, что хранить
К>{
К> std::shared_ptr<T> f;
К> if(!v.empty()) { f = v.back(); v.pop_back(); }
К> return f;
К>}
К>
К>Точнее, мы всю конкретику явно прибили гвоздями, не накладывая ограничений на собственно полиморфный тип.
Согласен
К>>>