LINUX.ORG.RU

[philosophy] В чем заключается революционность перехода от функциональщины к ООП?


1

0

Так уж повелось, что первый язык, который я изучал, был делфи. Потом всякие сишарпики, С++, лисп, и т.п. В итоге, как мне кажется, у меня ООП головного мозга. Когда возникала задача писать на С, я начал реализовавывать обьектную модель в этом языке.

«Стоп», сказал я себе и подумал. Почему сейчас все кругом вопят про ООП и про его архиполезность и архиправильность? Далее, по ходу раздумий, пришел к мысли, что все, что пишется с использованием ООПшной парадигмы, может быть написано и без нее.

Почему появились языки, которые взяли ООП за главенствующую идею (java, c#, етц)?

Неужели те преимущества, которые предлагает ООП (полиморфизм, инкапсуляция, наследование), дают прирост в эффективности, скорости написания программ, понимания их работы и поддержке? Здесь было бы интересно сравнить одну и ту же программу, написанную на С и на С++, чтобы узреть принципиальные архитектурные различия (может такие уже есть?).

Сухой остаток. ООП представляет из себя еще один уровень абстракции, который позволяет оперировать про проектировании не функциями, а обьектами. А неужели это так меняет дело и делает разработку более удобной?

Было бы интересно без срачей услышать компетентное мнение.

★★

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

> Really? Не расскажете об отличиях?

Может лучше вы дадите определение ad-hoc полиморфизма (ссылки приветствуются) и поясните, почему вы считаете ООП ad-hoc полиморфизмом. Хотелось особенно понять как это соотносится с ООП в Smalltalk, утиной типизацией в Python и прототипами в JavaScript.


шаблоны и частичная специализация шаблонов в С++

Нет, это препроцессор.


Не понял, хотите сказать что шаблоны C++ обрабатывает препроцессор??? Или о каком препроцессоре речь?

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

> хотите сказать что шаблоны C++ обрабатывает препроцессор??? Или о каком препроцессоре речь?

Вероятно, мигель решил напомнить, что когда-то давно (лет 20 назад) для реализации шаблонов использовался похаченный Си-препроцессор %)

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

почему вы считаете ООП ad-hoc полиморфизмом.

Потому что одна и та же функция выполняется разным кодом для разных типов данных.

Не понял, хотите сказать что шаблоны C++ обрабатывает препроцессор?

Я хочу сказать, что шаблоны C++ - и есть препроцессор.

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

Вероятно, мигель решил напомнить, что когда-то давно (лет 20 назад) для реализации шаблонов использовался похаченный Си-препроцессор

Нет, не решил, я вообще об этом не знал.

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

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

статической типизацией, в динамических - он совершенно не нужен.


Что значит не нужен? Есть или нет? Тогда любую функцию в динамических языках можно считать примером параметрического полиморфизма и появился он в ООП-языке на лет 15 раньше, чем появилась первая реализация Haskell.

Вообще это бред, брать терминологию из Haskell и пытаться натягивать её на другие языки.

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

> Я хочу сказать, что шаблоны C++ - и есть препроцессор.

Тогда ты называешь «препроцессором» какую-то сущность, отличную от той, которую называют «препроцессором» прогеры на Си/Си++. И, скорее всего, это даже не имеет отношения к делу.

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

Вообще это бред, брать терминологию из Haskell и пытаться натягивать её на другие языки.

Ничего, что этой терминологии 43 года? Haskell моложе.

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

Тогда ты называешь «препроцессором» какую-то сущность, отличную от той, которую называют «препроцессором» прогеры на Си/Си++.

Конечно. Или ты считаешь, что препроцессор - это исключительно cpp? А, скажем, m4 - это уже не препроцессор.

Повторю ещё раз. Меня не волнуют детали реализации - это проблемы компилятора. Но до сих пор шаблоны C++ ведут себя так, как будто программа с шаблонами сначала преобразуется в программу без шаблонов (с полным сохранением уровня типобезопасности), а потом уже компилируется. Шаблоны полностью развёртываются, понимаешь?

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

> Потому что одна и та же функция выполняется

разным кодом для разных типов данных.


Это что, общая черта сторонников ФП - не способность излагать свои мысли ясно и чётко? Я несколько раз прочитал это утверждение и не понял на каком основании что утверждается.

Вы можете просто привести ваше определение ad-hoc полиморфизма и исходя из него показать почему ООП является ad-hoc полиморфизмом? И привести пример для Python или JavaScript - предпочтительно использовать язык с динамической типизацией да бы опустить несущественные детали.

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

> А, скажем, m4 - это уже не препроцессор.

...

Шаблоны полностью развёртываются, понимаешь?



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

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

>> Тогда ты называешь «препроцессором» какую-то сущность, отличную от той, которую называют «препроцессором» прогеры на Си/Си++.

Конечно.

Прелестно.

Или ты считаешь, что препроцессор - это исключительно cpp? А, скажем, m4 - это уже не препроцессор.

Нет, я считаю, что в разговоре, в котором постоянно упоминается Си++, термин «препроцессор» должен употребляться с уточнением, если это не старый добрый /lib/cpp.

Шаблоны полностью развёртываются, понимаешь?

Ага, еще с 43 года^W^W^W^Wдавно (еще во втором издании Страуструпа это говорилось). Но я не вижу принципиальных проблем с таким подходом (технические вижу).

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

Я несколько раз прочитал это утверждение и не понял на каком основании что утверждается.

Вы не поняли само утверждение? Или вы не согласны?

И привести пример для Python или JavaScript

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

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

Вообще-то, шаблоны C++ образую Тьюринг-полный язык.

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

m4 тоже Тьюринг-полный, и что?

На самом деле, конечно, это препроцессор на стероидах, имеющий информацию о типах (объявленных, а не рантаймовых).

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

Но я не вижу принципиальных проблем с таким подходом (технические вижу).

Да я тоже не вижу. И технических тоже. Надо только понимать, с чем мы имеем дело, вот и всё.

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

> Вы не поняли само утверждение? Или вы не согласны?

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

кстати, не совсем верное


В каком, извините, месте?

что шаблоны полностью развёртываются?


Затрудняюсь, понять это утверждение. Что значит «полностью развёртываются»? Это плохо или хорошо? Во всей литературе по С++ для описания шаблонов используется термин «статический полиморфизм». Его нельзя отнести к «параметрическому полиморфизму»? Почему?

Пример чего? Одной функции, имеющей разные реализации

для разных типов данных?



Да. Например, берём такой ООП язык как JavaScript и используем там функции call или apply, в которых первым параметром указывается какой объект будет использоваться в качестве this в этих функциях. Или можено взять объект и конкретно для него изменить определение какого-либо метода. Я не могу понять как это соотносится с ad-hoc полиморфизмом.

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

Нет, это препроцессор.

гыгы;

вот посмотри на эту реализацию mapcar:

$ cat mapcar.hxx

#include <vector>

template<class Arg, class Res>  std::vector<Res*>& do_mapcar( Res* (*f)(Arg*), const std::vector<Arg*>& array );

template<class Arg, class Res> inline std::vector<Res*> mapcar( Res* (*f)(Arg*), const std::vector<Arg*>& array )
{
    return reinterpret_cast< std::vector<Res*>&>
        ( do_mapcar( reinterpret_cast<void*(*)(void*)>(f), reinterpret_cast<const std::vector<void*>&>(array) ) );
}

$ cat mapcar.cxx

#include "mapcar.hxx"

template<class Arg, class Res>  std::vector<Res*>& do_mapcar( Res* (*f)(Arg*), const std::vector<Arg*>& array )
{
    std::vector<Res*>* result = new std::vector<Res*>();
    for( unsigned int i=0; i<array.size(); i++ )
        result->push_back((*f)(array[i]));
    return *result;
}

template std::vector<void*>& do_mapcar<void, void> ( void*(*)(void*), const std::vector<void*>& ); /// instantiate it here!

$ cat main.cxx

#include <iostream>
#include <string.h>
#include "mapcar.hxx"

int* f(const char* x) { return new int(strlen(x)); }

int main(int argc, char** argv)
{
    std::vector<const char*> v;
    v.push_back("a"); v.push_back("bc"); v.push_back("def");
    std::vector<int*> vv=mapcar(f, v);
    std::cout <<  *(vv[0]) << *(vv[1]) << *(vv[2]) << '\n';
    return 0;
}

имеем ровно один экземпляр кода mapcar, который лежит в mapcar.o (objdump не даст соврать); ну и какой полиморфизм мы получили? (хотя да, еще тут придраться можно)

еще лучше бы генерить mapcar.hxx с помощью шаблона, но там есть проблемка

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

имеем ровно один экземпляр кода mapcar (который называется do_mapcar)

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

если еще подумать, то можно видимо все reinterpret_cast завернуть в шаблон, и избавить от необходимости их юзать не только клиентов, но и библиотеко-писателей

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

Я не понял какой смысл

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

В каком, извините, месте?

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

Это плохо или хорошо?

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

Во всей литературе по С++ для описания шаблонов используется термин «статический полиморфизм».

Ну, извините, не читал. По факту, это вообще не полиморфизм, это такая хреноватая имитация, протекающая по всем стыкам.

Например, берём такой ООП язык как JavaScript и используем там функции call или apply

Буэ. Берём ЛЮБОЙ ООП язык и реализуем один и тот же метод по разному в двух разных классах (имеющих общего предка, если язык статический). После этого вызываем этот метод для объекта, для которого заранее неизвестно, к какому из них он относится (опять же, если язык статический - для объекта, относящегося к базовому классу).

Ну, в Лиспе с его мультидиспатчингом всё ещё проще.

Я не могу понять как это соотносится с ad-hoc полиморфизмом.

Дык, собственно, он и есть. Одна функция, работающая с разными типами, и имеющая при этом разные реализации.

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

компилить например так:

g++ -g -o main.cxx mapcar.cxx
g++ main.o mapcar.o

и дальше objdump -S mapcar.o

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

ну и какой полиморфизм мы получили?

Да, вроде, никакого вообще? Функция do_mapcar инстанцируется один раз, соответственно, работает только с одним типом.

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

> Да, вроде, никакого вообще? Функция do_mapcar инстанцируется один раз, соответственно, работает только с одним типом.

а функция mapcar работает с любыми типами, причем проверяет их — чем не параметрический полиморфизм?

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

а функция mapcar работает с любыми типами, причем проверяет их — чем не параметрический полиморфизм?

Не, там каждый раз генерится новая функция. На уровне исходников (ты же не можешь засунуть mapcar в объектник).

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

> Не, там каждый раз генерится новая функция. На уровне исходников (ты же не можешь засунуть mapcar в объектник)

зачем мне засовывать mapcar в объектник и генерить лишний код? ради чьих-то эстетических чувств?

mapcar инлайнится, и вызвает do_mapcar, которая уже в 1 экз.

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

> Вы вызываете функцию, принимающую этот объект одним из аргументов.

Оказывается, что то, какой код будет при этом выполнен, зависит от

типа объекта.



И как из этого следует, что ООП это ad-hoc полиморфизм?

По факту, это вообще не полиморфизм


В литературе по языкам программирования это называется полиморфизмом. Если у вас какое-то определение полиморфизма, которое родилось вне языков программирования, и которым вы пользуетесь, то можете его привести? Среди программистов есть устоявшаяся терминология, вы её отрицаете, но это явно не говорите, а вместо этого ведёте диспут не понятно о чём. Вы можете, в конце концов, объяснить, что такое полиморфизм по вашему вообще и ad-hoc и параметрический в частности? И почему есть только эти?

это такая хреноватая имитация, протекающая по всем стыкам.


Можно раскрыть эту мысль? А то ведь на веру подобное утверждение не примешь.

Берём ЛЮБОЙ ООП язык и реализуем один и тот же метод по

разному в двух разных классах



Нет, я взял JavaScript, где вообще нет предков, а есть прототипы и не понимаю о чём вы мне это говорите.

Одна функция, работающая с разными типами, и имеющая

при этом разные реализации.



Ну и что? Мы же вроде говорили об ООП и мэйнстриных языках вообще и вы утверждали, что до недавнего времени там только ad-hoc полиморфизм и был.

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

причем заинлайненная mapcar, если не ошибаюсь, займет 0 (ноль) байт кода

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

> Таким образом, компиляция всегда либо заканчивается, либо вылетает

с ошибкой - что противоречит тьюринг-полноте.


Ага, а памяти всегда ограниченное колличество, так что любая программа может вылететь просто из-за её недостатка. Зачем вы путаете спецификации и конкретные реализации?

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

Можно раскрыть эту мысль?

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

где вообще нет предков,

И? Я упоминал о предках в контексте статически типизированных языков.

и не понимаю о чём вы мне это говорите.

Мдя. Бывает. Для самых тупых, последний раз: возьмите и напишите один метод двумя способами, для разных типов данных. Не можете, что ли?

и вы утверждали, что до недавнего времени там только ad-hoc полиморфизм и был.

Именно. Потому что второго вида полиморфизма, где одна функция работает с разными типами и имеет ОДНУ реализацию, в них не было.

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

> Это уже детали реализации.

не-не, это не детали, это доказывает выполнение определения ПП

mapcar: инлайнится везде, занимает 0 байт, проверяет типы
do_mapcar: присуствует в ровно 1 экземпляре, делает работу
___________________________________________________________

и кстати, так можно попробовать разорвать бесконечную инстанциацию шаблонов в твоем примере про равенство длин векторов

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

Ага, а памяти всегда ограниченное колличество, так что любая программа может вылететь просто из-за её недостатка. Зачем вы путаете спецификации и конкретные реализации?

Не путаю. Я же не сказал, что утверждение о тьюринг-полноте так вот просто неверно. Я сказал «не совсем верно».

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

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

mapcar: инлайнится везде, занимает 0 байт,

Да какая разница, сколько байт она там занимает. Детали реализации. Вот я возьму компилятор, который вообще не инлайнит ничего, и что?

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

и кстати, так можно попробовать разорвать бесконечную инстанциацию шаблонов в твоем примере про равенство длин векторов

Не, нельзя. Препроцессору, даже на стероидах, наплевать, сколько там займёт скомпилированная функция. Он на уровне исходников работает.

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

> Устал уже, раскрывал тут неоднократно.

Ну я знаю, о чем ты говоришь.

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

> Не, нельзя. Препроцессору, даже на стероидах, наплевать, сколько там займёт скомпилированная функция. Он на уровне исходников работает.

бугага — это все-таки не препроцессор

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

> если считать, что темплейты реализуют параметрический полиморфизм,

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

поиметь проблемы



Ни разу ни нарывался. Ну и что, что можно поиметь проблемы? Это как-то противоречит определению полиморфизма?

возьмите и напишите один метод двумя способами,

для разных типов данных.



Писал много раз. А ещё могу написать функцию, которая будет с разными типами данных.

Потому что второго вида полиморфизма, где одна функция работает

с разными типами и имеет ОДНУ реализацию, в них не было.



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

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

бугага — это все-таки не препроцессор

Бугугу, это он и есть.

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

Утверждения о параметрическом полиморфизме не имеют смысла в контексте языков с динамической типизацией.

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

> Утверждения о параметрическом полиморфизме не имеют смысла

в контексте языков с динамической типизацией.


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

Да, в динамических языках подобный полиморфизм достигается автоматически без каких-либо усилий со стороны программисты и называется «утиной типизацией», но разве это меняет суть явления?

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

Ни разу ни нарывался.

Рад за вас.

Если бы эти проблемы были широко распространены, от шаблонов бы давно отказались.

Ну и что, что можно поиметь проблемы?

То, что абстракция протекает.

Писал много раз.

Ну вот, чего вам ещё нужно?

Не понял, языков с динамической типизацией не было?

Да было, было. Функций, работающих с разными типами идентичным образом, не было (ну, почти).

Классический пример - конкатенация списков. В языке с динамической типизацией эта функция работает с одним типом - «список». Нет отдельного типа «список целых» или «список строк», хотя бы потому, что в список целых тривиально добавить строки, и наоборот.

В языке со статической типизацией она работает с разными типами, «список целых» и «список строк» - разные вещи. Если у нас есть ПП, то это возможно. Если у нас есть препроцессор, мы можем легко нагенерить новых функций, работающих так же - хотя должны иметь под рукой исходники этой функции. Если у нас нет ничего из этого, мы можем, разве что, плюнуть на типобезопасность и использовать везде какой-нибудь (void*).

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

Именно так. Что тебе непонятно здесь: [philosophy] В чем заключается революционность перехода от функциональщины к ООП? (комментарий)

void f()
{
  int x = input_some_number(); 
  if(x < 0 || x > 15) return; 
  int u=foo(x);
  int v=bar(x);
  int w=baz(x);
  ...
}

Допустим, что foo, bar, baz определены в другой еденице компиляции и при х, выходящим за отерзок от 0 до 15 дают неправильный результат, но похожий на правильный (не вызывая исключения, не сегфолтясь, ..., особенные садисты могут добавить сюда тихую порчу чужих данных)

Тогда код выше будет правильный.

Но если мы выкинем строку с if, то код станет неверным, и компилятор тихо промолчит, а он ДОЛЖЕН давать варнинг или лучше ошибку. Поэтому нихрена ваш компилятор не знает.

Теперь правильный путь, где компилятор знает:

void f() throw
{
  Int<0,15> x;
  try{ x=Int<0,15>(input_some_number()); } catch(OutOfRange) { return; }
  int u=foo(x);
  int v=bar(x);
  int w=baz(x);
  ...
}

естественно, определять надо int foo(Int<0,15>) и т.д.

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

не называется это утиной типизацией, утиной типизацией называется другое


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

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

Нету типов данных, поэтому говорить о «различных типах данных».

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

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

Ну, я имел в виду то, что ты про списки писал.

Вообще это все херня.
Смысл понятен, но разговор идет о словах

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

> То, что абстракция протекает.

Абстракция всегда протекает, просто потому, что компьютер конечен и имеет ограниченные ресурсы.

Ещё раз: почему шаблоны в C++ не являются примером параметричекого полиморфизма?

Функций, работающих с разными типами идентичным образом,

не было (ну, почти).



qsort? Таких функций много, особенно в языках с динмаческой типизацией.

В языке с динамической типизацией эта функция работает

с одним типом - «список»



Я несколько раз уже сказало «утиная типизация», вы знаете что это такое? Так зачем про список рассказываете?

Если у нас есть препроцессор, мы можем легко нагенерить новых функций


Нет, в С++ это делает компилятор, а не препроцессор. И какая разница что он там генерит или не генерит? В стандарте C++, кстати, предусмотрена возможность раздельной компиляции шаблонов (так что это точно не препроцессор).

Если у нас нет ничего из этого, мы можем, разве что, плюнуть

на типобезопасность и использовать везде какой-нибудь (void*).



Это как-то противоречит определению полиморфизма?

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

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

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

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

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