Здравствуйте, ути-пути, Вы писали:
УП>Какие-то жабные стереотипы. Большинство языков на это не повелись, и в них все это легко и непринужденно решается свободными функциями. Да и в жабе это возможно, если рассматривать часть классов лишь как пространства имен. УП>Ничто не мешает на плюсах написать "НарисуйКартину(холст, кисть)", а ведь только на этом все эти претензии строятся
Не, не только на этом. На С тоже можно написать "НарисуйКартину(холст, кисть)", но это не сделает его функциональным.
Вот смотри, как это работает. Допустим, мы хотим написать функцию filter(collection, predicate) так, чтобы можно было применить её к коллекции целых, и отфильтровать все меньше определённого числа.
На классическом С нам придётся изобрести что-то типа
(это я пренебрегаю сложностью изобретения концепции "произвольная перечислимая коллекция" на C)
и после этого мы будем писать что-то вроде
collection filterlessthan(collection c, int limit)
{
return filter(collection, &lessthan, &limit);
}
bool lessthan(int a, void* limit)
{
return a < *(int*)limit;
}
То есть помимо (на самом деле независимо от) возможности писать свободные функции нужна возможность описывать замыкания неявно.
Потому что без этого наш код сильно лучше не становится — мы сможем заманить корявую пару из (predicate, context) на функтор, но конструировать функтор придётся по-прежнему руками.
А когда мы оборудованы возможностью делать лексические замыкания и конструировать кортежи, ООП становится не очень нужно — его можно эмулировать практически без потерь.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, D. Mon, Вы писали:
DM>Это миф, кстати. Многие ФЯзыки или до сих пор не умеют в многоядерность, или много лет не умели. У многих самая популярная структура данных — односвязный список, работу с которым параллелить довольно сложно.
Эмм, а в чём сложность, если он иммутабельный? Естественно, в предположении, что "работа" стоит больше, чем собственно итерирование по списку, поэтому skip(100) выполняется достаточно быстро по сравнению с process(take(100)).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, mrTwister, Вы писали:
T>1) ООП вынуждает привязывать методы к данным, хотя чаще всего в домене эта связь отсутствует. В результате, эта связь делается случайным образом, что приводит к каше в коде. Например, можно написать код в стиле ООП: "Холст.НарисуйКартину(кисть)", либо "Кисть.НарисуйКартину(холст)". Какой из этих двух вариантов правильный? Тут нет неправильного варианта. Реализован будет произвольный из них, смотря какая пятка зачесалась у программиста. Хотя в ФП стиле все однозначно: "НарисуйКартину(холст, кисть)". Нет никаких разночтений.
Вероятно, потому что правильным будет "Художник.НарисуйКартину(холст, кисть)"
T>2) Данные жёстко связаны с методами. Какой бы из описанных в предыдущем пункте мы не выбрали вариант, мы будем жёстко связаны принятым случайным образом решением. Если мы выбираем вариант "Холст.НарисуйКартину(кисть)", то НарисуйКартину жёстко привязана к холсту и мы сможем в дальнейшем рисовать картины исключительно только на холстах. В другом варианте мы будем жёстко привязаны к кисти и сможем рисовать только кистью. С другой стороны, при ФП декомпозиции, функция НарисуйКартину(где, чем) ни к чему не привязана и может рисовать на чём угодно и чем угодно, если это пригодно для рисования.
Мне кажется, ты не в курсе существования генерализации типов в ООП.
T>3) ООП провоцирует на создание большого количества лишних сущностей. Для борьбы с предыдущими проблемами приходится вводить большое количество искусственных сущностей, которые отсутствуют в исходном домене. Для этих сущностей часто даже невозможно придумать адекватного названия, вот и появляются всякие FooManager, BarHelper, все это обмазывается толстым слоем абстрактных фабрик, визиторов, репозиториев и прочих паттернов, которые для эксперта из доменной области звучат, как тарабарщина и мешают тем самым применению DDD. В результате программист вместо решения задачи из предметной области воюет с визиторами и декораторами.
Смотри выше.
T>В результате, ФП проект со временем менее подвержен превращением в кашу. В фп стиле легче писать правильно и труднее говнокодить.
То, ч то ты написал, можно резюмировать парой слов: "ООП ниасилил".
S>(это я пренебрегаю сложностью изобретения концепции "произвольная перечислимая коллекция" на C)
S>и после этого мы будем писать что-то вроде
S>
S>collection filterlessthan(collection c, int limit)
S>{
S> return filter(collection, &lessthan, &limit);
S>}
S>bool lessthan(int a, void* limit)
S>{
S> return a < *(int*)limit;
S>}
S>
S>То есть помимо (на самом деле независимо от) возможности писать свободные функции нужна возможность описывать замыкания неявно.
S>Потому что без этого наш код сильно лучше не становится — мы сможем заманить корявую пару из (predicate, context) на функтор, но конструировать функтор придётся по-прежнему руками.
S>А когда мы оборудованы возможностью делать лексические замыкания и конструировать кортежи, ООП становится не очень нужно — его можно эмулировать практически без потерь.
Здравствуйте, alexanderfedin, Вы писали:
A>Много бреда от незнания
Хорошо зашел!
S>>На классическом С нам придётся изобрести что-то типа S>... S>То есть помимо (на самом деле независимо от) возможности писать свободные функции нужна возможность описывать замыкания неявно.
Здравствуйте, alexanderfedin, Вы писали:
A>Вероятно, потому что правильным будет "Художник.НарисуйКартину(холст, кисть)"
Класс без состояния с одним методом? Это не ООП. Зачем этот класс вообще нужен, не проще ли оставить только один метод из класса? Не думаешь же ты, что если взять ФП программу, каждую функцию вынести в отдельный класс, то мы получим ООП программу?
A>Мне кажется, ты не в курсе существования генерализации типов в ООП.
Перекрестись
A>То, ч то ты написал, можно резюмировать парой слов: "ООП ниасилил".
ООП ниасилил тот, кто делает классы без состояния с одним методом, потому что это не ООП
Здравствуйте, varenikAA, Вы писали:
AA>>>Достаточно набора функций,
Y>>А если набор функций — это 10 функций? Не легче ли инжектировать 1 интерфейс?
AA>Уверены что в одном методе нужно 10 функций? AA>Возможно следует использовать композицию для получения из 10-ти 1-й функции.
Ну про это есть буква "I" в слове "SOLID".
Функций в интерфейсе должно быть столько, сколько надо, и не больше.
Если же их вообще все поодиночке передавать, то это будет IMHO капец.
Например, как будешь автокомплит (киллер-фичу ООП) реализовывать?
ООП взлетело в том числе потому, что оно позволяет "обучение по месту" в IDE (тот самый автокомплит),
давая пользователю возможность выбрать из списка вариантов, а не написать.
bnk>Если же их вообще все поодиночке передавать, то это будет IMHO капец. bnk>Например, как будешь автокомплит (киллер-фичу ООП) реализовывать?
Вместо IList передаешь структурку где Add/Remove — это тупо поля с делегатами нужной сигнатуры. Все, комплит тебе также предложит вызывать Add/Remove как только вобьешь точку.
На самом деле конечно можно обойтись или только интерфейсами, или только делегатами. Прелесть шарпа в мультипарадигменности: выбирай любой стиль, или смесь — и вперед гавнокодить )
AA>Получается, что функции высшего порядка делают код чище и проще, и интерфейсы тут как бы не особо уже нужны. AA>Достаточно набора функций, при наличии частичного применения
Интерфейс объединяет методы общей семантикой.
Интерфейс только лишь из одного метода — да, вызывает вопросы. Но вот IDisposable считаю удачным.
Изучение кода, особенно с решарпером в обнимку, проще на интерфейсах, это мой личный опыт. Скорее всего по той же причине что интерфейс наделяет семантикой даже один метод.
Однозначно ответа думаю нет. Везде надо чувство меры.
Недавно опробовал такой класик:
class AsDisposable : IDisposable
{
public AsDisposable(Action onDispose)
{}
//IDisposable
}
— тупо оберточка из делегата делающая из делегата IDisposable.
Хорошо зашло. using синтаксис придает смысл. А если б можно было имплементации фигачить лямбдами то и этого не надо было бы.
Здравствуйте, barn_czn, Вы писали:
AA>>Получается, что функции высшего порядка делают код чище и проще, и интерфейсы тут как бы не особо уже нужны. AA>>Достаточно набора функций, при наличии частичного применения
_>Интерфейс объединяет методы общей семантикой. _>Интерфейс только лишь из одного метода — да, вызывает вопросы. Но вот IDisposable считаю удачным. _>Изучение кода, особенно с решарпером в обнимку, проще на интерфейсах, это мой личный опыт. Скорее всего по той же причине что интерфейс наделяет семантикой даже один метод. _>Однозначно ответа думаю нет. Везде надо чувство меры.
_>Недавно опробовал такой класик:
_>
_>- тупо оберточка из делегата делающая из делегата IDisposable. _>Хорошо зашло. using синтаксис придает смысл. А если б можно было имплементации фигачить лямбдами то и этого не надо было бы.
В плане фичей под dotnet мне больше нравится F#
// (unit -> unit) -> IDisposable
let disp f = {new IDisposable with member it.Dispose() = f() }
use it = disp (fun _ -> printfn "it is work!")
it.Dispose()
//it is work!
Здравствуйте, D. Mon, Вы писали:
DM>В Хаскеле есть и зеленые потоки, вроде горутин, и обычные системные. Наверняка регулируется. Но толку-то, если сборщику нужно старое поколение собирать, он все равно все остановит, ибо старое поколение кучи общее для всех потоков, а чистить параллельно, как в свежих версиях Го, он пока не умеет, вроде.
Здравствуйте, Poopy Joe, Вы писали:
DM>>В Хаскеле есть и зеленые потоки, вроде горутин, и обычные системные. Наверняка регулируется. Но толку-то, если сборщику нужно старое поколение собирать, он все равно все остановит, ибо старое поколение кучи общее для всех потоков, а чистить параллельно, как в свежих версиях Го, он пока не умеет, вроде.
PJ>Разве не оно?! PJ>https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/local-gc.pdf
А оно есть в GHC из коробки? А не в чьей-то частной ветке?
IComparer<T> — вот пример уродского интерфейса который меня постоянно бесит.
Практически всегда удобнее передать делегат Comparison<T>. Но бывает что целевой класс (коллекция например с методом Sort) не умеет его принимать, подавай IComparer.
DM>А оно есть в GHC из коробки? А не в чьей-то частной ветке?
Из коробки.
-qg ⟨gen⟩¶
Default
0
Since 6.12.1
Use parallel GC in generation ⟨gen⟩ and higher. Omitting ⟨gen⟩ turns off the parallel GC completely, reverting to sequential GC.
The default parallel GC settings are usually suitable for parallel programs (i.e. those using GHC.Conc.par, Strategies, or with multiple threads). However, it is sometimes beneficial to enable the parallel GC for a single-threaded sequential program too, especially if the program has a large amount of heap data and GC is a significant fraction of runtime. To use the parallel GC in a sequential program, enable the parallel runtime with a suitable -N ⟨x⟩ option, and additionally it might be beneficial to restrict parallel GC to the old generation with -qg1.
Здравствуйте, Poopy Joe, Вы писали:
DM>>А оно есть в GHC из коробки? А не в чьей-то частной ветке?
PJ>Из коробки. PJ>Use parallel GC in generation ⟨gen⟩ and higher. Omitting ⟨gen⟩ turns off the parallel GC completely, reverting to sequential GC.
Насколько я понимаю, это совершенно другая вещь. Весь мир останавливается и старое поколение собирается в несколько тредов. Толку-то. Это не thread-local heaps и не параллельно выполнению программы.
Здравствуйте, D. Mon, Вы писали:
DM>Здравствуйте, Poopy Joe, Вы писали:
DM>>>А оно есть в GHC из коробки? А не в чьей-то частной ветке?
PJ>>Из коробки. PJ>>Use parallel GC in generation ⟨gen⟩ and higher. Omitting ⟨gen⟩ turns off the parallel GC completely, reverting to sequential GC.
DM>Насколько я понимаю, это совершенно другая вещь. Весь мир останавливается и старое поколение собирается в несколько тредов. Толку-то. Это не thread-local heaps и не параллельно выполнению программы.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Ну, я отвечал на " а чистить параллельно, как в свежих версиях Го, он пока не умеет, вроде." Чистить параллельно он умеет, [s]он не умеет это делать конкуретно.
Ну это я выразился неудачно. Имел в виду именно параллельно программе, т.е. конкурентно.