Имя метода обычно содержится в сообщении, в той или иной форме.
Это только если какой-то аналог RPC делается. Сообщение это данные. Обычно, если там и есть имя - это тип этих данных. Но ни как не тип потребителя этих данных. Тем более, что обработчик - вообще говоря - может быть и не один.
Достаточны только завтипы, и то не факт.
С одной стороны, хочется уточнить: достаточно для чего? А с другой - формализмы зав. типов - это "охренеть как смело" (с) :-)
Кроме того, вы так и не показали, что ваш формализм лучше.
Мой это какой?! Я - вроде как - никаких новых формализмов не вводил.
Примерно соответствующий (в вашей аналогии) сигнатуре метода в ООП
Каким образом?! Я ещё могу представить, как из "здоровенной схемы, описывающую сообщение" выводятся функциональные типы. Это не сложно. Но, что они вам дадут?! Что бы "выйти" на методы нам нужны имена. А тут они - буквально - любые.
Ну так оно и формализовано в большинстве языков, в виде интерфейсов...
Только на уровне формализма подтипа. Его - чаще всего - не достаточно. Поэтому там где нужных формализмов в языке нет, возникают всякого рода "техники программирования", которые переносят их (недостающие формализмы) на уровень реализации.
Смотрите... вот у нас есть обычные типы (что бы это не значило). А есть функциональные типы. Какая между ними "разница"? Достаточно ли будет сказать, что функциональные имеют более высокий порядок? И в чём разница между типами разного порядка?
Или - с другой стороны заход - есть тип, а есть его экземпляры (что бы это не значило). Вот представьте себе эдакий НЕХ - экземплярами которого являются типы. Какая между ними разница? Мы на уровне типов даже выразить этот НЕХ (да... это про категорию типов) не сможем :-(
Т.е. эти вот "протоколы"/"поведения"/"классы типов" - это всё про "ограничения", которые накладываются не на уровне самих типов. Это про ограничения уровнем (я порой и не одним) выше, так сказать.
Когда вы реализуете "интерфейс", вы - фактически (через subtyping) - накладываете ограничения на [используемые] типы. С одной стороны, это вроде как "то, что нужно". А с другой - очень часто, это "не те" (либо слишком строгие, либо слишком слабые) ограничения, которые мы (на самом деле) хотим. По большому счету - это из-за того, что сам "интерфейс" - это тоже тип. Соответственно, он и описывается исключительно в "терминах" типов, со всеми их ограничениями. Даже если притащить сюда ещё и параметрический полиморфизм, то ограничения на параметры обобщенного типа - всё равно описываются в терминах типов. Не вырваться :-(
А если мы будет - по сути - тоже самое делать "уровнем выше", то никаких проблем такого рода у нас просто не будет.
Если идти дальше, то это уже какой-то "код" надо будет писать, чтобы показать "разницу". Оно надо?
Какой "объем знаний о получателе" это нам добавляет?
... плюс описание в документации, что этот получателе делать должен.
:-) А это точно нужно знать отправителю?!
Потому что если не знаем - то смысла посылать?
Например, потому что "что, куда и когда" определяется не отправителем (и не получателем), а протоколом. Точнее, контекстом, в котором сейчас находится отправитель, и протоколом, который этим самым контекстом задается.
Если вам удобнее смотреть на это (контекст и протокол) через призму "интерфейсов", то упрощенно это можно представить себе некоторый медиатор (в терминах GoF).
Так что знание таки всегда есть, пусть и не формализованное в языке.
Ну вот и представьте, что это таки может быть "формализованное в языке".
Ну то есть, исключительно такие, в которых можно спросить компилятор "а это всё вообще имеет смысл?" ...
Если я правильно понял о чём речь, то "компилятор" - сам по себе - тут вообще не при чем. Статический анализ применим и к языкам с динамической типизацией. Главное, чтоб она (типизация) строгой была.
Но я-то не про типизацию... я про связывание (binding). Оно - вообще говоря - ортогонально типизации.
На самом деле, это охренеть какое преимущество :-)
Всё же как программист вы обязаны знать хоть что-то о получателе ведь иначе с чего бы вам вообще пришло в голову посылать это сообщение?
Зачем?! Если "на пальцах": что вы "знаете" о получателе, когда отправляете сообщение, например, в какой-нибудь exchange AMQP брокера? Ну или, когда добавляете запись в какой-то topic kafka - вы много "знаете" о том, кто будет эту запись потом "читать"? А когда уже вы читаете запись и топика, вы что-либо "знаете" об "отправителе"? Зачем это вам?!
Кроме того, если такой свойство действительно необходимо - оно спокойно реализуется интерфейсами.
Ну, нет. "Интерфейсы" (а точнее, их реализация) - это просто "вот такая вот" форма subpyping'а. А protocol'ы smalltalk'а, behaviour 'ы erlang'а и прочая, и прочая - это не subtyping ни разу.
Я в одном, пожалуй, соглашусь... если рассматривать исключительно "статические" системы, то такого рода преимущество не совсем очевидно. Ну действительно - "зачем козе баян", если "всё, что может быть" мы и так уже описали "до того как".
Но как только вы перейдете к "динамическим" системам - полезность "такой штуки" становится более чем очевидна. Представьте себе, что у вас "рождаются и умирают" не только экземпляры типов, а сами типы - "рождаются и умирают".
Попробуйте посмотреть на ООП не как оно есть мейнстримно, а как оно было в оригинале - то, что Алан Кей сделал в Smalltalk.
Про "как" и "почему так", Алан "сделал в Smalltalk" лучше, имхо, почитать у самого Алана. Благо его The Early History of Smalltalk давно есть в сети. Очень познавательно.
Вот вам цитата. Это прям самое начало... даёт представление о "какую проблему решаем".
Though OOP came from many motivations, two were central. The large scale one was to find a better module scheme for complex systems involving hiding of details, and the small scale one was to find a more flexible version of assignment ...
Но лучше самому почитать, имхо. Там не много...
Т.е. посылка сообщений.
"посылка сообщений" - aka message passing - это то, что, в конечно счете, станет решением для этой самой "small scale one". То, что он сам потом назовет "extreme late binding of all things".
:-) Прям сразу "с козырей". Можно же было как-то "тихонечко"... через композицию там... подвести к теории типов... сумма типов, произведение... то сё... показать, что subclassing это такой "ugly kid Joe", в смысле суммы типов... ну и потому же - да - "шарахать" :-)
Сокрытие в ООП ни разу не является ключевым. Изначально оно про message passing, и никто не запрещает ввести сообщения, которые о состоянии расскажут.
Причем тут message passing?! Оно - не "цель" (ООП), а лишь инструмент (один из). Да и смысл "сокрытия" совсем не в том, что состояние не видно "из вне". А в том, что его (состояние) нельзя изменить "из вне".
Или вот Perl/Python - все поля объекта общедоступны, и ничего, это не перестает быть ООП.
"Открытое" состояние (и даже просто его принципиальная возможность в) - есть основная часть критики "оопешности" таких (и не только этих) языков/систем. Это, на всякий случай... вдруг вы не в курсе.
А если брать уж действительно глобально, то все парадигмы предназначены ровно для одной первичной цели - переиспользование кода (DRY).
Как-то всё с ног на голову, имхо. Они - парадигмы т.е. - действительно про "одну цель". И DRY - если попытаться рассматривать этот принцип, как парадигму - тут не исключение. Цель эта - борьба со сложностью.
Если "маненько упростить", то ОПП - как парадигма - предлагает "сокрытие" состояния, как основной способ борьбы со сложностью (реализуемых систем). Т.е. вот прям буквально: если все изменения состоянияS будут локализованы в S, то это не привнесет в систему (в которую S будет включено) сложность самой S. И в этом смысл. Речь - естественно - о "сложности системы" в инженерном смысле.
А дальше возникает, например, т.н. "позднее связывание" - как ответ на вопрос: "А как бы нам таки уже включить это самое S в нашу замечательную систему?" И т.з. целостности, его ("позднее связывание") тоже можно рассматривать (более того, так обычно и делают), как часть парадигмы ООП. Но важно понимать, что оно - не самоценно. И без первого ("сокрытия" состояния) - вообще говоря, имеет мало смысла в этой (ООП) парадигме. И, упомянутый вами, message passing - это как раз одни из способов (да, наверное самый универсальный, но таки один из) получить это самое "позднее связывание".
... но изначальные причины появления, всегда - уменьшить количество работы программиста.
Я бы сказал - если "на пальцах" - причина: это мочь реализовывать/развивать более сложные системы, чем до этого. О "работе программиста" речь если и идет, то как о "третьей с конца по важности".
Открытость/закрытость - это слишком общие понятия.
Это вполне конкретные понятия. Их просто "нилюбют". Гораздо интереснее обсуждать "другое". Но, как только, обсуждение переводится хоть сколько-то академическую плоскость (из "научно-популярной") всё сводится к состоянию и его "открытости"/"закрытости". Иногда ещё говорят не о "закрытости", а о "локальности изменений" состояния - сути это не меняет.
Но куча не простая, в структурированная - данные связанны "смысловыми стрелочками" друг с другом и алгоритмами и алгоритмы друг с другом. Куча, море стрелочек!
"структурирование данных" и "куча стрелочек" (DF/CF и прочие F) - это СП. Оно очень даже допускает "открытое состояние". Более того - по большому счету - оно всё и построено вокруг "не локальности изменений" глобального состояния. Без этого оно просто "ниработаит" (tm).
И общая задача упорядочивания сводится к сокращению стрелочек и упрощению их структуры.
?!
Так что все принципы ООП важны.
Нет там никаких других "принципов". Точнее - они не имманентны "сокрытию состояния".
Ибо по отдельности они не достигают поставленной цели.
Цель - одна: скрыть состояние - т.е. "локализовать его изменения". Это сильно всё упрощает само по себе, знаете ли.
И простое "сокрытие состояния", без наследования - деньги на ветер. Ибо туева хуча одноранговых объектов с кучей дублирующегося кода создаст чуть не большую кашу, чем бесструктурный код.
Извините, но как связаны "куча дублирующегося кода" и "наследование"? Точнее, как наличие второго исключает наличие первого? Или - по вашему - "наследование" - это про переиспользование чтоль?!
А наследование без полиморфизма - это вообще пытка над программистами из серии "протащи вызов через десять слоёв абстракции" (правда молодые программисты судя по моим наблюдениям её почему-то любят)
?! Наследование, это же один из способов получить тот самый полиморфизм. Способ. Один из. Т.е. вполне может быть полиморфизм без какого-либо наследования. Но вот "наследование без полиморфизма" я себе представить не могу. И мне вот сильно интересно, где вы видели это самое "наследование без полиморфизма"?
Точнее классы - это не ООП. Это вообще перпендикулярно. Но они очень сильно помогают дальнейшей метаструткуризации в ООП ...
Скажу так... есть гораздо более удобные способы для, чем subclassing.
Всегда удивляюсь, почему практически любое (читай, не академическое) обсуждение ООП всегда (и очень быстро) скатывается к обсуждению каких-либо технических деталей?
Концептуально, ООП это же очень просто: сокрытиесостояния. Как говорится, какое из этих двух слов вам не понятно? :-)
Остальное всё (вот буквально - всё) - это средства, которыми это самое сокрытие достигается в том, или ином случае. Ну либо "следствия" их использования. Всё, от какого-нибудь message passing до, какого-нибудь subtyping - это всего лишь "инструменты", т.е. суть - вообще не важны для понимания.
Да даже само понятие "объект" - вторично. Внезапно, ООП замечательно обходится из без "объектов" - не говоря уже про, какие-нибудь "классы"... да и "типы" вообще :-)
В этом смысле, показательно что ООП "ломается" как раз тогда, когда нет состояния. Нечего скрывать, не с чем становится работать - и тут же "рождаются драконы" разной степени монструозности и упоротости.
Резюмируя... ООП - это не про "классы" и т.п. Когда мы говорим об ООП, то - по сути - мы говорим лишь от том, что "открытое" состояние - не допустимо. В отличие от какого-нибудь СП, которое вполне себе это допускает. Остальное уже частности, особенности платформ, конкретных языковых средств и т.п., которые - если убрать сокрытиесостояния - тут же теряют всякую ценность.
Из этого же, например, следует, что "противопоставление" ООП и ФП - это немножко "такое" (tm). По отношению к наличию "открытого" состояния - они категоричны.
Да и ФП, которое, одновременно, ООП - это "вообще норм" (tm). В том смысле, что если есть (ну вот надо нам) состояние, то мы его обязательно скрываем. Например, в "процессе" - как в Erlang, или, например, в специальном "типе" - как в Haskell.
Как-то сильно "на пальцах" и, местами, вообще небрежно и - соответственно - порой, получается вообще "не про то" :-(
Начиная, с атомов (в примерах, внезапно, не "% примеры атомов"), и заканчивая "сумбуром" про функции, модули и процессы.
Как вообще можно рассказывать про Erlang, в котором - буквально - всё есть сопоставление с образцом... и из которого "прорастает" весь его синтаксис, начиная не с концепции "сопоставления с образцом"?!
... тот будет потом бесконечно платить и переплачивать ...
Во-первых, "как что-то плохое" (tm) :-) А во-вторых, что-то мы как-то не наблюдаем толпы тех, кто уже... что несколько печалит.
На данный момент, единственное, что мы - как исполнители - точно знаем: это то, что если "говнокод" и является проблемой, то это "проблема издержек". Со всеми вытекающими...
Статья, имхо, заход прям очень уже не с той стороны.
Как мне кажется, "проблема говнокода" - она вообще не лежит в, скажем так, сколь-нибудь технологической плоскости.
Грубо говоря, т.к. важен только наблюдаемый результат и больше ничего, то никто не готов платить за абстрактный "красивый код". Вот и всё.
Всегда (ну ок... за редчайшим исключением) выбирается "быстро и не дорого". А даже когда выбирается "быстро и качественно" - по этим самым "качественно" всё равно подразумевается не некая абстрактная "красота кода" (которая сама по себе ещё и ничего не гарантирует с т.з. достижения результата), а совсем другие вещи.
Ну а то, что при достигнутом результате потом "главное, не трогать несущую трубу" (с) - вообще мало кого волнует. Да вообще никого, можно сказать :-)
"Выражение" - в смысле expression? Если да, то почему "подвыражение"? clause - с тем же успехом - и к statement (канон - внезапно - "оператор") может относится. Ну и оно вообще не про "под". Оно вполне себе самостоятельным может же быть. Да хоть уже упоминаемая function clause в erlang - это вполне себе самодостаточная языковая конструкция.
Но канона для clause в этом смысле - насколько я знаю - нет. Попытки есть... типа "предложение", "клаузула" и т.п. - но это, имхо, такое.
Эт хоть и не вам ответ был а для @sibirier, но таки спрошу: Таненбаума читали? Оба два? :-)
Но в текущий момент вся эта история тем не менее выглядит оторванной ...
Ну я чтоль виноват, что термин thread такой древний? :-) И к cpu - с его flow - он вообще не относился пол века почти. Он из "теории" операционных систем так-то.
Но цикл планировщика потоков (thread scheduler) - это и сейчас spin. Да и spin lock со spin wait'ом никуда не делись.
... и история с водяными потоками выглядит несколько более понятной.
Оно не правильно "выглядит". Эти ваши "водяные потоки" как-то можно ещё натянуть на те threads, что в cpu. А те, что в ОС - не "водяные", и не "потоки"... в том смысле, что их идея - как раз в обратном.
Слушайте... вот реально "и выросло поколение" :-) Без обид, только :-)
Изначально термин thread вообще к процессору (cpu) никакого отношение не имел. Это просто было лишено какого-либо смысла, ибо "там" - одни control flow и "вертись как хочешь". Thread - в изначальном смысле - это "часть" процесса (process) - который не "вообще" - а в некоторой операционной системе... которая тож не просто так... но эт не суть важно.
И вот ими (этими самыми thread'ами) управляет не cpu (он этого просто не умеет), а специальный планировщик (scheduler) - часть той самой "хитрой" ОС. Оно и сейчас, по большому счету, так.
Так вот - thread они потому, что scheduler... ну, он делает spin... вот - т.е. он буквально "прядет" их, переключая т.н. контекст исполнения :-) Control flow-то один.
Тут вот у нас "штука", которая делает spin, а это вот - значит - threads... всё логично :-)
И threads весьма долго и были "нитями" - тут без вариантов было (всёж логично - см. выше). А в "потоки" начали превращаться - если мне не изменяет память - с изданием у нас Таненбаума... т.е. в середине 90х примерно. И к 2000ым - на моей памяти - про "нити" уже никто (ну практически) не вспоминал. Т.е. threads стали "потоками" прям очень быстро... хотя, какой-нибудь spinWait никуда не делся (да он и сейчас - никуда не делся, впрочем), но никого это "не парило". И то, что "потоки" (это которые flow) уже были - никого не смутило, просто те "потоки" (flow) они у процессора, а эти "потоки" (которые thread) - они у ОС. И всё просто и понятно всем, кто в теме. Да и "семантически" они похожи - всё вообще шикарно. А то "нити" какие-то :-)
Как-то так, если "на пальцах".
А то, что потом и в cpu появились свои собственные threads - ну бывает. Оно так-то про "немножко другое"... но, "крякает, как утка..." - все дела. Все уже привыкли к тому, что "это" - threads.
P.S. Блин... это ответ был на верхнее сообщение топика. То, которое про "Изначальный перевод слова "thread" не очень удачный просто". Сорри - промахнулся.
Это только если какой-то аналог RPC делается. Сообщение это данные. Обычно, если там и есть имя - это тип этих данных. Но ни как не тип потребителя этих данных. Тем более, что обработчик - вообще говоря - может быть и не один.
С одной стороны, хочется уточнить: достаточно для чего? А с другой - формализмы зав. типов - это "охренеть как смело" (с) :-)
Мой это какой?! Я - вроде как - никаких новых формализмов не вводил.
Каким образом?! Я ещё могу представить, как из "здоровенной схемы, описывающую сообщение" выводятся функциональные типы. Это не сложно. Но, что они вам дадут?! Что бы "выйти" на методы нам нужны имена. А тут они - буквально - любые.
Только на уровне формализма подтипа. Его - чаще всего - не достаточно. Поэтому там где нужных формализмов в языке нет, возникают всякого рода "техники программирования", которые переносят их (недостающие формализмы) на уровень реализации.
:-) Ну вот как это объяснить "на пальцах"?
Смотрите... вот у нас есть обычные типы (что бы это не значило). А есть функциональные типы. Какая между ними "разница"? Достаточно ли будет сказать, что функциональные имеют более высокий порядок? И в чём разница между типами разного порядка?
Или - с другой стороны заход - есть тип, а есть его экземпляры (что бы это не значило). Вот представьте себе эдакий НЕХ - экземплярами которого являются типы. Какая между ними разница? Мы на уровне типов даже выразить этот НЕХ (да... это про категорию типов) не сможем :-(
Т.е. эти вот "протоколы"/"поведения"/"классы типов" - это всё про "ограничения", которые накладываются не на уровне самих типов. Это про ограничения уровнем (я порой и не одним) выше, так сказать.
Когда вы реализуете "интерфейс", вы - фактически (через subtyping) - накладываете ограничения на [используемые] типы. С одной стороны, это вроде как "то, что нужно". А с другой - очень часто, это "не те" (либо слишком строгие, либо слишком слабые) ограничения, которые мы (на самом деле) хотим. По большому счету - это из-за того, что сам "интерфейс" - это тоже тип. Соответственно, он и описывается исключительно в "терминах" типов, со всеми их ограничениями. Даже если притащить сюда ещё и параметрический полиморфизм, то ограничения на параметры обобщенного типа - всё равно описываются в терминах типов. Не вырваться :-(
А если мы будет - по сути - тоже самое делать "уровнем выше", то никаких проблем такого рода у нас просто не будет.
Если идти дальше, то это уже какой-то "код" надо будет писать, чтобы показать "разницу". Оно надо?
Какой "объем знаний о получателе" это нам добавляет?
:-) А это точно нужно знать отправителю?!
Например, потому что "что, куда и когда" определяется не отправителем (и не получателем), а протоколом. Точнее, контекстом, в котором сейчас находится отправитель, и протоколом, который этим самым контекстом задается.
Если вам удобнее смотреть на это (контекст и протокол) через призму "интерфейсов", то упрощенно это можно представить себе некоторый медиатор (в терминах GoF).
Ну вот и представьте, что это таки может быть "формализованное в языке".
Если я правильно понял о чём речь, то "компилятор" - сам по себе - тут вообще не при чем. Статический анализ применим и к языкам с динамической типизацией. Главное, чтоб она (типизация) строгой была.
Но я-то не про типизацию... я про связывание (binding). Оно - вообще говоря - ортогонально типизации.
На самом деле, это охренеть какое преимущество :-)
Зачем?! Если "на пальцах": что вы "знаете" о получателе, когда отправляете сообщение, например, в какой-нибудь exchange AMQP брокера? Ну или, когда добавляете запись в какой-то topic kafka - вы много "знаете" о том, кто будет эту запись потом "читать"? А когда уже вы читаете запись и топика, вы что-либо "знаете" об "отправителе"? Зачем это вам?!
Ну, нет. "Интерфейсы" (а точнее, их реализация) - это просто "вот такая вот" форма subpyping'а. А protocol'ы smalltalk'а, behaviour 'ы erlang'а и прочая, и прочая - это не subtyping ни разу.
Я в одном, пожалуй, соглашусь... если рассматривать исключительно "статические" системы, то такого рода преимущество не совсем очевидно. Ну действительно - "зачем козе баян", если "всё, что может быть" мы и так уже описали "до того как".
Но как только вы перейдете к "динамическим" системам - полезность "такой штуки" становится более чем очевидна. Представьте себе, что у вас "рождаются и умирают" не только экземпляры типов, а сами типы - "рождаются и умирают".
Наверное тем, что для того, что послать сообщение получателю, про получателя не нужно знать вообще ничего?
Про "как" и "почему так", Алан "сделал в Smalltalk" лучше, имхо, почитать у самого Алана. Благо его The Early History of Smalltalk давно есть в сети. Очень познавательно.
Вот вам цитата. Это прям самое начало... даёт представление о "какую проблему решаем".
Но лучше самому почитать, имхо. Там не много...
"посылка сообщений" - aka message passing - это то, что, в конечно счете, станет решением для этой самой "small scale one". То, что он сам потом назовет "extreme late binding of all things".
:-) Прям сразу "с козырей". Можно же было как-то "тихонечко"... через композицию там... подвести к теории типов... сумма типов, произведение... то сё... показать, что subclassing это такой "ugly kid Joe", в смысле суммы типов... ну и потому же - да - "шарахать" :-)
Причем тут message passing?! Оно - не "цель" (ООП), а лишь инструмент (один из). Да и смысл "сокрытия" совсем не в том, что состояние не видно "из вне". А в том, что его (состояние) нельзя изменить "из вне".
"Открытое" состояние (и даже просто его принципиальная возможность в) - есть основная часть критики "оопешности" таких (и не только этих) языков/систем. Это, на всякий случай... вдруг вы не в курсе.
Как-то всё с ног на голову, имхо. Они - парадигмы т.е. - действительно про "одну цель". И DRY - если попытаться рассматривать этот принцип, как парадигму - тут не исключение. Цель эта - борьба со сложностью.
Если "маненько упростить", то ОПП - как парадигма - предлагает "сокрытие" состояния, как основной способ борьбы со сложностью (реализуемых систем). Т.е. вот прям буквально: если все изменения состояния S будут локализованы в S, то это не привнесет в систему (в которую S будет включено) сложность самой S. И в этом смысл. Речь - естественно - о "сложности системы" в инженерном смысле.
А дальше возникает, например, т.н. "позднее связывание" - как ответ на вопрос: "А как бы нам таки уже включить это самое S в нашу замечательную систему?" И т.з. целостности, его ("позднее связывание") тоже можно рассматривать (более того, так обычно и делают), как часть парадигмы ООП. Но важно понимать, что оно - не самоценно. И без первого ("сокрытия" состояния) - вообще говоря, имеет мало смысла в этой (ООП) парадигме. И, упомянутый вами, message passing - это как раз одни из способов (да, наверное самый универсальный, но таки один из) получить это самое "позднее связывание".
Я бы сказал - если "на пальцах" - причина: это мочь реализовывать/развивать более сложные системы, чем до этого. О "работе программиста" речь если и идет, то как о "третьей с конца по важности".
Это вполне конкретные понятия. Их просто "нилюбют". Гораздо интереснее обсуждать "другое". Но, как только, обсуждение переводится хоть сколько-то академическую плоскость (из "научно-популярной") всё сводится к состоянию и его "открытости"/"закрытости". Иногда ещё говорят не о "закрытости", а о "локальности изменений" состояния - сути это не меняет.
"структурирование данных" и "куча стрелочек" (DF/CF и прочие F) - это СП. Оно очень даже допускает "открытое состояние". Более того - по большому счету - оно всё и построено вокруг "не локальности изменений" глобального состояния. Без этого оно просто "ниработаит" (tm).
?!
Нет там никаких других "принципов". Точнее - они не имманентны "сокрытию состояния".
Цель - одна: скрыть состояние - т.е. "локализовать его изменения". Это сильно всё упрощает само по себе, знаете ли.
Извините, но как связаны "куча дублирующегося кода" и "наследование"? Точнее, как наличие второго исключает наличие первого? Или - по вашему - "наследование" - это про переиспользование чтоль?!
?! Наследование, это же один из способов получить тот самый полиморфизм. Способ. Один из. Т.е. вполне может быть полиморфизм без какого-либо наследования. Но вот "наследование без полиморфизма" я себе представить не могу. И мне вот сильно интересно, где вы видели это самое "наследование без полиморфизма"?
Скажу так... есть гораздо более удобные способы для, чем subclassing.
Всегда удивляюсь, почему практически любое (читай, не академическое) обсуждение ООП всегда (и очень быстро) скатывается к обсуждению каких-либо технических деталей?
Концептуально, ООП это же очень просто: сокрытие состояния. Как говорится, какое из этих двух слов вам не понятно? :-)
Остальное всё (вот буквально - всё) - это средства, которыми это самое сокрытие достигается в том, или ином случае. Ну либо "следствия" их использования. Всё, от какого-нибудь message passing до, какого-нибудь subtyping - это всего лишь "инструменты", т.е. суть - вообще не важны для понимания.
Да даже само понятие "объект" - вторично. Внезапно, ООП замечательно обходится из без "объектов" - не говоря уже про, какие-нибудь "классы"... да и "типы" вообще :-)
В этом смысле, показательно что ООП "ломается" как раз тогда, когда нет состояния. Нечего скрывать, не с чем становится работать - и тут же "рождаются драконы" разной степени монструозности и упоротости.
Резюмируя... ООП - это не про "классы" и т.п. Когда мы говорим об ООП, то - по сути - мы говорим лишь от том, что "открытое" состояние - не допустимо. В отличие от какого-нибудь СП, которое вполне себе это допускает. Остальное уже частности, особенности платформ, конкретных языковых средств и т.п., которые - если убрать сокрытие состояния - тут же теряют всякую ценность.
Из этого же, например, следует, что "противопоставление" ООП и ФП - это немножко "такое" (tm). По отношению к наличию "открытого" состояния - они категоричны.
Да и ФП, которое, одновременно, ООП - это "вообще норм" (tm). В том смысле, что если есть (ну вот надо нам) состояние, то мы его обязательно скрываем. Например, в "процессе" - как в Erlang, или, например, в специальном "типе" - как в Haskell.
Как-то сильно "на пальцах" и, местами, вообще небрежно и - соответственно - порой, получается вообще "не про то" :-(
Начиная, с атомов (в примерах, внезапно, не "% примеры атомов"), и заканчивая "сумбуром" про функции, модули и процессы.
Как вообще можно рассказывать про Erlang, в котором - буквально - всё есть сопоставление с образцом... и из которого "прорастает" весь его синтаксис, начиная не с концепции "сопоставления с образцом"?!
Во-первых, "как что-то плохое" (tm) :-)
А во-вторых, что-то мы как-то не наблюдаем толпы тех, кто уже... что несколько печалит.
На данный момент, единственное, что мы - как исполнители - точно знаем: это то, что если "говнокод" и является проблемой, то это "проблема издержек". Со всеми вытекающими...
Статья, имхо, заход прям очень уже не с той стороны.
Как мне кажется, "проблема говнокода" - она вообще не лежит в, скажем так, сколь-нибудь технологической плоскости.
Грубо говоря, т.к. важен только наблюдаемый результат и больше ничего, то никто не готов платить за абстрактный "красивый код". Вот и всё.
Всегда (ну ок... за редчайшим исключением) выбирается "быстро и не дорого". А даже когда выбирается "быстро и качественно" - по этим самым "качественно" всё равно подразумевается не некая абстрактная "красота кода" (которая сама по себе ещё и ничего не гарантирует с т.з. достижения результата), а совсем другие вещи.
Ну а то, что при достигнутом результате потом "главное, не трогать несущую трубу" (с) - вообще мало кого волнует. Да вообще никого, можно сказать :-)
Да-да-да :-)
"Выражение" - в смысле expression? Если да, то почему "подвыражение"? clause - с тем же успехом - и к statement (канон - внезапно - "оператор") может относится. Ну и оно вообще не про "под". Оно вполне себе самостоятельным может же быть. Да хоть уже упоминаемая function clause в erlang - это вполне себе самодостаточная языковая конструкция.
Но канона для clause в этом смысле - насколько я знаю - нет. Попытки есть... типа "предложение", "клаузула" и т.п. - но это, имхо, такое.
Эт хоть и не вам ответ был а для @sibirier, но таки спрошу: Таненбаума читали? Оба два? :-)
Ну я чтоль виноват, что термин thread такой древний? :-) И к cpu - с его flow - он вообще не относился пол века почти. Он из "теории" операционных систем так-то.
Но цикл планировщика потоков (thread scheduler) - это и сейчас spin. Да и spin lock со spin wait'ом никуда не делись.
Оно не правильно "выглядит". Эти ваши "водяные потоки" как-то можно ещё натянуть на те threads, что в cpu. А те, что в ОС - не "водяные", и не "потоки"... в том смысле, что их идея - как раз в обратном.
Слушайте... вот реально "и выросло поколение" :-) Без обид, только :-)
Изначально термин thread вообще к процессору (cpu) никакого отношение не имел. Это просто было лишено какого-либо смысла, ибо "там" - одни control flow и "вертись как хочешь". Thread - в изначальном смысле - это "часть" процесса (process) - который не "вообще" - а в некоторой операционной системе... которая тож не просто так... но эт не суть важно.
И вот ими (этими самыми thread'ами) управляет не cpu (он этого просто не умеет), а специальный планировщик (scheduler) - часть той самой "хитрой" ОС. Оно и сейчас, по большому счету, так.
Так вот - thread они потому, что scheduler... ну, он делает spin... вот - т.е. он буквально "прядет" их, переключая т.н. контекст исполнения :-) Control flow-то один.
Тут вот у нас "штука", которая делает spin, а это вот - значит - threads... всё логично :-)
И threads весьма долго и были "нитями" - тут без вариантов было (всёж логично - см. выше). А в "потоки" начали превращаться - если мне не изменяет память - с изданием у нас Таненбаума... т.е. в середине 90х примерно. И к 2000ым - на моей памяти - про "нити" уже никто (ну практически) не вспоминал. Т.е. threads стали "потоками" прям очень быстро... хотя, какой-нибудь spinWait никуда не делся (да он и сейчас - никуда не делся, впрочем), но никого это "не парило". И то, что "потоки" (это которые flow) уже были - никого не смутило, просто те "потоки" (flow) они у процессора, а эти "потоки" (которые thread) - они у ОС. И всё просто и понятно всем, кто в теме. Да и "семантически" они похожи - всё вообще шикарно. А то "нити" какие-то :-)
Как-то так, если "на пальцах".
А то, что потом и в cpu появились свои собственные threads - ну бывает. Оно так-то про "немножко другое"... но, "крякает, как утка..." - все дела. Все уже привыкли к тому, что "это" - threads.
P.S. Блин... это ответ был на верхнее сообщение топика. То, которое про "Изначальный перевод слова "thread" не очень удачный просто". Сорри - промахнулся.
Это в формализме любого языка есть. SQL тут не оригинален.
Конкретно я это перенял, если мне память не изменяет, у кого-то из Erlang'истов... как часть устойчивого выражения: "функциональная кляуза" :-)
Это про clause :-)
Expression, statement, clause и т.п... вот в этом контексте.