Комментарии 54
как человеку, далёкому от C# можете пояснить, почему без record type было плохо жить и почему с появлением такого типа жить станет проще, жить станет веселее?
+8
Потому что иммутабельное гораздо проще в эксплуатации (не надо делать копию на каждый чих, нет проблем с параллельным доступом, можно спокойно передавать куда угодно без риска испортить.
Но с текущим синтаксисом определение таким типов получается более громоздким, чем у мутабельных, что не есть хорошо.
Но с текущим синтаксисом определение таким типов получается более громоздким, чем у мутабельных, что не есть хорошо.
+4
Для иммутабельной структуры довольно удобно использовать структуру с readonly полями. Но в реальности это просаживает производительность, как и многие другие функциональные возможности. Так что использовать нужно с осторожностью.
+3
В реальности производительность просаживает как раз мутабельность. Из-за многочисленных копирований и особенно блокировок при многопоточном доступе.
К сожалению, сейчас структура с readonly полями требует для описание раза в два больше кода по сравнению с мутабельной версией.
К сожалению, сейчас структура с readonly полями требует для описание раза в два больше кода по сравнению с мутабельной версией.
-4
Зависит от того, о каком языке\среде речь. В «настоящих» функциональных языках типа Haskell или F# компилятор обеспечивает неизменяемость значений собственными проверками, поэтому под капотом передача значений может быть реализована дешевым способом, по ссылке. Однако статья про C#, а там ссылочные типы по умолчанию изменяемы и нужно либо описывать вся и всё через readonly-поля, либо использовать структуры, которые будут постоянно копироваться по значению.
+2
Так почему readonly-поля просаживают производительность-то?
+3
как я понял, огни просаживают скорость набора — приходится писать больше букаф, а это боль и страдание, да
-4
Сами по себе readonly-поля никак на производительность не влияют, однако требуют от разработчика внимательности и написания объемного кода. Однако просто пометить все поля этим атрибутом недостаточно: нужно, чтобы сами типы этих полей тоже были неизменяемыми — иными словами,
readonly List<int> Values
делать не следует, по очевидным причинам.+2
Думаю Bonart имел ввиду это: MICRO-OPTIMIZATION: THE SURPRISING INEFFICIENCY OF READONLY FIELDS
0
Для решения в том числе этой проблемы и написан RecSharp. Он генерирует классы, а не структуры
0
Это чтож за чушь то? Как не-мутабельные типы помогут избежать синхронизации при многопоточном доступе? Нука покажите ка мне не-мутабельный ConcurrentDictionary? Да и копирование это как раз к немутабельному коду, практически по определению.
Да, часть багов иммутабельность может предотвращать, но может привносить кучу новых а иногда и привносить чудовищные оверхеды, которые в принципе не решить железом, т.к. разница может быть на порядок (особенно страдают любители немутабельных графов, которые по логике программы все-таки надо изменять).
Да, часть багов иммутабельность может предотвращать, но может привносить кучу новых а иногда и привносить чудовищные оверхеды, которые в принципе не решить железом, т.к. разница может быть на порядок (особенно страдают любители немутабельных графов, которые по логике программы все-таки надо изменять).
-4
> Это чтож за чушь то?
Вы сегодня на редкость самокритичны.
> Как не-мутабельные типы помогут избежать синхронизации при многопоточном доступе?
Элементарно. При параллельном доступе к иммутабельному объекту синхронизации по записи нет в силу отсутствия записи, синхронизация по чтению не нужна в силу отсутствия изменений.
> Ну-ка покажите ка мне не-мутабельный ConcurrentDictionary?
ImmutableDictionary Как несложно догадаться, ему не надо быть Concurrent.
>Да и копирование это как раз к немутабельному коду, практически по определению.
Правда? Угадайте, почему ToArray() всегда делает копирование, даже если применяется к тому, что уже массив?
Угадайте, почему для иммутабельного массива легко делается оптимизация без копирования?
Попробуйте передать мутабельный объект сложному (или просто не вашему) методу, а потом воспользоваться его прежним состоянием. «Защитное» копирование начнет плодиться со страшной силой.
Далее — если у вас есть иммутабельный объект и нужен новый, отличающийся несколькими изменениями, то опять-таки никакого копирования не нужно: достаточно в новом объекте иметь ссылку на старый и набор изменений.
Есть такая хорошая книжка «Чисто функциональные структуры данных» Криса Окасаки — почитайте, разобраться поможет.
> Да, часть багов иммутабельность может предотвращать, но может привносить кучу новых а иногда и привносить чудовищные оверхеды
Любители забивать гвозди микроскопом есть всегда и везде, но это не значит что микроскоп — плохой и негодный инструмент.
Вы сегодня на редкость самокритичны.
> Как не-мутабельные типы помогут избежать синхронизации при многопоточном доступе?
Элементарно. При параллельном доступе к иммутабельному объекту синхронизации по записи нет в силу отсутствия записи, синхронизация по чтению не нужна в силу отсутствия изменений.
> Ну-ка покажите ка мне не-мутабельный ConcurrentDictionary?
ImmutableDictionary Как несложно догадаться, ему не надо быть Concurrent.
>Да и копирование это как раз к немутабельному коду, практически по определению.
Правда? Угадайте, почему ToArray() всегда делает копирование, даже если применяется к тому, что уже массив?
Угадайте, почему для иммутабельного массива легко делается оптимизация без копирования?
Попробуйте передать мутабельный объект сложному (или просто не вашему) методу, а потом воспользоваться его прежним состоянием. «Защитное» копирование начнет плодиться со страшной силой.
Далее — если у вас есть иммутабельный объект и нужен новый, отличающийся несколькими изменениями, то опять-таки никакого копирования не нужно: достаточно в новом объекте иметь ссылку на старый и набор изменений.
Есть такая хорошая книжка «Чисто функциональные структуры данных» Криса Окасаки — почитайте, разобраться поможет.
> Да, часть багов иммутабельность может предотвращать, но может привносить кучу новых а иногда и привносить чудовищные оверхеды
Любители забивать гвозди микроскопом есть всегда и везде, но это не значит что микроскоп — плохой и негодный инструмент.
+4
Эх, шикарный у вас мир. А что если я хочу с 10 потоков в словарь добавлять значения? Раз immutable то и никакой многопоточности не нужно — волшебство просто, жаль в реальной жизни не работает.
--Элементарно, при использовании велосипеда большие расстояния не преодолеваются, в силу того что педали крутить ноги устают, а на маленькие расстояния норм ездить.
Факт того что записывать иногда нужно вы игнорируете, да?
Иммутабельнный объект — это паттерн, а «наш код — иммутабельный» — это антипаттерн. В части случаев можно создать немутабельную оболочку для мутабельного объекта. Когда-то да, удобнее немутабельные, но далеко (очень далеко) не всегда.
Элементарно. При параллельном доступе к иммутабельному объекту синхронизации по записи нет в силу отсутствия записи, синхронизация по чтению не нужна в силу отсутствия изменений.Как вы собираетесь использовать велосипед вместо автомобиля, расстояния то большие?
--Элементарно, при использовании велосипеда большие расстояния не преодолеваются, в силу того что педали крутить ноги устают, а на маленькие расстояния норм ездить.
Факт того что записывать иногда нужно вы игнорируете, да?
Далее — если у вас есть иммутабельный объект и нужен новый, отличающийся несколькими изменениями, то опять-таки никакого копирования не нужно: достаточно в новом объекте иметь ссылку на старый и набор изменений.Ага, особенно хорошо это можно проделать для графа.
Попробуйте передать мутабельный объект сложному (или просто не вашему) методу, а потом воспользоваться его прежним состоянием. «Защитное» копирование начнет плодиться со страшной силой.
Иммутабельнный объект — это паттерн, а «наш код — иммутабельный» — это антипаттерн. В части случаев можно создать немутабельную оболочку для мутабельного объекта. Когда-то да, удобнее немутабельные, но далеко (очень далеко) не всегда.
-4
> А что если я хочу с 10 потоков в словарь добавлять значения
А что если я хочу использовать текущее состояние словаря для объемных расчетов?
> Факт того что записывать иногда нужно вы игнорируете, да?
Что именно, для чего и куда записывать? Вы всегда подменяете тезис во время дискуссии?
> Ага, особенно хорошо это можно проделать для графа.
Зависит от вида и способа хранения графа.
> В части случаев можно создать немутабельную оболочку для мутабельного объекта.
Правда? И как эта оболочка сохранит объект от изменений, сделанных при доступе помимо нее?
Мутабельный объект сугубо локальный и доступ к нему только через оболочку? Тогда его мутабельность значения не имеет вообще.
> Иммутабельнный объект — это паттерн, а «наш код — иммутабельный» — это антипаттерн
Пока не видел здесь никого, кто хотя бы упоминал тезис «наш код — иммутабельный» (кроме вас), а тем более его отстаивал
>Когда-то да, удобнее немутабельные, но далеко (очень далеко) не всегда.
А мужики-то и не знали. Тезис «иммутабельные удобнее всегда» никто не заявлял.
Что характерно, чрезвычайное по категоричности и голословности заявление «но в реальности это просаживает производительность» никакого отторжения у вас не вызвало.
PS: Подумайте, стоит ли начинать разговор с хамства, а продолжать попыткой заткнуть рот методом отключения микрофона. Вряд ли это сделает ваши аргументы весомее в глазах читателей.
А что если я хочу использовать текущее состояние словаря для объемных расчетов?
> Факт того что записывать иногда нужно вы игнорируете, да?
Что именно, для чего и куда записывать? Вы всегда подменяете тезис во время дискуссии?
> Ага, особенно хорошо это можно проделать для графа.
Зависит от вида и способа хранения графа.
> В части случаев можно создать немутабельную оболочку для мутабельного объекта.
Правда? И как эта оболочка сохранит объект от изменений, сделанных при доступе помимо нее?
Мутабельный объект сугубо локальный и доступ к нему только через оболочку? Тогда его мутабельность значения не имеет вообще.
> Иммутабельнный объект — это паттерн, а «наш код — иммутабельный» — это антипаттерн
Пока не видел здесь никого, кто хотя бы упоминал тезис «наш код — иммутабельный» (кроме вас), а тем более его отстаивал
>Когда-то да, удобнее немутабельные, но далеко (очень далеко) не всегда.
А мужики-то и не знали. Тезис «иммутабельные удобнее всегда» никто не заявлял.
Что характерно, чрезвычайное по категоричности и голословности заявление «но в реальности это просаживает производительность» никакого отторжения у вас не вызвало.
PS: Подумайте, стоит ли начинать разговор с хамства, а продолжать попыткой заткнуть рот методом отключения микрофона. Вряд ли это сделает ваши аргументы весомее в глазах читателей.
+3
Правда? И как эта оболочка сохранит объект от изменений, сделанных при доступе помимо нее?Я же написал — что в ряде случаев. Например для массива примитивных типов. Если не уверен вдруг — можно иногда сделать дешевую обертку. А вообще недекларируемое изменение объектов-параметров — это как раз зло в первую очередь. И опять же — ну избавились вы от одной проблемы введя везде immutable и получили проблемы в 10 других местах.
Пока не видел здесь никого, кто хотя бы упоминал тезис «наш код — иммутабельный»Да? А это что тогда? Конечно другими словами но это самое настоящее обобщение вида «иммутабельное — лучше».
Потому что иммутабельное гораздо проще в эксплуатации (не надо делать копию на каждый чих, нет проблем с параллельным доступом, можно спокойно передавать куда угодно без риска испортить.
А что если я хочу использовать текущее состояние словаря для объемных расчетов?Что? Как? Вам его надо обновлять.
Что именно, для чего и куда записывать? Вы всегда подменяете тезис во время дискуссии?
Ок. Давайте я приведу простую задачу. Будем подразумевать что и у меня и у вас есть библиотечные классы, у меня — MyConcurrentDictionary, у вас — MyImmutableDictionary.
Итак задача — простой кодер потока слов. Есть 40 потоков IEnumerable<string> — надо вернуть 40 потоков IEnumerable<int>. Причем конечно каждому слову во всех 40 потоках должен быть поставлен в соответствие один и тот же int.
Мой код (в классе у меня мутабельный concurrent dictionary и обычный мутабельный int):
public IEnumerable<int> Convert(IEnumerable<string> strings)
{
foreach(string s in strings)
{
yield return _mySharedMutableConcurrentDictionary.MyGetOrAdd(s, () => Interlocked.Increment(ref _mySharedMutableInt));
}
}
Давайте теперь вы приведете свой вариант кода допуская что любые стандартные не специфичные для этой задачи примитивы у вас есть.
PS: Подумайте, стоит ли начинать разговор с хамства,Я критиковал ваш пост, а не вас лично. Чушь все время от времени пишут.
-1
>Например для массива примитивных типов.
Массив примитивных типов вполне себе изменяем, если откуда-то еще, кроме оболочки к нему есть доступ как к массиву. Совсем плохо, что оболочка не может это контролировать.
>Конечно другими словами но это самое настоящее обобщение вида «иммутабельное — лучше».
Только если игнорировать обсуждаемую статью и даже конкретный вопрос первого комментатора.
Он спрашивал про record'ы, почему без них плохо и чем с ними лучше.
Т.е. вместо темы «иммутабельность везде» — реальная польза конкретно от record'ов
Вряд ли вы будете отрицать что вот такой класс
… в рамках текущего синтаксиса описывается гораздо проще чем его иммутабельный аналог:
… притом, что иммутабельные версии таких и подобных им типов реально удобнее и надежнее.
Про иммутабельность всегда и везде вести речь в контексте не отдельно взятых record'ов, а .NET целиком нет никакого смысла.
>Что? Как? Вам его надо обновлять.
Мне? Смотря что мне важно для задачи — актуальность, консистентность или все сразу.
>Итак задача — простой кодер потока слов. Есть 40 потоков IEnumerable — надо вернуть 40 потоков IEnumerable. Причем конечно каждому слову во всех 40 потоках должен быть поставлен в соответствие один и тот же int
Без дополнительных условий в вашей задаче лучше использовать мутабельный словарь.
Массив примитивных типов вполне себе изменяем, если откуда-то еще, кроме оболочки к нему есть доступ как к массиву. Совсем плохо, что оболочка не может это контролировать.
>Конечно другими словами но это самое настоящее обобщение вида «иммутабельное — лучше».
Только если игнорировать обсуждаемую статью и даже конкретный вопрос первого комментатора.
Он спрашивал про record'ы, почему без них плохо и чем с ними лучше.
Т.е. вместо темы «иммутабельность везде» — реальная польза конкретно от record'ов
Вряд ли вы будете отрицать что вот такой класс
public sealed class Mutable
{
public Type1 PropertyA { get; set; }
public Type2 PropertyB { get; set; }
}
… в рамках текущего синтаксиса описывается гораздо проще чем его иммутабельный аналог:
public sealed class Immutable
{
public Immutable(Type1 valueA, Type2 valueB)
{
fieldA = valueA;
fieldB = valueB;
}
public Type1 PropertyA => fieldA;
public Type2 PropertyB => fieldB;
private readonly Type1 fieldA;
private readonly Type2 fieldB;
}
… притом, что иммутабельные версии таких и подобных им типов реально удобнее и надежнее.
Про иммутабельность всегда и везде вести речь в контексте не отдельно взятых record'ов, а .NET целиком нет никакого смысла.
>Что? Как? Вам его надо обновлять.
Мне? Смотря что мне важно для задачи — актуальность, консистентность или все сразу.
>Итак задача — простой кодер потока слов. Есть 40 потоков IEnumerable — надо вернуть 40 потоков IEnumerable. Причем конечно каждому слову во всех 40 потоках должен быть поставлен в соответствие один и тот же int
Без дополнительных условий в вашей задаче лучше использовать мутабельный словарь.
0
Массив примитивных типов вполне себе изменяем, если откуда-то еще, кроме оболочки к нему есть доступ как к массиву. Совсем плохо, что оболочка не может это контролировать.Еще раз перечитайте. Такая оболочка — простой способ защитить некоторые типы от условного «незнакомого» кода. Это такой костыль который немного заменяет отсутствие const.
Про то что написать public const class X было бы проще чем делать его таким сейчас — это да, тут как бы по определению. Но я о том что это решает задачу которую решать по хорошему надо по другому, а иногда и вовсе не надо решать.
0
В рамках текущего синтаксиса (C# 6) всё описывается немного проще:
get-only свойства ведут себя точно так же, как readonly поля.
public sealed class Immutable
{
public Immutable(Type1 valueA, Type2 valueB)
{
PropertyA = valueA;
PropertyB = valueB;
}
public Type1 PropertyA { get; }
public Type2 PropertyB { get; }
}
get-only свойства ведут себя точно так же, как readonly поля.
+3
К слову. Большая часть случаев где оправдано применение immutable объектов решаются гораздо более красиво при введении константных методов и константных ссылок, одна из немногих вещей которых мне не хватает в C#.
0
Не понял этот ваш тезис.
Допустим, есть такой объект
Создаём его:
что помешает мне вот так испортить объект:
Допустим, есть такой объект
class Unit {
int Id;
string Name;
}
Создаём его:
// пусть val - неизменяемая ссылка
val unit = new Unit {Id = 42, Name = "test"};
что помешает мне вот так испортить объект:
unit.Id = 13;
0
То что если вы знаете что объект не должен меняться — вы передаете его как «const unit» и его никто не меняет. Если вы передаете без const — вы явно говорите что менять можно. А ненамеренное изменение объекта после создания в теле того же метогда где он был создан — это достаточно редкий случай (и от ошибок не страхует нигде). Ведь если его не надо изменять — его создают константным, а если надо — то тут что так что так ошибиться можно (например при immutable — создать измененную копию и потом передать старый объект вместо нового), и вообще если надо менять — то это как раз сигнал что immutable не нужен.
0
Человека может const или val и остановит, а вот такой метод
— нет.
Я считаю, что если объект неизменяемый, то не должно быть способа его изменить. Это удобно, когда, например, нужно реализовать rollback операции или цепочки операций: просто берём старую версию объекта без боязни, что её испортил метод вроде метода выше.
void Spoil(Unit unit)
{
unit.Id = 13;
}
— нет.
Я считаю, что если объект неизменяемый, то не должно быть способа его изменить. Это удобно, когда, например, нужно реализовать rollback операции или цепочки операций: просто берём старую версию объекта без боязни, что её испортил метод вроде метода выше.
0
А кто будет вызывать такой метод — будет знать что метод изменяет объект, этот метод явно об этом говорит в своей сигнатуре «я меняю объект unit». Если такой метод есть — значит объект изменять надо. Значит перед вызовом или сделают копию или вызов правильный и объект надо поменять. Сейчас есть проблема что не понятно меняет метод объект или нет, если он не меняет — это просто так нельзя проверить. Если введут const — проблем не будет с этим.
Конечно останутся сценарии когда immutable оправдан, даже после введения const. Некоторые (не все) случаи сценариев с rollback'ом (т.е. ведение журнала) — хороший пример.
Конечно останутся сценарии когда immutable оправдан, даже после введения const. Некоторые (не все) случаи сценариев с rollback'ом (т.е. ведение журнала) — хороший пример.
-1
Ничего он не говорит в своей сигнатуре. void может означать как «я меняю объект unit» так и «я вывожу на экран объект unit». В общем, между соглашениями/правилами и физической невозможностью поменять объект я выбираю второе, ибо надёжнее.
Насчёт аргумента «иногда нужно менять объект» отвечу, что обычно на любую реализацию с изменяемыми объектами найдётся реализация с неизменяемыми
Насчёт аргумента «иногда нужно менять объект» отвечу, что обычно на любую реализацию с изменяемыми объектами найдётся реализация с неизменяемыми
0
Если в языке есть const то сигнатура без const говорит «я буду этот объект менять». Если в сигнатуре есть const — метод говорит «я ничего менять не буду в объекте». Прямо физических ограничений на изменение объекта у вас нету нигде. В C# есть рефлексия, есть еще unsafe код, поменять можно что хотите. А еще можно вызвать нативную dll'ку которая поменяет что угодно. Но гарантий const по опыту других языков — достаточно.
что обычно на любую реализацию с изменяемыми объектами найдётся реализация с неизменяемымиЯ выше показал что не на любую.
0
>>Но гарантий const по опыту других языков — достаточно.
А по моему опыту — недостаточно.
>>Прямо физических ограничений на изменение объекта у вас нету нигде. В C# есть рефлексия
Согласен. Тут только язык переделывать. Но всё-же, это сложнее, чем вызвать setter.
>>Я выше показал что не на любую.
Во-первых, я написал, что «обычно». Во-вторых — не показали. Поясню. Вы приводили в пример запись из 10 потоков в ConcurrentDictionary, но такой алгоритм — это скорее всего решение какой-то другой задачи, и не факт, что её нельзя было решить другим образом. Из вредности я тоже могу придумать задачу, которую с mutable значениями будет решать очень геморройно, но зачем?
Я понимаю вас и вашу позицию, но мой опыт говорит о другом. Давайте не будем дальше спорить и уважать позицию друг друга?)
А по моему опыту — недостаточно.
>>Прямо физических ограничений на изменение объекта у вас нету нигде. В C# есть рефлексия
Согласен. Тут только язык переделывать. Но всё-же, это сложнее, чем вызвать setter.
>>Я выше показал что не на любую.
Во-первых, я написал, что «обычно». Во-вторых — не показали. Поясню. Вы приводили в пример запись из 10 потоков в ConcurrentDictionary, но такой алгоритм — это скорее всего решение какой-то другой задачи, и не факт, что её нельзя было решить другим образом. Из вредности я тоже могу придумать задачу, которую с mutable значениями будет решать очень геморройно, но зачем?
Я понимаю вас и вашу позицию, но мой опыт говорит о другом. Давайте не будем дальше спорить и уважать позицию друг друга?)
0
А по моему опыту — недостаточно.Ваш опыт идет вразрез с опытом индустрии.
Единственная проблема с const — это когда люди нигде не пишут его, но это мало отличается от того чтобы всегда на вход просить mutable объект, лечится прочтением пары книжек по языку и стилю программирования. Других проблем с const которые решал бы immutable я не вижу пока.
Согласен. Тут только язык переделывать. Но всё-же, это сложнее, чем вызвать setter.При чем тут setter? Я же написал — что в данном случае надо писать const метод, тогда будет видно что этот метод не изменяет значения.
Когда вы делаете immutable класс вы говорите что неизменяемость — свойство класса. В большинстве случаев неизменяемость — свойство объекта, а не класса (ну не может класс Person иметь свойство неизменяемости, вот класс LogItem может это подразумевать). А с неизменяемостью объектов хорошо справляется const.
и. Поясню. Вы приводили в пример запись из 10 потоков в ConcurrentDictionary, но такой алгоритм — это скорее всего решение какой-то другой задачиКакой задачи? Я привел описание задачи и ее решение.
Из вредности я тоже могу придумать задачу, которую с mutable значениями будет решать очень геморройно, но зачем?Так я не отрицаю что такие задачи есть (не то чтобы геморойно, просто в специфичных задачах плюсы перевешивают минусы и без immutable больше вероятность ошибок). Я говорю только то что далеко не все задачи — такие, многие задачи гораздо лучше решаются с mutable типами.
0
>>Ваш опыт идет вразрез с опытом индустрии.
Очень громкое заявление. «Индустрия» это и C# и C++ и Scala и Haskell и у меня такое чувство, что «опыт индустрии» это такая же иллюзорная вешь, как «общечеловеческая мораль».
>> при чём тут setter?
При том, что вот такой код
в случае использования моих record'ов (ну или record'ов в других языках) просто не скомпилируется. Const такого не обеспечит.
Очень громкое заявление. «Индустрия» это и C# и C++ и Scala и Haskell и у меня такое чувство, что «опыт индустрии» это такая же иллюзорная вешь, как «общечеловеческая мораль».
>> при чём тут setter?
При том, что вот такой код
void Spoil(Unit unit)
{
unit.Id = 13;
}
в случае использования моих record'ов (ну или record'ов в других языках) просто не скомпилируется. Const такого не обеспечит.
0
Еще раз:
1) Использовать везде immutable — это такая же глупость как использовать везде какой-либо другой специфичный паттерн
2) Я уже раз 10 сказал что есть ситуации где immutable лучше — но это далеко не все
3) Если у вас есть такой код — то вы при написании кода очевидно говорите «сейчас я вызову метод который объект поменяет», в большинстве случаев вы его даже вызвать не сможете. И опять же — единственная разница что в случае с const вы говорите «мой объект нельзя изменять» а в случае с immutable вы говорите «все объекты этого класса изменять нельзя». Второй вариант настолько строгий что применяется достаточно избирательно.
4) Я уже приводит примеры где просто очевидно с первого взгляда что immutable все усложнит и испортит, если у вас Unit — это что-то типа LogItem — то я же и не спорю что можно сделать это immutable в ряде случаев.
В очень большом количестве сценариев shared data между потоками — immutable только портит все, причем чем интенсивнее вычисления/изменения — тем очевидно больше он все портит.
А опыт индустрии — это посмотрите сколько вакансий открыто на императивные языки, и сколько на на чисто функциональные. Соотношение будет не в пользу последних.
1) Использовать везде immutable — это такая же глупость как использовать везде какой-либо другой специфичный паттерн
2) Я уже раз 10 сказал что есть ситуации где immutable лучше — но это далеко не все
3) Если у вас есть такой код — то вы при написании кода очевидно говорите «сейчас я вызову метод который объект поменяет», в большинстве случаев вы его даже вызвать не сможете. И опять же — единственная разница что в случае с const вы говорите «мой объект нельзя изменять» а в случае с immutable вы говорите «все объекты этого класса изменять нельзя». Второй вариант настолько строгий что применяется достаточно избирательно.
4) Я уже приводит примеры где просто очевидно с первого взгляда что immutable все усложнит и испортит, если у вас Unit — это что-то типа LogItem — то я же и не спорю что можно сделать это immutable в ряде случаев.
В очень большом количестве сценариев shared data между потоками — immutable только портит все, причем чем интенсивнее вычисления/изменения — тем очевидно больше он все портит.
А опыт индустрии — это посмотрите сколько вакансий открыто на императивные языки, и сколько на на чисто функциональные. Соотношение будет не в пользу последних.
0
Что-то про const слишком уж много оптимизма на мой взгляд.
Метод, указывающий параметр как const, обязуется его не менять.
Это очень сильное обязательство, особенно если метод вызывает другие методы, у которых есть прямой прямой или косвенный доступ к содержимому параметра.
А какие ограничения наложены на того, кто делает вызов метода с const параметром? Что мешает после вызова изменить переданный ранее объект и внезапно обрушить задачу в другом потоке, которую создал тот самый злосчастный метод?
Да, const позволяет компилятору выполнить некоторые оптимизации и отловить некоторые явные нарушения. Но с обеспечением взятых на себя обязательств все гораздо хуже, чем для иммутабельных объектов.
Метод, указывающий параметр как const, обязуется его не менять.
Это очень сильное обязательство, особенно если метод вызывает другие методы, у которых есть прямой прямой или косвенный доступ к содержимому параметра.
А какие ограничения наложены на того, кто делает вызов метода с const параметром? Что мешает после вызова изменить переданный ранее объект и внезапно обрушить задачу в другом потоке, которую создал тот самый злосчастный метод?
Да, const позволяет компилятору выполнить некоторые оптимизации и отловить некоторые явные нарушения. Но с обеспечением взятых на себя обязательств все гораздо хуже, чем для иммутабельных объектов.
0
Это очень сильное обязательство, особенно если метод вызывает другие методы, у которых есть прямой прямой или косвенный доступ к содержимому параметра.А как он может вызвать другие методы которые параметр изменяют?
Что мешает после вызова изменить переданный ранее объект и внезапно обрушить задачу в другом потоке, которую создал тот самый злосчастный метод?Самый очевидный подход — это понимать разницу между ссылкой и указателем, если метод продлевает время жизни параметра до момента после момента завершения метода — он принимает указатель, иначе — ссылку.
А что если в другом методе нужно чтобы объект этого типа был общим? Тогда мне придется делать кучу извращений которые гораздо более корявые чем копирование mutable объекта для первого метода. Я же 11й раз повторяю — что есть сценарии где immutable лучше, но это далеко не все. Кстати, копирования в приведенном случае можно избежать использовав паттерн freezable object. И overhead будет гораздо меньше чем overhead от разруливания ситуации если второй метод требует shared объект (а это разруливается по сути или созданием mutable класса или выпихиванием синхронизаций и кучей копирований при изменении, у обоих подходов overhead больше а второй еще и не в пример медленнее и сложнее).
0
В энтерпрайзе в первую очередь важна простота кода, а не производительность. Да и зачастую куда дешевле купить новую железку, чем тратить кучу времени на отлов кучи багов, которую за собой принесет использование не иммутабельных типов.
0
Отлично. Задача — какая-либо работа с большими структурами данных (ну пусть это будут графы). Мутабельный код потребует аккуратно реализовать синхронизацию и дальше позволит работать с этой структурой в параллель хоть с 40 потоков, операции могут не требовать выделения новой памяти и все будет очень быстро. А теперь делаем все это немутабельным. Еще большой вопрос уменьшится ли сложность кода (вместо багов в синхронизации у нас появятся баги с возможностью залинковать старую версию объекта), но вот производительность просядет настолько, что никакое новое железо не поможет. Железку можно купить чтобы не делать тонны микрооптимизаций которые еще не известно экономят ли или вредят в конечном итоге, а с иммутабельностью даже далеко не всегда очевиден выигрыш в плане сложности кода и количества багов, не говоря уже о проблемах с производительностью.
-1
Правда?
Обычно что-то на этапе «аккуратно реализовать синхронизацию» мешает при «работать с этой структурой в параллель хоть с 40 потоков» сделать чтобы «операции могут не требовать выделения новой памяти и все будет очень быстро».
Обычно что-то на этапе «аккуратно реализовать синхронизацию» мешает при «работать с этой структурой в параллель хоть с 40 потоков» сделать чтобы «операции могут не требовать выделения новой памяти и все будет очень быстро».
-1
Ну если вы не можете реализовать — это не значит что так никто не делает. Да, для новичков в части задач проще когда все вокруг немутабельно и сломать ничего нельзя, хотя в другой части это делает код еще более запутанным.
Простой пример — K-арное (K — фиксировано) дерево, в котором запускаем много потоков которые должны его переформировать в соответствии с какой-либо задачей. Деревья перемещаются, часть ребер удаляется, часть — добавляется. Выделения памяти — нету, при мутабельной структуре все происходит достаточно просто и главное — очень быстро. А теперь попробуйте сделать это на немутабельном дереве так же быстро.
Простой пример — K-арное (K — фиксировано) дерево, в котором запускаем много потоков которые должны его переформировать в соответствии с какой-либо задачей. Деревья перемещаются, часть ребер удаляется, часть — добавляется. Выделения памяти — нету, при мутабельной структуре все происходит достаточно просто и главное — очень быстро. А теперь попробуйте сделать это на немутабельном дереве так же быстро.
0
>Да, для новичков в части задач проще когда все вокруг немутабельно и сломать ничего нельзя
Зачем ВСЕ делать иммутабельным? Механизм с меньшим количество движущихся частей при всех прочих равных надежнее, но делать все механизмы подряд совсем без них обычно нет смысла. Кстати, тема исходной статьи — упрощенный синтаксис для описания простых иммутабельных сущностей.
>часть ребер удаляется, часть — добавляется
>Выделения памяти — нету
Это взаимоисключающие параграфы, если нет дополнительных условий.
>А теперь попробуйте сделать это на немутабельном дереве так же быстро.
По какой ставке вы готовы платить мне за эту работу? Кто, с какими начальными условиями и по какому критерию станет сравнивать?
Вам, в свою очередь, предоставляю право рассказать, почему у Erlang'а с горизонтальной масштабируемость все намного лучше чем, к примеру, у Python'а. Тем более что Erlang несет на себе страшный оверхед от иммутабельности.
Зачем ВСЕ делать иммутабельным? Механизм с меньшим количество движущихся частей при всех прочих равных надежнее, но делать все механизмы подряд совсем без них обычно нет смысла. Кстати, тема исходной статьи — упрощенный синтаксис для описания простых иммутабельных сущностей.
>часть ребер удаляется, часть — добавляется
>Выделения памяти — нету
Это взаимоисключающие параграфы, если нет дополнительных условий.
>А теперь попробуйте сделать это на немутабельном дереве так же быстро.
По какой ставке вы готовы платить мне за эту работу? Кто, с какими начальными условиями и по какому критерию станет сравнивать?
Вам, в свою очередь, предоставляю право рассказать, почему у Erlang'а с горизонтальной масштабируемость все намного лучше чем, к примеру, у Python'а. Тем более что Erlang несет на себе страшный оверхед от иммутабельности.
0
Это взаимоисключающие параграфы, если нет дополнительных условий.Я же написал выше:
K-арное (K — фиксировано) дерево.
Вам, в свою очередь, предоставляю право рассказать, почему у Erlang'а с горизонтальной масштабируемость все намного лучше чем, к примеру, у Python'а. Тем более что Erlang несет на себе страшный оверхед от иммутабельности.Я вам выше привел пример задачи. Задача у меня заняла 1 строчку собственно логики + объявление метода, поэтому имею полное моральное право фразу вида «По какой ставке вы готовы платить мне за эту работу?» в ответ на подобную задачу засчитать за признание собственной неправоты.
С деревом я кода не просил, я лишь показал что если вы делаете его иммутабельным — у вас возникнут проблемы. В моем варианте я устанавливаю блокировку на детей/родителей изменяемых нод и на сами изменяемые ноды, остальное я не трогаю. Мне нужно будет написать алгоритм согласования захвата комбинированной блокировки (или использовать готовый, или существующую библиотеку), при этом ожидать потоки будут друг друга только если они хотят поменять один и тот же кусок дерева, если граф меняется в разных местах — никаких блокировок не происходит. С иммутабельным деревом у меня в принципе такое не выйдет, кроме того мне обязательно придется выделять память.
Кстати, тема исходной статьи — упрощенный синтаксис для описания простых иммутабельных сущностей.Я отвечал не на исходную статью а на выпад «Потому что иммутабельное гораздо проще в эксплуатации» и собственно в продолжение соответствющей дискуссии.
Зачем ВСЕ делать иммутабельным?Я не знаю, вы обобщили «Потому что иммутабельное гораздо проще в эксплуатации», потом опять обобщение «Да и зачастую куда дешевле купить новую железку, чем тратить кучу времени на отлов кучи багов, которую за собой принесет использование не иммутабельных типов.» Вот у авторов этих цитат я и спрашиваю — зачем все делать иммутабельным?
0
Это вы обобщили мой тезис за меня, вырвав комментарий из контекста. Если учитывать тему публикации, ее содержание и текст предыдущих комментариев в ветке, то обобщения остаются только у вас.
0
В энтерпрайзе в первую очередь важна простота кода, а не производительность. Да и зачастую куда дешевле купить новую железку, чем тратить кучу времени на отлов кучи багов, которую за собой принесет использование не иммутабельных типов.Камент не ваш, но в этой ветке. Скажите, что тут вырвано из контекста? Тут абсолютно четко написано что иммутабельно == лучше, и что это в общем, а не в разрезе статьи. Как бы на хабре часто ветки комментариев не относятся к содержимому статьи.
Да и ваш изначальный комментарий очень тяжело воспринять как «если нужен immutable объект то лучше спец. синтаксис а не писать его руками» а не как «immutable — лучше».
0
>Как бы на хабре часто ветки комментариев не относятся к содержимому статьи.
Такое бывает, но я полагаю некорректным применять это ко всем комментариям по умолчанию.
Такое бывает, но я полагаю некорректным применять это ко всем комментариям по умолчанию.
0
Ну посмотрите еще раз. Тут очень однозначно «почему с record станет веселее?» — «Потому что станет проще писать immutable типы, а immutable типы — безусловное добро
![](https://habrastorage.org/files/180/5c0/2d0/1805c02d0af7414ea84336471c13cc96.PNG)
Как написали так и поняли.
Потому что иммутабельное гораздо проще в эксплуатации (не надо делать копию на каждый чих, нет проблем с параллельным доступом, можно спокойно передавать куда угодно без риска испортить.Это исключительно о том что „immutable — добро“. А дальше сетование на то что это добро сейчас делать тяжелее.
Как написали так и поняли.
0
>Как написали так и поняли.
Вы знакомы вот с этим мануалом? royallib.com/read/goralik_linor/kak_nam_vsem_nauchitsya_chitat_chto_napisano.html#0
Написан он весьма жестко, но про пользу учета контекста перед объявлением чего-то «чушью» там рассказывается крайне доходчиво.
Вы знакомы вот с этим мануалом? royallib.com/read/goralik_linor/kak_nam_vsem_nauchitsya_chitat_chto_napisano.html#0
Написан он весьма жестко, но про пользу учета контекста перед объявлением чего-то «чушью» там рассказывается крайне доходчиво.
0
В энтерпрайзе так же важна стабильность, поэтому бесполезные или неоднозначные плюшки, посыпанные сахарной пудрой, всегда встречаются с осторожностью ( я имею ввиду именно этот самый «энтерпрайз», а не программистов в нём — им то да, все плющки хороши), дополнительные конструкции всегда усложняют язык, много сходных синтаксических конструкций в коде, усложняет его, видимо осознавая это, введение данной фичи отложили до лучших времен.
Скажите, чем record Unit1 лучше, например, [readonly] struct Unit? Я это спрашиваю, потому что до изучения C# ещё не дошли руки, но интерес к языку есть, и понимание некоторых основ явно будет полезным.
Скажите, чем record Unit1 лучше, например, [readonly] struct Unit? Я это спрашиваю, потому что до изучения C# ещё не дошли руки, но интерес к языку есть, и понимание некоторых основ явно будет полезным.
0
Опишу отличия:
— структура в C# — тип значение, класс (генерируемый из описания record) — ссылочный тип, структура будет всегда копироваться, record можно передавать по ссылке
— в структуре нужно будет вручную писать конструктор с параметрами для инициализации всех полей
— так-же нужно будет написать вручную метод Copy(), с параметрами, имеющими значения по умолчанию, либо по методу With для каждого параметра. Эти методы нужны для удобной работы с такими объектами. Конечно, можно их пересоздавать вручную через конструктор, но опыт использования Scala показал более удобный путь
— вручную переопределить Equals, GetHashCode, ==, !==
в общем, написать много шаблонного и типового кода
Резюмируя, это не «неоднозначаные и непровереные плюшки», это провереная практика из «настоящих» функциональных языков, которую очень хотелось бы иметь в C#.
— структура в C# — тип значение, класс (генерируемый из описания record) — ссылочный тип, структура будет всегда копироваться, record можно передавать по ссылке
— в структуре нужно будет вручную писать конструктор с параметрами для инициализации всех полей
— так-же нужно будет написать вручную метод Copy(), с параметрами, имеющими значения по умолчанию, либо по методу With для каждого параметра. Эти методы нужны для удобной работы с такими объектами. Конечно, можно их пересоздавать вручную через конструктор, но опыт использования Scala показал более удобный путь
— вручную переопределить Equals, GetHashCode, ==, !==
в общем, написать много шаблонного и типового кода
Резюмируя, это не «неоднозначаные и непровереные плюшки», это провереная практика из «настоящих» функциональных языков, которую очень хотелось бы иметь в C#.
0
Вдобавок, для рантайма это вообще не должно нести что-то новое.
Просто сахар для более удобного и выразительного описания простых неизменяемых сущностей.
Просто сахар для более удобного и выразительного описания простых неизменяемых сущностей.
0
ну я struct как пример написал, можно и [readonly] class написать.
Остальное — напряг с написанием некоторого количества ОДНОТИПНОГО кода — решается использованием дженерика, разве нет?
Остальное — напряг с написанием некоторого количества ОДНОТИПНОГО кода — решается использованием дженерика, разве нет?
0
А можно как-то сгенерить C# код вместо dll? Думал сделать это на T4, но руки пока не дошли.
+1
Да, можно, более того, это и сейчас является промежуточной стадией
До этого у меня была идея модификации кода с помошью PostSharp, но это оказалось неудобно из-за того, что аспекты инжектируются после компиляции и метод Copy не виден для IDE и инструментов на этапе разработки.
До этого у меня была идея модификации кода с помошью PostSharp, но это оказалось неудобно из-за того, что аспекты инжектируются после компиляции и метод Copy не виден для IDE и инструментов на этапе разработки.
0
Сделал custom tool для VisualStudio
Проект и расширение вот здесь: github.com/danslapman/RecSharp.VisualStudio
Необходимо создать файл с расширением .rcs
Проект и расширение вот здесь: github.com/danslapman/RecSharp.VisualStudio
Необходимо создать файл с расширением .rcs
0
Удивительно, что ни один спорщик-комментатор не упомянул огромный прирост производительности GC при использовании immutable-объектов. c2.com/cgi/wiki?ImmutableObjectsAndGarbageCollection
+1
Это не для дотнета, а для хаскеля, который таки функционально чист.
+1
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Когда нет сил ждать Record'ы