Вроде и есть полезные штуки, но как-то все убого. Как кусок дерьма, присыпанный сахаром.
Почему "data class", а не просто "record"?
"public data class Person { string FirstName; string LastName; }" и "public data class Person(string FirstName, string LastName)". Не хватает еще "public data class Person [ string FirstName string LastName ]".
Магия и сахар для top-level инструкций.
">= 3000 and <= 5000 => 10.00m" — нечитаемый кусок дерьма, плюс появились "and", "or", "not". Конгениально.
Здравствуйте, AlexRK, Вы писали:
ARK>Почему "data class", а не просто "record"?
Например, потому что это таки class, хоть и присыпанный сахаром. На мой вкус в троице class, struct и record — последний лишний, потому что и class и struct являются record.
Здравствуйте, Mystic Artifact, Вы писали:
ARK>>Почему "data class", а не просто "record"? MA> Например, потому что это таки class, хоть и присыпанный сахаром. На мой вкус в троице class, struct и record — последний лишний, потому что и class и struct являются record.
В тексте используется _исключительно_ терминология "record", а словосочетание "data class" встречается _только_ в сниппетах кода. Какой мудрый ход со стороны микрософта — называть в коде сущность одним именем, а в документации — другим. Разве это не прекрасно?
Здравствуйте, AlexRK, Вы писали:
ARK>В тексте используется _исключительно_ терминология "record", а словосочетание "data class" встречается _только_ в сниппетах кода. Какой мудрый ход со стороны микрософта — называть в коде сущность одним именем, а в документации — другим. Разве это не прекрасно?
Единой терминологии на этот счет нет нигде. В мире около ФП и хипсиеров record используется именно так. В классике жанра — record это просто составной тип. Поэтому, честно говоря наскоро пролистал всю статью, понял о чем они и закрыл. В принципе не плохо, на мой взгляд.
PS: Улучшенный паттернматчинг местами переулучшен.
Здравствуйте, Mystic Artifact, Вы писали:
ARK>>В тексте используется _исключительно_ терминология "record", а словосочетание "data class" встречается _только_ в сниппетах кода. Какой мудрый ход со стороны микрософта — называть в коде сущность одним именем, а в документации — другим. Разве это не прекрасно?
MA> Единой терминологии на этот счет нет нигде.
Это понятно, но не в рамках одного языка же. Шизофрения какая-то, в тексте "record", в коде "data class".
Да и, откровенно говоря, само словосочетание "data class" мне кажется убогим. Какая разница, класс это на самом деле или нет. Делегаты тоже классы, почему бы не назвать их "callable class"?
… случайно смержили в релиз — Как-то иначе эти нововведения объяснить сложно.
Сделать одну фундаментальную фичу макросы — и вот эти "нововведения" запилит студент в корридоре кампуса на стажировке. Собственно от "топовых" новинок именно такое ощущение. Если бы это выкатили пакетом в NuGet — не факт что я бы его подключил.
Ну и конечно фичи есть куда развивать. Явно нехватает двух вещей: value-type records это раз. И скобочек на клавиатуре для них это два.
Предвижу проблему с logical patterns. Они поддерживают туплы?!
(a, b) switch
{
(<10 and >5, >10) => WriteLine("J это C#12!");
}
Пока лютую дичь написать нельзя, есть ещё над чем работать и куда расти!
И только Covariant returns штука хорошая, даст возможность выкинуть куски повторяющегося и тупого кода.
Налицо явный многолетний тренд на возрождение Паскаля. Дочитал до "with" и аж вздрогнул. А так — чувствуется лютое влияние Kotlin и Swift (читай — стараются не отставать от Google и Apple).
Здравствуйте, Mr.Delphist, Вы писали:
MD>Налицо явный многолетний тренд на возрождение Паскаля. Дочитал до "with" и аж вздрогнул. А так — чувствуется лютое влияние Kotlin и Swift (читай — стараются не отставать от Google и Apple).
Лично я не считаю, что паскаль — это плохо (даже считаю наоборот, но это неважно). Плохи бессистемность и эклектичность. Раз уже есть && и || — так делайте дальше в том же стиле, а не пихайте and/or. Та же байда с == и equals и многим другим. Получается уродливое месиво — тут си, тут паскаль, тут рыбу заворачивали. Логика, последовательность? Какое там. "Запомнытэ это, дэти, потому что понять это — нэвозможно!" (с)
Здравствуйте, Mystic Artifact, Вы писали:
MA>Здравствуйте, AlexRK, Вы писали:
ARK>>Почему "data class", а не просто "record"? MA> Например, потому что это таки class, хоть и присыпанный сахаром. На мой вкус в троице class, struct и record — последний лишний, потому что и class и struct являются record.
В F# под record принято понимать сравнение по значению и невозможность инициализации null-ами(если это не указано специально).
Может поэтому*
ARK>Раз уже есть && и || — так делайте дальше в том же стиле, а не пихайте and/or.
Подозреваю что они отказались от && и || потому что если писать с ними качественный продакшен код:
Здравствуйте, AlexRK, Вы писали:
ARK>Лично я не считаю, что паскаль — это плохо (даже считаю наоборот, но это неважно). Плохи бессистемность и эклектичность. Раз уже есть && и || — так делайте дальше в том же стиле, а не пихайте and/or. Та же байда с == и equals и многим другим. Получается уродливое месиво — тут си, тут паскаль, тут рыбу заворачивали. Логика, последовательность? Какое там. "Запомнытэ это, дэти, потому что понять это — нэвозможно!" (с)
А мне нравится! Могли и сразу ввести and, or and not вместо && и || и ! перекурочили бинарные (& и | и ~)
Просто в паскале and, or and not применяется к булевым и числовым по разному. А здесь наследственность понимаетели.
Правильным путем идем товарищи!
и солнце б утром не вставало, когда бы не было меня
Шарп упорно соревнуется с самим собой в том, сколько одних и тех же по своей сути вещей можно сделать разными способами.
> Target-typed new expressions > new expressions in C# have always required a type to be specified (except for implicitly typed array expressions). Now you can leave out the type if there’s a clear type that the expressions is being assigned to. > > Point p = new (3, 5);
Point p(3, 5); уже можно записать, или это кандидат в 10.0?
ARK>Что за тупые ублюдки там язык дизайнят?
Редчайший случай, коллега! Точно знаю что кто-то из нас двоих оказался слабее в пост-иронии. Но не могу обосновать кто!
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, Mr.Delphist, Вы писали:
MD>>Налицо явный многолетний тренд на возрождение Паскаля. Дочитал до "with" и аж вздрогнул. А так — чувствуется лютое влияние Kotlin и Swift (читай — стараются не отставать от Google и Apple).
ARK>Лично я не считаю, что паскаль — это плохо (даже считаю наоборот, но это неважно). Плохи бессистемность и эклектичность. Раз уже есть && и || — так делайте дальше в том же стиле, а не пихайте and/or. Та же байда с == и equals и многим другим. Получается уродливое месиво — тут си, тут паскаль, тут рыбу заворачивали. Логика, последовательность? Какое там. "Запомнытэ это, дэти, потому что понять это — нэвозможно!" (с)
Что Вы, Паскаль — ни в коем случае не плохо (гляньте мой ник). Вот именно про эклектичность и был оригинальный посыл. Из C-образного семейства язык начинает миграцию к совсем иной парадигме, но обратную совместимость не отменяют, и получаем что получаем. Собственно, выше всё и описано.
Здравствуйте, Serginio1, Вы писали:
S>Welcome to C# 9.0
positional deconstruction — это какая-то шляпа. Ошибки пойдут косяками.
Неявные преобразования типов для with — тоже не очень.
А супер-эконопия на "static void Main()"вообще повысит производительность на порядки.
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, Serginio1, Вы писали:
S>>Welcome to C# 9.0 V>positional deconstruction — это какая-то шляпа. Ошибки пойдут косяками.
На самом деле они уже давно есть https://docs.microsoft.com/ru-ru/dotnet/csharp/deconstruct
V>Неявные преобразования типов для with — тоже не очень.
with это не преобразование а копирование с замещением в with
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, AlexRK, Вы писали:
НС>>Потому что, скорее всего, будут еще data struct ARK>Тогда странно, что вовсю используется термин record. Это только усиливает путаницу.
Релиза еще не было, даже публичного превью не было, официальная терминология тоже не декларировалась, странно придираться к терминам в посту Мэдса.
Здравствуйте, hi_octane, Вы писали:
_>И только Covariant returns штука хорошая, даст возможность выкинуть куски повторяющегося и тупого кода.
Согласен, covariant returns единственное, что радует. В меньшей степени можно и init свойства сюда отнести. А вот вместо всего остального лучше сделали бы варианты как в немерле и запилили квазицитаты, а то до генераторов дошли, а до квазицитат не додумались, хотя казалось бы, есть успешный пример применения перед глазами — Expression, ну и еще до кучи typedef как том же немерле или С++.
После этого и top-level declaration можно было бы представлять как вишенку на торте.
Ты обрати внимание — в примере на Nemerle на все 3 шаблона создаётся одна переменная 's' с разными значениями. В современном C# это вообще никак.
Второе — там 3 варианта выбора заканчиваются одним действием. В современном C# этого тоже нет.
И дело не в том что без этих фич жить нельзя. Выжить можно на одних if и goto. Просто если изначально компилятор делать по уму, концептуально, потом самые навороченные фичи появляются без проблем.
R>Согласен, covariant returns единственное, что радует. В меньшей степени можно и init свойства сюда отнести. А вот вместо всего остального лучше сделали бы варианты как в немерле и запилили квазицитаты, а то до генераторов дошли, а до квазицитат не додумались, хотя казалось бы, есть успешный пример применения перед глазами — Expression, ну и еще до кучи typedef как том же немерле или С++.
Ага.
Ну и не expression единым, вторая версия квази-цитат, возможно даже более подходящая — dynamic. Типы не определены, корректность кода язык проверяет, всё в compile-time. Equals для небольших кусочков любой студент наляпать может — вот и сопоставление одного куска кода с другим. Подставить типы тоже элементарно, более того jit это уже делает.
Здравствуйте, hi_octane, Вы писали:
R>>Согласен, covariant returns единственное, что радует. В меньшей степени можно и init свойства сюда отнести. А вот вместо всего остального лучше сделали бы варианты как в немерле и запилили квазицитаты, а то до генераторов дошли, а до квазицитат не додумались, хотя казалось бы, есть успешный пример применения перед глазами — Expression, ну и еще до кучи typedef как том же немерле или С++. _>Ага. _>Ну и не expression единым, вторая версия квази-цитат, возможно даже более подходящая — dynamic. Типы не определены, корректность кода язык проверяет, всё в compile-time. Equals для небольших кусочков любой студент наляпать может — вот и сопоставление одного куска кода с другим. Подставить типы тоже элементарно, более того jit это уже делает.
> IT>| (s, _) => IT>| (_, s) => IT>| _ with s = 1 => s.ToString() IT>
static Sector GetSector(Point point) => point switch
{
var (x, y) when x > 0 && y > 0 => Sector.Two,
(1, var y) when y < 0 => Sector.Three,
_ => Sector.Unknown
};
ну разница только в var
плюс добавили
DeliveryTruck t when t.GrossWeightClass switch
{
< 3000 => 10.00m - 2.00m,
>= 3000 and <= 5000 => 10.00m,
> 5000 => 10.00m + 5.00m,
},
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, AlexRK, Вы писали:
ARK>Почему "data class", а не просто "record"?
Это просто легализация DTO. Люди прямо сейчас плодят классы в которых нет ничего, кроме свойств. Проблема в том, что классы позволяют делать с собой намного больше, чем нужно для DTO, что может привести к порче данных и багам.
Никакой record не нужен, у тебя уже есть struct.
Короче, я вижу это как костыли для убогих, умеющих писать код только в процедурном стиле.
Здравствуйте, AlexRK, Вы писали:
ARK>Вроде и есть полезные штуки, но как-то все убого. Как кусок дерьма, присыпанный сахаром.
В целом согласен. Правильные фичи, но со спорным синтаксисом.
ARK>Магия и сахар для top-level инструкций.
Ну, выбрасывание case/break это очень даже правильно. То что | как в других ML-наследниках есть не ввели — плохо (как по мне). Но в целом, нечитаемо оно тебе только от непричычки. На самом деле case/break куда как хуже.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Mystic Artifact, Вы писали:
MA> Единой терминологии на этот счет нет нигде. В мире около ФП и хипсиеров record используется именно так. В классике жанра — record это просто составной тип. Поэтому, честно говоря наскоро пролистал всю статью, понял о чем они и закрыл. В принципе не плохо, на мой взгляд.
Нет, все несколько сложнее. Рекорд — это разновидность алгебраического типа. Они в спеке шарпа это позиционностью называют. А вот вэлью он или референсный — без разницы.
Ну, и надо понимать, что майкрософтовские рекорды по сути не рекорды, а юнионы, так как поддерживают наследование.
MA>PS: Улучшенный паттернматчинг местами переулучшен.
Есть момент. Но точно лучше чем case/break.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, AlexRK, Вы писали:
ARK>Это понятно, но не в рамках одного языка же. Шизофрения какая-то, в тексте "record", в коде "data class".
ARK>Да и, откровенно говоря, само словосочетание "data class" мне кажется убогим. Какая разница, класс это на самом деле или нет. Делегаты тоже классы, почему бы не назвать их "callable class"?
Причем новое лючевое слово все равно вводят. Можно было бы тупо писать "record" вместо "data class" или хотя бы "data".
Тут явно вкусовщина. Причем еще и дурацкая.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, hi_octane, Вы писали:
_>Сделать одну фундаментальную фичу макросы — и вот эти "нововведения" запилит студент в корридоре кампуса на стажировке.
Если их правильно сделать, то все последующие фичи можно запелить силами студентов. Но вот поднять их без аналога Нитры не реально. А на Нитру они еще 20 лет убьют, сч их подходами.
Им надо меня с Хардкейсом на работу взять и дать нам человек 5 в подчинение. Будут тогда супер-макросы через год-два.
_> Собственно от "топовых" новинок именно такое ощущение. Если бы это выкатили пакетом в NuGet — не факт что я бы его подключил.
Не. У них там все лобзиком выпилено и гвоздями прибито. Не могут они синтаксис извне менять.
_>Ну и конечно фичи есть куда развивать. Явно нехватает двух вещей: value-type records это раз.
Может и есть. Не вижу почему бы не сделать data применимым к struct. Может потому data и выбрали, что он применяется не только к class.
_>И скобочек на клавиатуре для них это два.
_>И только Covariant returns штука хорошая, даст возможность выкинуть куски повторяющегося и тупого кода.
Не. Все нововведения полезны. Я в Немерле в 90% случаях имьютабельные рекорды использую. Вопрос лишь синтаксиса. with в Немерле тоже не хватает. Согласен, что можно сделать макрами.
Но до макросов они морально не доросли пока.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Налицо явный многолетний тренд на возрождение Паскаля. Дочитал до "with" и аж вздрогнул. А так — чувствуется лютое влияние Kotlin и Swift (читай — стараются не отставать от Google и Apple).
Какой еще Паскаль? Открой для себя ML!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>data struct вроде уже есть. Но доживут ли они до релиза — ХЗ, там есть некоторое количество логических проблем.
Это что за логические проблемы такие? И почему они не мешают F# и Nemerle?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, vmpire, Вы писали:
S>>Welcome to C# 9.0 V>positional deconstruction — это какая-то шляпа. Ошибки пойдут косяками.
Во всех функциональных языках почему-то не идут. Хотя их уже десятки лет используют.
Их record — это реализация в Шарпе концепции алгебраических типов. В них тип определяется структурой. Ни к каким проблемам это не приводит. Наоборот, делает код проще.
V>Неявные преобразования типов для with — тоже не очень.
Нет там никаких преобразований. Ты на входе и на выходе имеешь один и тот же тип. Просто оперировать ты можешь базовым классом, а в рантайме получишь тот тип, что был.
V>А супер-эконопия на "static void Main()"вообще повысит производительность на порядки.
Это полезно если стоит задача "поиграться". И для тестов. Куча болерплэйта уходит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Релиза еще не было, даже публичного превью не было, официальная терминология тоже не декларировалась, странно придираться к терминам в посту Мэдса.
Странный выбор ключевого слова. А вот к Мэдсу претензий нет. Думаю их еще завалят подобными упреками и они вернутся к record.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, rameel, Вы писали:
R>Согласен, covariant returns единственное, что радует. В меньшей степени можно и init свойства сюда отнести. А вот вместо всего остального лучше сделали бы варианты как в немерле
Дык в Немерле кроме вариантов еще мара Record есть, которая к обычным классам и струтурам все перечисленные (кроме with) фичи прикручивает. Очень полезная вещь!
Как я понял они хотят одним выстрелом убить двух зайцев. data будет делать варинты, и рекорды одним синтаксисом. Если есть наследование и sealed, будут варианты. Если нет или структура — рекорды. По сути они только наследованием и отличаются.
R>и запилили квазицитаты,
О, да! Это было бы мега-круто и на Немерле можно было бы поставить крест (так как он свою задачу выполнил бы, а Шарп стал бы его реализацией).
Причем их можно было бы использовать как для Expression Tree, так и для кодогенерации в компайлтайме. Даже если для ET сделали бы было уже очень удобно. Пришлось тут с ET ручками поработать — это жесть как неудобно.
R>а то до генераторов дошли,
Это где? Что-то я пропустил.
R>а до квазицитат не додумались, хотя казалось бы, есть успешный пример применения перед глазами — Expression, ну и еще до кучи typedef как том же немерле или С++.
Еще есть F# где зачатки цитирования тоже есть. В общем, нет в МС решительности. Задрали еже, если честно. Я так на пенсию выйду, а Шарп в Немерл не превратится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Блин, сначала дал ссылку на русскую Википедию, но там в корне не верное описание алгебраических типов! Какие-то неучи поставили знак раверства между алгебраическим типом и SUM-типом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
S>>>Welcome to C# 9.0 V>>positional deconstruction — это какая-то шляпа. Ошибки пойдут косяками. VD>Во всех функциональных языках почему-то не идут. Хотя их уже десятки лет используют.
Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
VD>Их record — это реализация в Шарпе концепции алгебраических типов. В них тип определяется структурой. Ни к каким проблемам это не приводит. Наоборот, делает код проще. V>>Неявные преобразования типов для with — тоже не очень. VD>Нет там никаких преобразований. Ты на входе и на выходе имеешь один и тот же тип. Просто оперировать ты можешь базовым классом, а в рантайме получишь тот тип, что был.
Вот это и напрягает, когда вместо базового класса появляется производный. Нет, если LSP в порядке, то проблем не будет. Но вот если не в порядке...
Хотя тут, скорее всего, дело привычки. Нужно просто запомнить, как это работает.
V>>А супер-эконопия на "static void Main()"вообще повысит производительность на порядки. VD>Это полезно если стоит задача "поиграться". И для тестов. Куча болерплэйта уходит.
Эта "куча" — 2 строки кода.
Единественно когда это может быть полезно, так это если делать интерпретатор, как в скриптовых языках.
V>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
Ну то же самое, что в конструкторе параметры поменять.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, VladD2, Вы писали:
НС>>data struct вроде уже есть. Но доживут ли они до релиза — ХЗ, там есть некоторое количество логических проблем. VD>Это что за логические проблемы такие?
Я уже не помню. Там были какие то нестыковки с вспомогательными методами типа equals или copy-конструктора.
VD> И почему они не мешают F# и Nemerle?
Здравствуйте, Serginio1, Вы писали:
V>>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
S>Ну то же самое, что в конструкторе параметры поменять.
Конструктор — это всё-таки метод.
То, что порядок параметров в вызове метода имеет значение, все понимают интуитивно. Плюс, есть всякие решарперы, чтобы поменять везде порядок аргументов.
А вот то, что изменение порядка объявлений членов класса может поломать программу — интуитивно непонятно (про С++ сейчас не говорим ).
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, Serginio1, Вы писали:
V>>>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
S>>Ну то же самое, что в конструкторе параметры поменять. V>Конструктор — это всё-таки метод. V>То, что порядок параметров в вызове метода имеет значение, все понимают интуитивно. Плюс, есть всякие решарперы, чтобы поменять везде порядок аргументов. V>А вот то, что изменение порядка объявлений членов класса может поломать программу — интуитивно непонятно (про С++ сейчас не говорим ).
Ну будут и для рекордов в студии . Там так или иначе есть конструктор.
Ты его можешь сам и написать. Но большинство будут использовать краткую запись. Для этого они и придумывались
Positional records
Sometimes it’s useful to have a more positional approach to a record, where its contents are given via constructor arguments, and can be extracted with positional deconstruction.
It’s perfectly possible to specify your own constructor and deconstructor in a record:
public data class Person
{
string FirstName;
string LastName;
public Person(string firstName, string lastName)
=> (FirstName, LastName) = (firstName, lastName);
public void Deconstruct(out string firstName, out string lastName)
=> (firstName, lastName) = (FirstName, LastName);
}
But there’s a much shorter syntax for expressing exactly the same thing (modulo casing of parameter names):
public data class Person(string FirstName, string LastName);
This declares the public init-only auto-properties and the constructor and the deconstructor, so that you can write:
var person = new Person("Scott", "Hunter"); // positional constructionvar (f, l) = person; // positional deconstruction
If you don’t like the generated auto-property you can define your own property of the same name instead, and the generated constructor and deconstructor will just use that one.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>А вместо data struct?
Вместо "data". Оно, конечно, с научной точки зрения будет не верно, так как record относится только к не SUM-типам (к кортежам и типам без наследования), но этих тонкостей один хрен никто не знает кроме человек 100 на земле. Так что было бы нормально.
А так, действительно, в рассуждениях record, а в синтаксисе data. В прочем может это Мэдс не очень компетентен в вопросе или рассчитывает на некомпетентную аудиторию, вот и использует не верную терминологию. В принципе нужно было это АлгТД называть. Термин "запись" применим только к классам без наследования и к структурам. А с наследованием получается эдакое расширение SUM-тиов. То что в Nemrle variant-ами называется, а в F# union-ами.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, vmpire, Вы писали: S>Ну будут и для рекордов в студии. Там так или иначе есть конструктор.
Конструктор тут ни при чём, я же про positional deconstruction
Но я, кажется, понял смысл: деконструктор объявляется явно, а синтаксис records повторяет синтаксис метода, а не класса, этого я поначалу не уловил.
Если так, то проблем быть не должно
Здравствуйте, vmpire, Вы писали:
V>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
Будет тоже самое, что при изменении порядка параметров в методе. С методами тебя это не смущает? Ну, на фигачат рефакторинов по их изменению.
V>Вот это и напрягает, когда вместо базового класса появляется производный.
Это у тебя предрассудки. Тут нет никакой разницы с вызовом метода. Ты точно так же можешь вызвать метод на базовом тип, а он преобразуется в вызов наследника, так как виртуальный. По сути тут и есть завуалированный вызов виртуального метода копирования.
V>Нет, если LSP в порядке, то проблем не будет. Но вот если не в порядке...
Если в голове что-то не в порядке, проблемы будут по любому. Тут ничего не поделаешь.
Ну, а то что они реализовали растет из науки и 100 проверенно на практике. Я этим уже 14 лет пользуюсь и доволен.
V>Хотя тут, скорее всего, дело привычки. Нужно просто запомнить, как это работает.
Тут дело в боязни нового и парадоксе блаба (погугли). Просто ты смотришь на изменения которые тебе новы и не понятны и резонно опасаешься "как бы чего не вышло". Но эти фичи не новые. Они только для Шарпа новые. Так то они появились в 1970-м году в ML-е. Под ними лежит не хилый научный базис.
Поверь тем кто это уже использовал. На практике это очень упрощает жизнь.
V>Эта "куча" — 2 строки кода.
А ты посчитай. 3 на класс. 3 на метод. А тебе нужно вывести одну строку на консоль.
Согласен, что особой пользы от этого нет. Но как приятное бесплатное дополнение вполне потянет.
V>Единественно когда это может быть полезно, так это если делать интерпретатор, как в скриптовых языках.
Отнюдь. Компилируемым языкам это тоже полезно, хотя и без этого прожить легко.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Потому что C# это не F# и Nemerle.
И в чем разница, особенно в плане equals и копированя?
Не выдумывай.
А Шарп продолжает движение к Немерлу. И это правльно. Вот только не ясно зачем было тратить на это 20 лет и делать столько ошибок. Рано или поздно Шарп станет Немерлом с уговняконным, не консистентным синтаксисом, косяками дизайна, но идеальной реализацией компилятора и ИДЕ. О чем я и говорил 13 лет назад.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Судя по описанию это пруф оф концепт. В ИДЕ поддержки нет. Как к проекту цеплять не ясно. Ты это пробовал использовать?
<ItemGroup>
<!-- Note that this is not a "normal" ProjectReference.
It needs the additional 'OutputItemType' and 'ReferenceOutputAssmbly' attributes. -->
<ProjectReference Include="path-to-sourcegenerator-project.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
Пробовал пару примеров, работает, но поддержки со стороны студии никакой, только командная строка.
Здравствуйте, σ, Вы писали:
>> Версия 9.0 >> Добавили covariant return types
σ>Вот что значит доверить проектирование языка дельфисту.
Дельфист сейчас разрабатывает TypeScript https://ru.wikipedia.org/wiki/TypeScript
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, VladD2, Вы писали:
V>>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет. VD>Будет тоже самое, что при изменении порядка параметров в методе. С методами тебя это не смущает? Ну, на фигачат рефакторинов по их изменению.
Я уже посмотрел повнимательнее, в данном случае синтаксис остаётся как у метода, поэтому проблем не будет.
VD>Ну, а то что они реализовали растет из науки и 100 проверенно на практике. Я этим уже 14 лет пользуюсь и доволен.
Ну я и С++ пользовался лет 10, где порядок объявления членов в классе влиял на логику программы. Но это не значит, что я считаю, что это хорошо.
Но, ещё раз: в данном случае используется синтаксис вызова метода, поэтому проблем не будет.
VD>Тут дело в боязни нового и парадоксе блаба (погугли). Просто ты смотришь на изменения которые тебе новы и не понятны и резонно опасаешься "как бы чего не вышло". Но эти фичи не новые. Они только для Шарпа новые. Так то они появились в 1970-м году в ML-е. Под ними лежит не хилый научный базис.
Вот уж тут Вы точно не угадали Чего чего, а боязни нового у меня нет. Наоборот, я постоянно смотрю, где что появилось и как это может мне пригодится.
Но вместе с тем, некоторые (именно некоторые) вещи вызывают во мне опасение, так я наловился за свою жизнь ошибок в программах, как своих так и не своих, и примерно понимаю, где могут быть слабые места.
В качестве контрпримера: под реляционными базами тоже лежит нехилый научный базис, даже поболее, чем то, что мы обсуждаем.
Что совершенно не мешает поколениям программистов ходить строем по одним и тем же граблям.
VD>Поверь тем кто это уже использовал. На практике это очень упрощает жизнь.
Верить не буду. Ни плюсам, ни минусам. Дойдут руки — проверю сам.
Верить не буду потому, что неоднократно видел, как мыши годами ели кактус и считали, что всё замечательно.
Здравствуйте, rameel, Вы писали:
R>Пробовал пару примеров, работает, но поддержки со стороны студии никакой, только командная строка.
И как оно видно в студии? Отсутствующие типы? Так то по идее если насрать текст в файл и подсунуть это студии через проектную систему должно кое-как работать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, _FRED_, Вы писали:
_FR>Нормальная поддержка в студии: _FR>Note: you will currently need to restart Visual Studio to see IntelliSense and get rid of errors with the early tooling experience
Да, мега технология.
Ну, ладно. Лиха беда начало. Тут главное, чтобы они свои свои животные страхи и парадокс блаба преодолели. Дальше пойдет легче.
_FR>Наше всё :о))
Я имел в виду, как подключить это чудо к своему проекту, а не как дебажит. Но, спасибо rameel, в его сообщении есть ответ
.
_FR>Я пробую. Пока не понятно, как удобнее код генерить: через `StringBuilder.Append(…)` или вручную дерево собирать.
Да там главное как к проекту подключиться и как это обратно окмпилятору подсунуть.
Что-то больно через жопу они все это подключают. Надо было через ключ компилятора. Тогда можно было бы пользоваться его символами и новый код было бы не проблема подсунут. В Росзлине все это предусмотрено (ну хоть как-то). А они через расширения к студии пошли. Это тупик, я думаю.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, vmpire, Вы писали:
V>Я уже посмотрел повнимательнее, в данном случае синтаксис остаётся как у метода, поэтому проблем не будет.
И где логическая связь? А ее нет!
Проблем не будет просто потому, что это проверенно на практике. В Немрле позиции определяются порядком определения полей. Но никаких проблем нет. Ты просто понимаешь, что позиции полей имеют значение и не меняешь их без необходимости.
Единственно, что... возможно кому-то придется пересмотреть порядок объявлений. Лично я и те кто со мной работает на Немерле давно привыкли описывать весь список полей (не зависимо от того приватные они или публичные) и автосвойства вначале файла, а методы и простые свойства после конструкторов (если они есть).
Но это полезно не только из-за рекородв, а в принципе. Опять же практикой подтверждено. Поля в классе объясняют его суть (и наследование, конечно же). А значит она должны быть на виду.
V>Ну я и С++ пользовался лет 10, где порядок объявления членов в классе влиял на логику программы. Но это не значит, что я считаю, что это хорошо. V>Но, ещё раз: в данном случае используется синтаксис вызова метода, поэтому проблем не будет.
Не надо сравнивать творение рук не человеческих и наунчо обоснованную вещь.
С++ создает проблемы потому что криво спроектирован изначально. Тут же таких родовых травм нет.
Ты базами данных пользовался? Функциями? Ну, вот тут очень близкая база. Все растет из котрежей. А они и есть структурный тип.
Ну, или тогда будь последователен и протестуй против кортежей, которые давно в шаре есть.
V>Вот уж тут Вы точно не угадали Чего чего, а боязни нового у меня нет.
Боязнь нового, как и просто боязнь — это нормально. Не бояться может только дурак или маньяк. Вопрос лишь в разумности. Насколько человек может рационально взвесить пользу и возможные проблемы.
V>Наоборот, я постоянно смотрю, где что появилось и как это может мне пригодится.
Значит это первый случай в твоей жизни.
V>Но вместе с тем, некоторые (именно некоторые) вещи вызывают во мне опасение, так я наловился за свою жизнь ошибок в программах, как своих так и не своих, и примерно понимаю, где могут быть слабые места.
Вот, вот. Это и есть страх "как бы чего не вышло". Это нормально. Даже хорошо. Но нужно подходить к нему рационально. А то вот люди тоже боятся чипирования и прочего бреда из тех же соображений. Но у них нет понимания того чего они боятся.
V>В качестве контрпримера: под реляционными базами тоже лежит нехилый научный базис, даже поболее, чем то, что мы обсуждаем. V>Что совершенно не мешает поколениям программистов ходить строем по одним и тем же граблям.
По граблям вообще сложно запретить ходить. В Шарпе много думали о том как их число сократить. Хотя иногда и факапили.
И ты прав. Под РСБУД лежит не просто наука, а ровно та же наука. Только ее там немного в другое русло увело (в множества). А теория типов там очень похожая. И именно по этому АлгТД отлично ложатся на работу с СУБД.
V>Верить не буду. Ни плюсам, ни минусам. Дойдут руки — проверю сам. V>Верить не буду потому, что неоднократно видел, как мыши годами ели кактус и считали, что всё замечательно.
Ну, дерзай. Я тоже на слово редко верю.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Тебе опять поднять мой старый пост, где я в ответ на твои космические корабли предсказал будущее Немерле?
Да ты можешь хоть об отвечаться. Это ничего не меняет. Шарп неумолимо превращается в нНемерл с чуть другим синтаксисом. Поду и макры лет через 10 осилят.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>И как оно видно в студии? Отсутствующие типы? Так то по идее если насрать текст в файл и подсунуть это студии через проектную систему должно кое-как работать.
Голая студия видит сгенерированные типы и методы после перезагрузки. Решарпер же ни в какую их видеть не хочет — подсвечивает все красным
Здравствуйте, VladD2, Вы писали:
V>>Я уже посмотрел повнимательнее, в данном случае синтаксис остаётся как у метода, поэтому проблем не будет. VD>И где логическая связь? А ее нет!
А она есть. К тому, что порядок параметров метода имеет значение все привыкли.
К тому, что порядок объявления членов класса в C# имеет значение не только никто не привык, а даже мало кто подозревает об этом.
VD>Проблем не будет просто потому, что это проверенно на практике. В Немрле позиции определяются порядком определения полей. Но никаких проблем нет. Ты просто понимаешь, что позиции полей имеют значение и не меняешь их без необходимости.
Не соглашусь. Сравнение с Немерлем вообще некорректное: те, кто на нём пишет изначально привыкли к поведению, которое для него специфично и не меняется.
Изменение поведения языка, на котором уже много кто пишет — это совсем другое дело.
VD>Единственно, что... возможно кому-то придется пересмотреть порядок объявлений. Лично я и те кто со мной работает на Немерле давно привыкли описывать весь список полей (не зависимо от того приватные они или публичные) и автосвойства вначале файла, а методы и простые свойства после конструкторов (если они есть).
Лично вы — привыкли. Но не все пишут в таком стиле и не стоит считать его единственно верным.
А привыкнуть можно ко всему (кроме PowerShell ). На С когда-то тоже переменные группировали наверху. А JS (местами) и TSQL и сейчас так неявно делают. Но это не значит, что это удобно. И как раз из за этого бывают иногда реальные ошибки.
Привыкнуть — да, можно. Я, например, как-то привык писать на языке, где не было циклов, а есдинственным способом их сделать было goto. Причём, списков и даже целочисленных переменных там тоже не было.
Привык? Да, привык. Понравилось ли это? Нет.
VD>Но это полезно не только из-за рекородв, а в принципе. Опять же практикой подтверждено. Поля в классе объясняют его суть (и наследование, конечно же). А значит она должны быть на виду.
Только для DTO. А так, точно так же можно сказать, что суть класса объясняют его методы. Или название. Или интерфейсы.
Это понимание у каждого своё, спорить тут бессмысленно.
V>>Ну я и С++ пользовался лет 10, где порядок объявления членов в классе влиял на логику программы. Но это не значит, что я считаю, что это хорошо. V>>Но, ещё раз: в данном случае используется синтаксис вызова метода, поэтому проблем не будет. VD>Не надо сравнивать творение рук не человеческих и наунчо обоснованную вещь.
Нескромный вопрос: а "научно обосновал" кто? инопланетяне? Тоже ведь люди.
И никто ничего не сравнивает, это разные вещи, которые обе нужно брать во внимание.
VD>С++ создает проблемы потому что криво спроектирован изначально. Тут же таких родовых травм нет. VD>Ты базами данных пользовался? Функциями? Ну, вот тут очень близкая база. Все растет из котрежей. А они и есть структурный тип. VD>Ну, или тогда будь последователен и протестуй против кортежей, которые давно в шаре есть.
Я ни против чего не протестую. Я высказываю мнение, что при изменении привыченого поведения возможны ошибки.
>>> Версия 9.0 >>> Добавили covariant return types
σ>>Вот что значит доверить проектирование языка дельфисту. S>Дельфист сейчас разрабатывает TypeScript https://ru.wikipedia.org/wiki/TypeScript
Т.е. банальные covariant return types завезли только когда дельфист отстранился (ну или его отстранили) от разработки языка?
Здравствуйте, σ, Вы писали:
>>>> Версия 9.0 >>>> Добавили covariant return types
σ>>>Вот что значит доверить проектирование языка дельфисту. S>>Дельфист сейчас разрабатывает TypeScript https://ru.wikipedia.org/wiki/TypeScript
σ>Т.е. банальные covariant return types завезли только когда дельфист отстранился (ну или его отстранили) от разработки языка?
ну если ты прочитаешь ссылочку, то он с 12 года занимантся. И он активно развивается.
При нем так и не ввели метаклассы. Типа статические методы класса можно было переопределять.
Там же были и виртуальные конструкторы.
А вот как он ушел, даже and, or, not и with вспомнили!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, VladD2, Вы писали:
НС>>Тебе опять поднять мой старый пост, где я в ответ на твои космические корабли предсказал будущее Немерле? VD>Да ты можешь хоть об отвечаться. Это ничего не меняет.
Безусловно. Как Немерле был в жопе, так там и остался.
VD>Шарп неумолимо превращается в нНемерл с чуть другим синтаксисом.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ночной Смотрящий, Вы писали:
НС>>Потому что C# это не F# и Nemerle.
VD>И в чем разница, особенно в плане equals и копированя?
VD>Не выдумывай.
VD>А Шарп продолжает движение к Немерлу. И это правльно. Вот только не ясно зачем было тратить на это 20 лет и делать столько ошибок. Рано или поздно Шарп станет Немерлом с уговняконным, не консистентным синтаксисом, косяками дизайна, но идеальной реализацией компилятора и ИДЕ. О чем я и говорил 13 лет назад.
Десятое правило Гринспена:
Любая достаточно сложная программа на Си или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp.
Здравствуйте, VladD2, Вы писали:
VD>Да ты можешь хоть об отвечаться. Это ничего не меняет. Шарп неумолимо превращается в нНемерл с чуть другим синтаксисом. Поду и макры лет через 10 осилят.
Чего не было (как сейчас не знаю), так это ref locals/ref return values, expression bodied methods, throw expressions и async streams вроде. При чем поддержать все это можно, было бы желание). Unsafe подмножество немерле не поддерживает изначально по идеологическим причинам насколько помнится. Все остальное было с 2006, что-то может немного позже, а то, чего не хватало появлялось достаточно быстро, можно было и самому написать, вплоть до расширения синтаксиса. К тому же, то что было и то, что есть в языке — на две головы выше, чем в том же шарпе есть сейчас. При чем многое из всего этого доступно было еще 10-14 лет назад Карл! К чему можно придраться, так это, чтобы написать linq query нужно писать linq <# from .... #>.
Просто, чтобы расширить кругозор пройдись по ссылкам, что я дал, посмотри примеры и оцени масштабы
И главное, в немерле многие вещи, которые казались невозможными или труднореализуемыми — доступны, главное понять это. А понимаешь это не сразу, так как трудно сразу думать другими категориями.
S>В C# 7.2. появились Span<T> и Memory<T> S>https://docs.microsoft.com/ru-ru/dotnet/csharp/write-safe-efficient-code
Нативная поддержка Span<T> это заслуга рантайма, а не шарпа.
Здравствуйте, rameel, Вы писали:
R>а то, чего не хватало появлялось достаточно быстро, можно было и самому написать, вплоть до расширения синтаксиса.
Что и было продемонстрировано на примере добавления линка. Правда, почему то пришлось параллельно основательно перепахать компилятор, потому что expression tree не влез в возможности макросов, но то такое.
R>К тому же, то что было и то, что есть в языке — на две головы выше, чем в том же шарпе есть сейчас. При чем многое из всего этого доступно было еще 10-14 лет назад Карл! К чему можно придраться, так это, чтобы написать linq query нужно писать linq <# from .... #>.
А к чему придраться, видимо, нельзя, так это к тому что линк в немерле появился позже шарпа.
Ну и еще один мелкий нюансик. До Немерле те же фичи были в Хаскеле и еще пачке других языков, а подиж ты.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Что и было продемонстрировано на примере добавления линка. Правда, почему то пришлось параллельно основательно перепахать компилятор, потому что expression tree не влез в возможности макросов, но то такое.
Для того, чтобы поддержать линк в том виде, что есть, перепахивать компилятор не пришлось. А вот с expression tree всех деталей я не помню. Недостатки в реализации макросов пересмотрели только в N2, но заметь, даже этого хватило, чтобы линк заработал. Если так смотреть, то в шарпе так вообще грамматику языка перепахали, но то такое
R>>К тому же, то что было и то, что есть в языке — на две головы выше, чем в том же шарпе есть сейчас. При чем многое из всего этого доступно было еще 10-14 лет назад Карл! К чему можно придраться, так это, чтобы написать linq query нужно писать linq <# from .... #>.
НС>А к чему придраться, видимо, нельзя, так это к тому что линк в немерле появился позже шарпа.
Так я что против?! Я только за. То, что показало себя удобно, перекочевывало из шарпа, при чем достаточно быстро, а вот в обратную сторону — годами.
НС>Ну и еще один мелкий нюансик. До Немерле те же фичи были в Хаскеле и еще пачке других языков, а подиж ты.
Из немерля мне не хватало толька патерн матчинга.
Макросы заменяли T4 и использование рослина для генерации классов.
То, что появилось и нет в Nemerle больше нужно. Один async awaite чего стоит. >> это заслуга рантайма, а не шарпа.
Ну этого не хватало, а так как основной язык .Net это C# то и заслуга его.
Все таки говорить, что C# это недоделанный немерль неправда!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, rameel, Вы писали:
НС>>Ну и еще один мелкий нюансик. До Немерле те же фичи были в Хаскеле и еще пачке других языков, а подиж ты. R>А кто-то говорил об исключительности?
Никто, однако, не говорит, что C# превращается в Хаскель или там Скалу, верно?
Здравствуйте, Serginio1, Вы писали:
S>Из немерля мне не хватало толька патерн матчинга.
То, что в шарпе появился паттерн-матчинг хорошо, теперь ждем 9 версию, в которой может устранят куцость текущего.
S>Макросы заменяли T4 и использование рослина для генерации классов.
Вы делаете мне смешно... Если мощностей T4 хватало бы, в команде шарпа не задумались бы о source generator. Только сколько времени теперь пройдет, когда мы увидим квазицитаты для этого?
S>То, что появилось и нет в Nemerle больше нужно. Один async awaite чего стоит.
async/await есть в немерле. Я говорил об async streams, которые только вот появились в шарпе
S>Все таки говорить, что C# это недоделанный немерль неправда!
ЗЫ. Вы зачем так агритесь на то, что кто-то указал на недостатки шарпа?!
Ты спросил, что было, а что нет в немерле, я тебе написал. Шарп — это всего лишь инструмент, которым пользуюсь и я каждый день, но это не мешает мне видеть плюсы и минусу моего инструмента.
ЗЫ. Несколько лет назад у нас в команде были джависты, которым тоже все эти var, foreach, структуры, linq и expression trees, properties не нужны были, излишними и чужеродными. Но стоило некоторым вещам появится в джаве как вдруг это все надо и удобно. Или вот, одному из команды джавистов нужно было пересесть на шарп, то скажем так, проникся, особенно поработав с linq2db. Да, по началу плевался, чертыхался, здесь не так и тут не эдак, но позже возвращаясь обратно было досадно, что нельзя как в шарпе. При том, что и джаве было/есть что предложить шарпу — те же реализации методов интерфейса по умолчанию. Или к примеру, анонимные классы с реализацией интерфейса по месту. Или вот, поддержка tiered compilation в джите. Разве плохо, что плюсы и возможности других языков и платформ появляются в твоем любимом языке или платформы?! Или теперь нельзя обсуждать недостатки/недоделки языка/платформы потому что чьи чувства будут задеты, потому что в моем инструменте все прекрасно, все идеально. Нет не все, иначе язык не развивался бы так как сейчас.
Expression<Func<int,int>> f = i => i * 0 + 0 + i + 10 * (i * 0 + 2);
var f1 = f.TransformEx(ex => ex switch
{
Multiply(Constant(0) e, _) => e, // 0 * e => 0
Multiply(_, Constant(0) e) => e, // e * 0 => 0
Multiply(Constant(1), var e) => e, // 1 * e => e
Multiply(var e, Constant(1)) => e, // e * 1 => e
Divide (Constant(0) e, _) => e, // 0 / e => 0
Divide (var e, Constant(1)) => e, // e / 1 => e
Add (Constant(0), var e) => e, // 0 + e => e
Add (var e, Constant(0)) => e, // e + 0 => e
Subtract(Constant(0), var e) => Negate(e), // 0 - e => -e
Subtract(var e, Constant(0)) => e, // e - 0 => e
Multiply(Constant(int x), Constant(int y)) => Constant(x * y), // x * y => e
Divide (Constant(int x), Constant(int y)) => Constant(x / y), // x / y => e
Add (Constant(int x), Constant(int y)) => Constant(x + y), // x + y => e
Subtract(Constant(int x), Constant(int y)) => Constant(x - y), // x - y => e
_ => ex
});
Console.WriteLine(f);
Console.WriteLine(f1);
Assert.IsTrue(f1.EqualsTo(i => i + 20));
Здравствуйте, Ночной Смотрящий, Вы писали:
R>>А кто-то говорил об исключительности?
НС>Никто, однако, не говорит, что C# превращается в Хаскель или там Скалу, верно?
Шарп и немерл визуально очень близки, вот и сравнение... Был бы F# с С-подобным синтаксисом говорили бы... хотя о шарпе наверное мало бы тогда говорили
Шарп развивается сейчас быстро и много фич из разных языков взято. Здесь проблема скорее всего была с совместимостью итд.
Сейчас язык будет развиваться без оглядки на старый фреймворк и будут вводиться новые фичи в том числе и для Source Generators .
Посмотрим.
А так не хватало даже обычного
if (o is int n)
и солнце б утром не вставало, когда бы не было меня
R>Чего не было (как сейчас не знаю), так это ref locals/ref return values, expression bodied methods, throw expressions
throw expressions вроде были, не было catch ... when
Здравствуйте, hi_octane, Вы писали:
R>>Чего не было (как сейчас не знаю), так это ref locals/ref return values, expression bodied methods, throw expressions _>throw expressions вроде были
R>Expression<Func<int,int>> f = i => i * 0 + 0 + i + 10 * (i * 0 + 2);
R>var f1 = f.TransformEx(ex => ex switch
R>{
R> Multiply(Constant(0) e, _) => e, // 0 * e => 0
R> Multiply(_, Constant(0) e) => e, // e * 0 => 0
R> Multiply(Constant(1), var e) => e, // 1 * e => e
R> Multiply(var e, Constant(1)) => e, // e * 1 => e
R> Divide (Constant(0) e, _) => e, // 0 / e => 0
R> Divide (var e, Constant(1)) => e, // e / 1 => e
R> Add (Constant(0), var e) => e, // 0 + e => e
R> Add (var e, Constant(0)) => e, // e + 0 => e
R> Subtract(Constant(0), var e) => Negate(e), // 0 - e => -e
R> Subtract(var e, Constant(0)) => e, // e - 0 => e
R> Multiply(Constant(int x), Constant(int y)) => Constant(x * y), // x * y => e
R> Divide (Constant(int x), Constant(int y)) => Constant(x / y), // x / y => e
R> Add (Constant(int x), Constant(int y)) => Constant(x + y), // x + y => e
R> Subtract(Constant(int x), Constant(int y)) => Constant(x - y), // x - y => e
R> _ => ex
R>});
R>Console.WriteLine(f);
R>Console.WriteLine(f1);
R>Assert.IsTrue(f1.EqualsTo(i => i + 20));
R>
О, а как это работает? Мне такое нужно, а то код арифметического упрощения похож на какие-то спагетти. Тут же ex какого-то особенного типа, не System.Linq.Expressions.Expression?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>>То, что появилось и нет в Nemerle больше нужно. Один async awaite чего стоит.
R>async/await есть в немерле. Я говорил об async streams, которые только вот появились в шарпе
Я про то, что изначально в Немерле не было async/await, Linq итд. И появились они не сразу.
Я к тому, что языки постоянно развиваются и берут друг у друга фичи.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinclair, Вы писали:
S>О, а как это работает? Мне такое нужно, а то код арифметического упрощения похож на какие-то спагетти. Тут же ex какого-то особенного типа, не System.Linq.Expressions.Expression?
Да, перед матчингом выражение конвертируется в Expr, так как в Expression бинарные операции выражены просто через единый BinaryExpression, унарные — через UnaryExpression и т.п., что при матчинге наглядности не добавят, поэтому такой вот дополнительный шаг. Для полноты были написаны соответствующие методы расширения для деконструкции колекции параметров и инициализаторов. А для еще большей читаемости добавляем в начале файла:
using static Linq.Expressions.Deconstruct.Expr;
using static System.Linq.Expressions.Expression;
А дальше шарп уже сам включит всю магию паттерн-матчинга.
А да, также у Expr есть implicit оператор обратно в Expression
Здравствуйте, Serginio1, Вы писали:
S>Я про то, что изначально в Немерле не было async/await, Linq итд. И появились они не сразу.
Не сразу, это грубо говоря, недели две после
Здравствуйте, Serginio1, Вы писали:
S> Шарп развивается сейчас быстро и много фич из разных языков взято.
Это не может не радовать, только вот
S>Сейчас язык будет развиваться без оглядки на старый фреймворк и будут вводиться новые фичи в том числе и для Source Generators .
Хотелось бы, чтобы смелее смотрели на то, что вокруг сделано и как.
Здравствуйте, rameel, Вы писали:
R>Здравствуйте, Serginio1, Вы писали:
S>>Я про то, что изначально в Немерле не было async/await, Linq итд. И появились они не сразу. R>Не сразу, это грубо говоря, недели две после
Угу http://rsdn.org/forum/nemerle/7731140.flat
Здравствуйте, rameel, Вы писали:
R>Здравствуйте, Serginio1, Вы писали:
S>> Шарп развивается сейчас быстро и много фич из разных языков взято.
R>Это не может не радовать, только вот
S>>Сейчас язык будет развиваться без оглядки на старый фреймворк и будут вводиться новые фичи в том числе и для Source Generators .
R>Хотелось бы, чтобы смелее смотрели на то, что вокруг сделано и как.
Я к тому, что необходимость паттернматчинга и генерация кода в дизайн тайме очевидна. Почему так долго непонятно.
Но как только начал развиваться .Net Core количество изменений языка сразу выросло. Может это связано в том числе и с опен соурсом.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, rameel, Вы писали:
Отлично. Попробовал. Есть ощущение, что возвёрнутые значения дальше не трансформируются. Т.е. трансформация работает только снизу вверх.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Я все же настоятельно рекомендую посмотреть документацию, не подумай, я не агитирую тебя писать на немерле, просто для саморазвития, тогда и вопросов будет меньше, а что не так. Немерле это как пример весьма удачной концепции compiler as plugin, реализация — это другой вопрос. Не нравятся слово немерле, посмотри на макросы раста. В немерле тоже не все есть. Мне например нравятся идея зависимых типов, хотел бы видеть, на худой конец возможность задавать контрактное поведение, чтобы зависимости и инварианты проверялись на этапе компиляции и полностью устранялись из машинного кода. Посмотри на идрис, агду или dafny, у которого синтаксис приятный/привычный. Вот кстати nullable reference, добавленный в шарп, можно сказать часть контракта, всего лишь часть от целого. Смотрели бы шире, добавили бы обобщение, а не только nullable, тогда можно было бы писать:
public static T Get<T>(T[] array, int index)
requires array != null
requires index >= 0 && index < array.Length
{
return array[index];
}
И все это проверялось бы еще на этапе компиляции. Гляди там и JIT научат смотреть на аннотации, тогда и null и bounds check'и устранятся. Для array != null можно и ? добавить как сейчас в шарпе.
Та же возможность вмешиваться в этап компиляции позволит прозрачно для пользователя переписывать linq в набор for и foreach, устранить косвенность, в точности и при вызове делегатов, устранить аллокации и даже слохпнуть это в константное выражение, когда можно. Бонусом, в большинстве случаев выражать Enumerator через структуры будет уже не надо.
А сейчас имеем, что имеем. Хочешь избежать мусора в памяти пиши for или енумератор на структурах, нужен индекс пиши for, нужен свитч по регулярке пиши if, хочешь lock над ReaderWriterLockSlim потрудись расписывать все через try-finally, нужна реализация для IEquatable — пиши сам, нужен dispose внутренних объектов — пиши и внимательно, никого не забудь, и таких вот мелочей куча. По отдельности некоторые вещи это мелочи вроде, но попробуй сейчас пересесть на шарп от второго фреймворка.
Вот сейчас полноценно в строй введут source-генераторы, то даже с тем, что есть (добавлять, не изменять), сколько всего будет сделано, что будет казаться и как раньше без этого жили то
Здравствуйте, Sinclair, Вы писали:
S>Отлично. Попробовал. Есть ощущение, что возвёрнутые значения дальше не трансформируются. Т.е. трансформация работает только снизу вверх.
Ну, то есть как это работает: допустим, у меня есть таблица для некоторых ParameterExpression, которая задаёт их пределы.
Типа {minA} <= {a} <= {maxA}.
Теперь у меня есть выражение типа (a + 1 > 0).
Я пытаюсь его свернуть. Как это делается?
При помощи правила, которое сопоставляет выражениям вида {a} > e вот такую кракозяблу (({a} > e) || (minA > e)) && (maxA > e)). (1)
Перед этим надо превратить (a + 1 > 0) в (a > -1). (2)
Но вот у меня правило, которое вызвалось на выражении LessThan(Add(var e, Constant(int x))), Constant(int y)), вернуло оно (a > -1) и всё, поезд ушёл. Трансформатор поехал выше.
Допустим, я запихал правило 2 в первую фазу, а правило 1 — во вторую фазу трансформации.
Теперь, скажем, когда у нас в качестве minA указан {0}, а maxA — {h}, мы получаем (({a} > {-1}) || ({0} > {-1})) && ({h} > {-1}))
Тут надо заново начинать трансформацию, т.к. {0} > {-1} => {true}, ({a} > {-1}) || {true})=>{true}, {true} && ({h} > {-1}) => ({h} > {-1}). Ок, это у нас была фаза трансформации №3 — свёртка AndAlso и OrElse с конст-аргументами.
Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}.
Итого — либо мы гоняем цикл
Здравствуйте, rameel, Вы писали:
R>Здравствуйте, Serginio1, Вы писали:
>>>>При компиляции выдает что на list[t] не может найти linq Max, Min.
R>Просто баг, а не отсутствие фичи
Вот и проблема немерля в том, что не хватает сил на компилятор итд.
Например для оптимизации неплохо оптимизировать перед компиляцией roslyn-linq-rewrite
и солнце б утром не вставало, когда бы не было меня
Здесь проблема как в сообществе, так и разработчиках.
Если сообщество будет давить сильнее, то разработчики прогнутся. Акиньшин рассказывал, как чувак кестрел помогал разрабатывать.
И здесь нужно определенное давление сообщества. То, что мы с тобой поговорим, от этого новых фич не появится.
А вот разработать систему давления и хотя бы отзывы почему это не делается и как помочь, идеи для реализации это даже важнее чем сами фичи
Ну или там голосование, какая фича самая востребованная. Можешь выбрать только одну
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinclair, Вы писали:
S>Отлично. Попробовал. Есть ощущение, что возвёрнутые значения дальше не трансформируются. Т.е. трансформация работает только снизу вверх.
Да, получается снизу вверх.
S>Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}. S>Итого — либо мы гоняем цикл S>
Здравствуйте, rameel, Вы писали: R>Тут подумать надо как лучше, но насколько мне известно, clang именно так и делает — гоняет правила трасформации по кругу
Тут меня смущает потенциальная непроизводительность — эти трансформации работают в основном на уровне листьев, поэтому перетрансформация отдельной веточки интуитивно дешевле, чем полный проход по дереву.
С другой стороны, может тут экономия на спичках. Надо сначала добиться корректности.
А она в первую очередь зависит от компактности изложения правил.
Игоревский трансформатор уже очень, очень хорошо ужимает код. И даже с ним там простейшие трансформации превращаются в простыни:
Add(Constant(int x), Add(var e, Constant(int y))) => Add(e, Constant(x + y)), // x + (e + y) => e + (x + y)
Add(Constant(int x), Add(Constant(int y), var e)) => Add(e, Constant(x + y)), // x + (y + e) => e + (x + y)
Add(Add(var e, Constant(int x)), Constant(int y)) => Add(e, Constant(x + y)), // (e + x) + y => e + (x + y)
Add(Add(Constant(int x), var e), Constant(int y)) => Add(e, Constant(x + y)), // (x + e) + y => e + (x + y)
Add(Constant(int x), Subtract(var e, Constant(int y))) => Add(e, Constant(x - y)), // x + (e - y) => e + (x - y)
Add(Constant(int x), Subtract(Constant(int y), var e)) => Subtract(Constant(x + y), e), // x + (y - e) => (x + y) - y
Add(Subtract(var e, Constant(int x)), Constant(int y)) => Add(e, Constant(y - x)), // (e - x) + y => e + (y - x)
Add(Subtract(Constant(int x), var e), Constant(int y)) => Subtract(Constant(x + y), e), // (x - e) + y => (x + y) - e
Subtract(Constant(int x), Add(var e, Constant(int y))) => Subtract(Constant(x - y), e),
Subtract(Constant(int x), Add(Constant(int y), var e)) => Subtract(Constant(x - y), e),
Subtract(Add(var e, Constant(int x)), Constant(int y)) => Add(e, Constant(x - y)),
Subtract(Add(Constant(int x), var e), Constant(int y)) => Add(e, Constant(x - y)),
Есть искушение, к примеру, задавить на первом проходе все Subtract в Add(a, Negate(b)), чтобы сократить объём правил.
И так же LessThan в Not(GreaterThanOrEqual).
Кстати, чего я не понял — зачем такой огромный switch в TransformInternal.
Там же можно просто по типам свитчится, а не по NodeType.
case BinaryExpression be
expr = be.Update(
TransformInternal(be.Left, func),
(LambdaExpression?)TransformInternal(be.Conversion, func),
TransformInternal(be.Right, func));
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, rameel, Вы писали:
R>Хотелось бы, чтобы смелее смотрели на то, что вокруг сделано и как.
А с чего ты взял что они не смотрят? Language design process у шарпа сейчас более менее открытый. А делать из него экспериментальный никто не будет, не то назначение у языка.
Здравствуйте, rameel, Вы писали: R>Тут подумать надо как лучше, но насколько мне известно, clang именно так и делает — гоняет правила трасформации по кругу
Прикрутил. Вроде работает. Теперь надо научиться сворачивать выражения типа ((i+1)-(i+2)+1).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Есть искушение, к примеру, задавить на первом проходе все Subtract в Add(a, Negate(b)), чтобы сократить объём правил. S>И так же LessThan в Not(GreaterThanOrEqual).
Можно еще попробовать сделать реассоциацию выражений, в приниципе, ты это и предложил только еще с перестановкой, чтобы b + a ==> a + b и b < a ==> в a > b, чтобы сократить количество правил, чтобы вот такое ((a + c1) + b) + c2 превратить в такое c2 + c1 + a + b, что обнаружится при constant folding
S>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. S>Там же можно просто по типам свитчится, а не по NodeType.
Наверное можно было бы, но свитч по типам он последовательный, а свитч на enum по таблице переходов работает, там 22-23 ветки, хотя да код подсократился бы
Здравствуйте, Ночной Смотрящий, Вы писали:
R>>Хотелось бы, чтобы смелее смотрели на то, что вокруг сделано и как.
НС>А с чего ты взял что они не смотрят? Language design process у шарпа сейчас более менее открытый.
Ключевое слово смелее. А так да, хорошо что не стоят на месте
НС>А делать из него экспериментальный никто не будет, не то назначение у языка.
Сейчас они и так экпериментируют, те же шейпы взять
Здравствуйте, rameel, Вы писали:
R>Можно еще попробовать сделать реассоциацию выражений, в приниципе, ты это и предложил только еще с перестановкой, чтобы b + a ==> a + b и b < a ==> в a > b, чтобы сократить количество правил, чтобы вот такое ((a + c1) + b) + c2 превратить в такое c2 + c1 + a + b, что обнаружится при constant folding
Вот тут я пока не очень понимаю, как это сделать. Ну, то есть я могу выделять константы — правило про (e1 + c1) + (e2 + c2) => (e1 + e2) + {c1+c2} и подобные есть.
А вот для ((a + c1) + b) + c2 правила нету, и такие выражения остаются как есть. S>>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. S>>Там же можно просто по типам свитчится, а не по NodeType.
R>Наверное можно было бы, но свитч по типам он последовательный, а свитч на enum по таблице переходов работает, там 22-23 ветки, хотя да код подсократился бы
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, rameel, Вы писали:
НС>>А делать из него экспериментальный никто не будет, не то назначение у языка. R>Сейчас они и так экпериментируют, те же шейпы взять
Те же шейпы из релиза убрали, потому что дизайн их недостаточно готов для релиза. В этом ключевое отличие от Немерли, где каждый тащит в язык что хочет.
R1: c1 + c2 = c1+c2
R3: c1 * c2 = c1*c2
R5: c1 - c2 = c1-c2
R2: t + c = c + t
R4: t * c = c * t
R6: t - c = (-c) + t
R7: t1 + (t2 + t3) = (t1 + t2) + t3
R8: t1 * (t2 * t3) = (t1 * t2) * t3
R9: (c1 + t) + c2 = (c1+c2) + t
R10: (c1 * t) * c2 = (c1*c2) * t
Выражения слева матчатся, соответственно, правилами R2 и R4; константы уезжают налево: c2 ~ (c1 ~ t); дальше срабатывают правила R7 и R8, получая как раз (c2 ~ c1) ~ t, оттуда по правилам R1/R3 получаем финальное выражение из константы и нашего терма.
Хорошо, что такие вещи вылавливает сразу компилятор C#.
R>Google book: Advanced Compiler Design Implementation
Да, давненько я не перечитывал Аппеля.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: S>Да, давненько я не перечитывал Аппеля.
Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов.
Либо надо допиливать как-то так:
Multiply(Constant(Type t, var x) e, _) when t.IsNumeric && Convert.ToDouble(x) == 0.0 => e, // 0 * e => 0
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: R>>Google book: Advanced Compiler Design Implementation S>Да, давненько я не перечитывал Аппеля.
В общем, с range checking как-то пока не выходит.
Проблемы с выражениями типа j-1 < w, если известно, что j < w.
Арифметические реассоциации превращают это дело в -1 + j < w
И вот тут начинаются приключения, потому что я хочу одновременно
оставить слева только одну j, чтобы увидеть там ограниченный параметр, и попробовать подставить в это выражение maxJ. То есть нужны правила, приводящие то, что сверху, к j < (1 + w).
Уже не вполне понятно, как именно. Правила "параметры едут налево" приведут к j + -w < 1. Правила "константы едут направо" могут помочь, но неясно, как это всё сработает в более общем случае.
когда я подставляю туда maxJ, то получаем (w-1)<(1+w). Арифметические реассоциации трансформируют его в
-1 + w < 1 + w
И вот дальше непонятно, что c этим делать — если утаскивать константы направо, то получим w < (2 + w) (опять же, после реассоциации и constant folding в правой части).
Надо как-то из этого сделать true.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>В общем, с range checking как-то пока не выходит. S>Проблемы с выражениями типа j-1 < w, если известно, что j < w. S>Арифметические реассоциации превращают это дело в -1 + j < w S>Надо как-то из этого сделать true.
Можно хранить свойства отношений больше/меньше/равно для каждой переменной. То есть если известно, что j < w то для j заводится информация об этом в отношении w также как для w в отношении j. Далее, если отбросить переполнение типов, то исходить от истинности условий, что j — c < w равно как и j < w + c. Остается прописать правила истинности и проверять это отдельным проходом.
Здравствуйте, VladD2, Вы писали:
VD>Им надо меня с Хардкейсом на работу взять и дать нам человек 5 в подчинение. Будут тогда супер-макросы через год-два.
Боюсь вы опять улетите в дальние дали. Не надо. Пусть C# живёт.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, vmpire, Вы писали:
V>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
Что будет если поменять порядок объявления параметров в методе?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, rameel, Вы писали:
R>Можно хранить свойства отношений больше/меньше/равно для каждой переменной. То есть если известно, что j < w то для j заводится информация об этом в отношении w также как для w в отношении j.
Это уже сделано. R>Далее, если отбросить переполнение типов, то исходить от истинности условий, что j — c < w равно как и j < w + c. Остается прописать правила истинности и проверять это отдельным проходом.
Вот непонятно, что значит "исходить из истинности условий". И что такое "правила истинности". В моём воображении всё работает примерно так: есть правила редукции. В конце концов мы должны прийти к выражению из одних констант, которое свернётся в true. Для range мы подставляем в сравнение минимальное значение — если даже оно заведомо не меньше правой части (редукция в false), то и всё выражение false.
Если при подстановке максимального значения получаем true, то и всё выражение true. Если же нам не удаётся редуцировать границы диапазона таким образом, то, значит, придётся честно вычислять выражение в рантайме.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Итого — либо мы гоняем цикл
Изначально этот код был выдернут из linq2db, где он работал в обратном направлении. Можно сделать ещё один метод (или два) для любых других направлений. Всё в ваших руках. Могу дать доступ к репе.
ЗЫ. Код был написан на коленке за недельку в качестве изучения новых возможностей ПМ. Так что строго не судите.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}.
Тебе скорее всего нужен метод, в котором ты сам будешь управлять необходимостью повторных трансформаций, т.к. это напрямую зависит от самих трансформаций.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, rameel, Вы писали: S>Тут меня смущает потенциальная непроизводительность — эти трансформации работают в основном на уровне листьев, поэтому перетрансформация отдельной веточки интуитивно дешевле, чем полный проход по дереву.
В linq2db работает версия, которая позволяет останавливать трансформацию вообще. Т.е. в таких вот непонятных случаях мы текущую трансформацию останавливаем, но перед этим запускаем новую с нужного места.
S>С другой стороны, может тут экономиS>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal.
Стырено из linq2db.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов.
Сделай gemeric метод для трансформаций с этими типами и гоняй по кругу. Ну это так, чтоб совсем не мучиться
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
V>>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
IT>Что будет если поменять порядок объявления параметров в методе?
Очевидно, будет ошибка. Представьте, например, метод с двумя строковыми параметрами: строка формата и форматируемое значение.
А потои автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат.
Здравствуйте, IT, Вы писали: IT>Сделай gemeric метод для трансформаций с этими типами и гоняй по кругу. Ну это так, чтоб совсем не мучиться
Ну так возможность сделать генерик-метод как раз ограничена отсутствием шейпов
Мне же надо что-то вроде
Мысль, которую думал ночью: сделать CalculateBinary(ExpressionType type, MethodInfo method, Constant left, Constant right), в который и делегировать все Binary операции c константами.
Там же надо аккуратно — если есть метод, то вызвать его (все же знают, что "Hello, "+ "world" — это ExpressionType.Add c Method == string.Concat?).
Если нет метода — то уже свитчиться по типу выражения и вычислять его. Если пренебречь checked, то можно просто конвертировать обе константы в дабл, а потом приводить результат обратно к node.Type при помощи Convert.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, IT, Вы писали:
IT>В linq2db работает версия, которая позволяет останавливать трансформацию вообще. Т.е. в таких вот непонятных случаях мы текущую трансформацию останавливаем, но перед этим запускаем новую с нужного места.
Ну, мне в итоге пришлось таки сделать трансформацию в цикле. Иначе приходится писать в разы больше правил для обеспечения протаскивания трансформации вверх.
И трансформацию отдельных подветочек тоже пришлось сделать — лобовой подход с range checking не проходит. Он навешивает на каждое сравнение параметра два дополнительных сравнения, из которых не более одного можно свернуть в константу, в итоге дерево неконтролируемо растёт.
Так что окончательный вариант проверялки диапазона выглядит так:
Expression CheckRange(Expression node, ParameterExpression left, Expression right)
{
var range = variableRanges[left];
if (range.maxVal != null && Arithmetic.Simplify(MakeBinary(node.NodeType, range.maxVal, right), variableRanges) is ConstantExpression rangeMatch1 && (bool)rangeMatch1.Value == true)
return rangeMatch1;
if (range.minVal != null && Arithmetic.Simplify(MakeBinary(node.NodeType, range.minVal, right), variableRanges) is ConstantExpression rangeMatch2 && (bool)rangeMatch2.Value == false)
return rangeMatch2;
return node;
}
Вызывается он вот так:
LessThan(Parameter pe, var e) when variableRanges.ContainsKey(pe) => CheckRange(expr, pe, e),
LessThanOrEqual(Parameter pe, var e) when variableRanges.ContainsKey(pe) => CheckRange(expr, pe, e),
S>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. IT>Стырено из linq2db.
А, ок.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Sinclair, Вы писали:
S>>Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}.
IT>Тебе скорее всего нужен метод, в котором ты сам будешь управлять необходимостью повторных трансформаций, т.к. это напрямую зависит от самих трансформаций.
В целом — да. Но пример из https://github.com/igor-tkachev/Linq.Expressions.Deconstruct/blob/master/Tests/Tests.cs#L156-L183, например, делает неполный фолдинг.
Например, i + 1 — 1 + 2 — i свёрнут не будет — нет правил для перегруппировки.
А как только мы введём такие правила, их придётся гонять по кругу.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vmpire, Вы писали: V>А потом автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат.
Да, в дорешарперные эпохи так и было. А сейчас всё делает IDE — вот и с классом также будет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
V>>А потом автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат. S>Да, в дорешарперные эпохи так и было. А сейчас всё делает IDE — вот и с классом также будет.
"Всё делает IDE" только если у вас маленькие простые проекты.
А если метод, который изменился, находится в библиотеке, которая в отдельном солюшне и используется другой командой, которая её только через месяц в виде пакета подтянет в свой проект, то начинаются очень интересные вещи.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Sinclair, Вы писали: S>>Да, давненько я не перечитывал Аппеля. S>Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов. S>Либо надо допиливать как-то так: S>
S>Multiply(Constant(Type t, var x) e, _) when t.IsNumeric && Convert.ToDouble(x) == 0.0 => e, // 0 * e => 0
S>
Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double. И в оптимизаторе уже матчить всё на Double
Здравствуйте, Sinclair, Вы писали:
S>Ты имеешь в виду Add(Constant(T x), Constant(T y)) => Constant(new LambdaExpression(expr).Compile()())?
Что-то типа вот этого:
[Test]
public void ConstantFoldingExTest2()
{
Expression<Func<int,int>> f = i => i + 1 - 1 + 2 - i;
Expr Transform<T>(Expr ex, Func<T,T,T> multiply, Func<T,T,T> divide, Func<T,T,T> add, Func<T,T,T> subst)
{
return ex switch
{
Multiply(Constant(T x), Constant(T y)) => Constant(multiply(x, y)), // x * y => e
Divide (Constant(T x), Constant(T y)) => Constant(divide (x, y)), // x / y => e
Add (Constant(T x), Constant(T y)) => Constant(add (x, y)), // x + y => e
Subtract(Constant(T x), Constant(T y)) => Constant(subst (x, y)), // x - y => e
_ => null
};
};
var f1 = f.TransformEx(ex => ex switch
{
Multiply(Constant(0) e, _) => e, // 0 * e => 0
Multiply(_, Constant(0) e) => e, // e * 0 => 0
Multiply(Constant(1), var e) => e, // 1 * e => e
Multiply(var e, Constant(1)) => e, // e * 1 => e
Divide (Constant(0) e, _) => e, // 0 / e => 0
Divide (var e, Constant(1)) => e, // e / 1 => e
Add (Constant(0), var e) => e, // 0 + e => e
Add (var e, Constant(0)) => e, // e + 0 => e
Subtract(Constant(0), var e) => Negate(e), // 0 - e => -e
Subtract(var e, Constant(0)) => e, // e - 0 => e
_ when Transform<int> (ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ when Transform<long> (ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ when Transform<double>(ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ => ex
});
Console.WriteLine(f);
Console.WriteLine(f1);
Assert.IsTrue(f1.EqualsTo(i => 2));
}
Только вместо лямбд, если уж заниматься этим серьёзно, следует залудить какой-нибудь интерфейс или базовый класс и сделать все арифметические операции в нём.
Либо сгенерировать всю эту хрень.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Jack128, Вы писали:
J>Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double.
Всё — нет, недопустимо. Потом запаришься обратно приводить — потому что there is no built-in operator Add defined for the types [byte] and [Double].
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
J>>Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double. S>Всё — нет, недопустимо. Потом запаришься обратно приводить — потому что there is no built-in operator Add defined for the types [byte] and [Double].
Кстати, эту идею стоит помусолить. Например, ввести наряду с Constant что-нибудь типа Numerical, которая будет такой же константой, но любого числового типа. Количество явно используемых констант используемых в паттернах, вроде 0,1 должно быть ограниченным и можно смело использовать целые числа.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Кстати, эту идею стоит помусолить. Например, ввести наряду с Constant что-нибудь типа Numerical, которая будет такой же константой, но любого числового типа. Количество явно используемых констант используемых в паттернах, вроде 0,1 должно быть ограниченным и можно смело использовать целые числа.
Даже ещё проще. Нужен ещё один деконструктор для Constant, который будет заточен исключительно на числа и сравнивать их значения с целым.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>А как только мы введём такие правила, их придётся гонять по кругу.
Вот вариант с частичным гонянием по кругу:
public static T Fold<T>(T expr)
where T : notnull, LambdaExpression
{
return expr.TransformEx<T>(FoldExpr);
}
public static Expression? Fold(Expression? expr)
{
return expr.TransformEx(FoldExpr);
}
static Expr FoldExpr(Expr ex)
{
return ex switch
{
Multiply(Constant(0) e, _) => e, // 0 * e => 0
Multiply(_, Constant(0) e) => e, // e * 0 => 0
Multiply(Constant(1), var e) => e, // 1 * e => e
Multiply(var e, Constant(1)) => e, // e * 1 => e
Divide (Constant(0) e, _) => e, // 0 / e => 0
Divide (var e, Constant(1)) => e, // e / 1 => e
Add (Constant(0), var e) => e, // 0 + e => e
Add (var e, Constant(0)) => e, // e + 0 => e
Subtract(Constant(0), var e) => Negate(e)!, // 0 - e => -e
Subtract(var e, Constant(0)) => e, // e - 0 => e
Multiply(Constant(int x), Constant(int y)) => Constant(x * y)!, // x * y => e
Divide (Constant(int x), Constant(int y)) => Constant(x / y)!, // x / y => e
Add (Constant(int x), Constant(int y)) => Constant(x + y)!, // x + y => e
Subtract(Constant(int x), Constant(int y)) => Constant(x - y)!, // x - y => e
Add (Add (Constant(int c1), var e), Constant(int c2)) => Fold(Add (Constant(c1 + c2), e)), // (c1 + e) + c2 = (c1 + e2) + e
Multiply(Multiply(Constant(int c1), var e), Constant(int c2)) => Fold(Multiply(Constant(c1 * c2), e)), // (c1 + e) + c2 = (c1 + e2) + e
Add (var e, Constant c) => Add (c, e), // e + c => c + e
Multiply(var e, Constant c) => Multiply(c, e), // e * c => c * e
Subtract(var e, Constant(int x)) => Add (Constant(-x), e), // e - c => (-c) + e
Add (var e1, Add (var e2, var e3)) => Fold(Add (Add (e1, e2), e3)), // e1 + (e2 + e3) = (e1 + e2) + e3
Multiply(var e1, Multiply(var e2, var e3)) => Fold(Multiply(Multiply(e1, e2), e3)), // e1 + (e2 + e3) = (e1 + e2) + e3
Subtract(Add(var e1, var e2), var e3) when e2 == e3 => e1, // (e1 + e2) - e2 => e1
_ => ex
};
}
Последнее правило тупо захардкожено, но можно либо расширить, либо сделать полную имплементацию аналогичную константам.
ЗЫ. Насчёт типов (шейпов) тоже есть идея как решить проблему.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, VladD2, Вы писали:
VD>>Причем новое лючевое слово все равно вводят. Можно было бы тупо писать "record" вместо "data class"
НС>А вместо data struct?
UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Здравствуйте, AlexRK, Вы писали: ARK>UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Почему мутабельными-то???
Records are immutable by default.
Как фсигда: разобраться времени нет, но надо обязательно покритиковать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, AlexRK, Вы писали:
ARK>UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Records are specialized classes
As I just covered, the record and the class variants of LoginResource are almost identical. The class definition is a semantically identical subset of the record. The record provides more, specialized, behavior.
Just so we’re on the same page, the following comparison is between a record, and a class that uses init instead of set for properties, as demonstrated earlier.
What’s the same?
Construction
Immutability
Copy semantics (records are classes under the hood)
What’s different?
Record equality is based on content. Class equality based on object identity.
Records provide a GetHashCode() implementation is that is based on record content.
Records provide an IEquatable<T> implementation. It uses the unique GetHashCode() behavior as the mechanism to provide the content-based equality semantic for records.
Record ToString() is overridden to print record content.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, AlexRK, Вы писали: ARK>Не надо никаких by default. Уже есть классы и структуры.
Отличная тактика. Если спорол чушь, то не надо признавать это — надо тут же спороть другую чушь, чтобы отвлечь собеседников от чуши предыдущей.
Продолжайте, мне сейчас курьер пакет попкорна должен привезти.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
ARK>>Не надо никаких by default. Уже есть классы и структуры. S> Отличная тактика. Если спорол чушь, то не надо признавать это — надо тут же спороть другую чушь, чтобы отвлечь собеседников от чуши предыдущей. S>Продолжайте, мне сейчас курьер пакет попкорна должен привезти.
Какая еще "чушь", "тактика", але. Негрософт сделал очередное мутабельное говно, впридачу к двум существующим. То, что оно иммутабельно по умолчанию — совершенно ничего не значит, такие "иммутабельные" классы и сейчас делать можно.
Впрочем, реакция сектантов совершенно предсказуема, я об этом сразу и написал.
Здравствуйте, AlexRK, Вы писали: ARK>Какая еще "чушь", "тактика", але.
Действительно, какая тут тактика. Прошу прощения, я ошибся. Тут только метание говна, без тактики и стратегии. ARK>Негрософт сделал очередное мутабельное говно, впридачу к двум существующим. То, что оно иммутабельно по умолчанию — совершенно ничего не значит, такие "иммутабельные" классы и сейчас делать можно. ARK>Впрочем, реакция сектантов совершенно предсказуема, я об этом сразу и написал.
Мне всё же интересно — можете проиллюстрировать "проблему" кодом?
Ну, то есть полезность рекордов проиллюстрировать кодом легко — например, можно тупо нарисовать иерархию классов System.Linq.Expression и сравнить объём кода на "и сейчас делать можно" с объёмом кода на рекордах.
Покажите, какой именно сценарий вы хотите написать на рекордах, и вам не удаётся из-за того, что это "очередное мутабельное говно".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
ARK>>Какая еще "чушь", "тактика", але. S>Действительно, какая тут тактика. Прошу прощения, я ошибся. Тут только метание говна, без тактики и стратегии.
Не, ну необоснованно обвинять меня в какой-то там невнимательности — это тоже тактика так себе. В статье по ссылке вообще мутабельность никак не педалируется. Чтобы понять, что рекорды мутабельные, надо как раз наоборот быть внимательным.
S>Мне всё же интересно — можете проиллюстрировать "проблему" кодом? S>Ну, то есть полезность рекордов проиллюстрировать кодом легко — например, можно тупо нарисовать иерархию классов System.Linq.Expression и сравнить объём кода на "и сейчас делать можно" с объёмом кода на рекордах.
Не вижу иллюстрации.
S>Покажите, какой именно сценарий вы хотите написать на рекордах, и вам не удаётся из-за того, что это "очередное мутабельное говно".
Проблема не в том, удается что-то сделать или не удается. Проблема в том, что негрософт поощряет индусский стиль программирования. Чем плохо мутабельное состояние, вроде довольно известная вещь. И я не понимаю, зачем это делать там, где можно не делать. Тем более, что то же самое уже давно есть, только на два символа больше написать надо.
Здравствуйте, AlexRK, Вы писали:
ARK>Какая еще "чушь", "тактика", але. Негрософт сделал очередное мутабельное говно, впридачу к двум существующим. То, что оно иммутабельно по умолчанию — совершенно ничего не значит, такие "иммутабельные" классы и сейчас делать можно.
Ты о чем вообще? Оператор with не делает рекорды мутабельными.
Здравствуйте, AlexRK, Вы писали:
ARK>Не, ну необоснованно обвинять меня в какой-то там невнимательности — это тоже тактика так себе. В статье по ссылке вообще мутабельность никак не педалируется. Чтобы понять, что рекорды мутабельные, надо как раз наоборот быть внимательным.
ARK>Не вижу иллюстрации.
Ну, если вы так настаиваете, вот примерно как выглядит типичный иммутабельный класс на C# 8.0:
public sealed class Color: IEquatable<Color>
{
public byte R { get; }
public byte G { get; }
public byte B { get; }
public Color(byte r, byte g, byte b) => (R, G, B) = (r, g, b);
public void Deconstruct(out byte r, out byte g, out byte b) => (r, g, b) = (R, G, B);
public override int GetHashCode()
{
return R << 24 | G << 16 | B << 8;
}
public override bool Equals(object obj)
{
if (this == null)
return obj == null;
return obj is Color c && Equals(c);
}
public bool Equals([AllowNull] Color other)
{
if (this == null)
return other == null;
if (other == null)
return false;
return R == other.R && G == other.G && B == other.B;
}
public Color Clone() => new Color(R, G, B);
}
Вот как это же записывается на C# 9:
public sealed record Color(byte R, byte G, byte B);
Преимущества, имхо, очевидны. И это мы ещё не полезли в наследование — там код быстро становится ещё кучерявее. S>>Покажите, какой именно сценарий вы хотите написать на рекордах, и вам не удаётся из-за того, что это "очередное мутабельное говно". ARK>Проблема не в том, удается что-то сделать или не удается.
Именго к этому и сводятся все проблемы. ARK>Проблема в том, что негрософт поощряет индусский стиль программирования.
Стиль программирования бывает плохим не сам по себе, а потому что приносит измеримые проблемы. Любая "плохая" практика либо не является плохой, либо легко ткнуть в случай, где она приносит явное зло. ARK>Чем плохо мутабельное состояние, вроде довольно известная вещь. И я не понимаю, зачем это делать там, где можно не делать. Тем более, что то же самое уже давно есть, только на два символа больше написать надо.
Давайте конкретнее. Где вы хотите написать пару символов так, чтобы улучшить рекорды?
Покажите пример, где стреляет мутабельность рекордов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Преимущества, имхо, очевидны.
Если код состоит только из определений иммутабельных классов — то да.
ARK>>Проблема в том, что негрософт поощряет индусский стиль программирования. S>Стиль программирования бывает плохим не сам по себе, а потому что приносит измеримые проблемы. Любая "плохая" практика либо не является плохой, либо легко ткнуть в случай, где она приносит явное зло.
Как выглядит тычок в случай, например, нуллабельной ссылки?
Я по аналогии сделаю такую же ошибку с мутабельным состоянием.
ARK>>Чем плохо мутабельное состояние, вроде довольно известная вещь. И я не понимаю, зачем это делать там, где можно не делать. Тем более, что то же самое уже давно есть, только на два символа больше написать надо. S>Давайте конкретнее. Где вы хотите написать пару символов так, чтобы улучшить рекорды?
Я не хочу пару символов в рекордах, я бы предпочел их видеть иммутабельными (безусловно).
А то, что сейчас сделано — навесили бы атрибут для кодогенерации какой-нибудь.
S>Покажите пример, где стреляет мутабельность рекордов.
Там же, где любое изменяемое состояние. По этой причине его обычно минимизируют, не так ли?
Здравствуйте, AlexRK, Вы писали:
ARK>Проблема не в том, удается что-то сделать или не удается. Проблема в том, что негрософт поощряет индусский стиль программирования. Чем плохо мутабельное состояние, вроде довольно известная вещь. И я не понимаю, зачем это делать там, где можно не делать. Тем более, что то же самое уже давно есть, только на два символа больше написать надо.
То там кроме выделенного есть сравнение классов и рекордов
using System;
using System.Linq;
using static System.Console;
var user = "Lion-O";
var password = "jaga";
var rememberMe = true;
LoginResourceRecord lrr1 = new(user, password, rememberMe);
var lrr2 = new LoginResourceRecord(user, password, rememberMe);
var lrc1 = new LoginResourceClass(user, password, rememberMe);
var lrc2 = new LoginResourceClass(user, password, rememberMe);
WriteLine($"Test record equality -- lrr1 == lrr2 : {lrr1 == lrr2}");
WriteLine($"Test class equality -- lrc1 == lrc2 : {lrc1 == lrc2}");
WriteLine($"Print lrr1 hash code -- lrr1.GetHashCode(): {lrr1.GetHashCode()}");
WriteLine($"Print lrr2 hash code -- lrr2.GetHashCode(): {lrr2.GetHashCode()}");
WriteLine($"Print lrc1 hash code -- lrc1.GetHashCode(): {lrc1.GetHashCode()}");
WriteLine($"Print lrc2 hash code -- lrc2.GetHashCode(): {lrc2.GetHashCode()}");
WriteLine($"{nameof(LoginResourceRecord)} implements IEquatable<T>: {lrr1 is IEquatable<LoginResourceRecord>} ");
WriteLine($"{nameof(LoginResourceClass)} implements IEquatable<T>: {lrr1 is IEquatable<LoginResourceClass>}");
WriteLine($"Print {nameof(LoginResourceRecord)}.ToString -- lrr1.ToString(): {lrr1.ToString()}");
WriteLine($"Print {nameof(LoginResourceClass)}.ToString -- lrc1.ToString(): {lrc1.ToString()}");
public record LoginResourceRecord(string Username, string Password, bool RememberMe);
public class LoginResourceClass
{
public LoginResourceClass(string username, string password, bool rememberMe)
{
Username = username;
Password = password;
RememberMe = rememberMe;
}
public string Username { get; init; }
public string Password { get; init; }
public bool RememberMe { get; init; }
}
view rawProgram.cs hosted with ❤ by GitHub
Note: You will notice that the LoginResource types end in Record and Class. That pattern is not the indication of a new naming pattern. They are only named that way so that there can be a record and class variant of the same type in the sample. Please don’t name your types that way.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, Ночной Смотрящий, Вы писали:
НС>>Ты о чем вообще? Оператор with не делает рекорды мутабельными.
ARK>"set" тоже не делает?
with создает и возвращает новый инстанс рекорда, исходный он не меняет
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Serginio1, Вы писали:
S>>Про рекорды
IT>Что-то я не вижу, деконструктор они туда прикрутили? https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9
The compiler produces a Deconstruct method for positional records. The Deconstruct method has parameters that match the names of all public properties in the record type. The Deconstruct method can be used to deconstruct the record into its component properties:
public record Person(string FirstName, string LastName);
var person = new Person("Bill", "Wagner");
var (first, last) = person;
Console.WriteLine(first);
Console.WriteLine(last);
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, AlexRK, Вы писали:
ARK>Как выглядит тычок в случай, например, нуллабельной ссылки?
Примерно так:
public class Customer
{
public string Name {get;set};
private Manager Manager {get;set};
public override string ToString()=>$"Customer {Name} is managed by {Manager.Name}";
}
...
public static Customer CreateCustomer(string name, int managerId) => new Customer() {Name = name; Manager = db.Managers.GetById(managerId)}
... // где-то далеко-далеко, в совсем другом модуле:
Console.WriteLine(this.customer.ToString()); // BANG! We wish you a happy debugging
ARK>Я по аналогии сделаю такую же ошибку с мутабельным состоянием.
Это всё трёп, пока нет кода. ARK>Я не хочу пару символов в рекордах, я бы предпочел их видеть иммутабельными (безусловно).
Предпочтения — фигня. Интересна практическая ценность. ARK>А то, что сейчас сделано — навесили бы атрибут для кодогенерации какой-нибудь.
Ну, примерно так и сделано. S>>Покажите пример, где стреляет мутабельность рекордов. ARK>Там же, где любое изменяемое состояние. По этой причине его обычно минимизируют, не так ли?
Вот я как раз вижу минимизацию изменяемого состояния в рекордах. И в упор не вижу эту великую, непреодолимую проблему, которая вам так мозолит глаз.
Если вы неспособны проиллюстрировать своё мнение кодом, то вы ошиблись не только форумом, но и сайтом.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
ARK>>Я по аналогии сделаю такую же ошибку с мутабельным состоянием. S>Это всё трёп, пока нет кода.
Мне лень придумывать код, в комментариях по ссылке уже привели пример:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
MyRecord record = new MyRecord(5);
MyRecord clone = record with {};
// check that we do have a clone
Console.WriteLine(Equals(record, clone)); // true
Console.WriteLine(ReferenceEquals(record, clone)); // falsevar hash = new HashSet();
hash.Add(record);
Console.WriteLine(hash.Contains(record)); // true
Console.WriteLine(hash.Contains(clone)); // true
record.Danger = true; // uh oh
Console.WriteLine(hash.Contains(record)); // false
Console.WriteLine(hash.Contains(clone)); // false
}
}
public record MyRecord(int Value)
{
public bool Danger { get; set; }
}
namespace System.Runtime.CompilerServices
{
internal class IsExternalInit { }
}
Ога?
ARK>>Я не хочу пару символов в рекордах, я бы предпочел их видеть иммутабельными (безусловно). S>Предпочтения — фигня. Интересна практическая ценность. ARK>>А то, что сейчас сделано — навесили бы атрибут для кодогенерации какой-нибудь. S>Ну, примерно так и сделано.
Примерно так. Только местами через зад.
S>>>Покажите пример, где стреляет мутабельность рекордов. ARK>>Там же, где любое изменяемое состояние. По этой причине его обычно минимизируют, не так ли? S>Вот я как раз вижу минимизацию изменяемого состояния в рекордах. И в упор не вижу эту великую, непреодолимую проблему, которая вам так мозолит глаз.
Не понимаю, нахрена делать мутабельность? Вот что это кому дает?
Я не против рекордов, я сразу это сказал. Но ложку говна в бочку меда микрософт все же добавил.
S>Если вы неспособны проиллюстрировать своё мнение кодом, то вы ошиблись не только форумом, но и сайтом.
Здравствуйте, Serginio1, Вы писали:
ARK>>Не понимаю, нахрена делать мутабельность? Вот что это кому дает? S>Может кому то и нужно раз они оставили такую возможность.
Уже есть классы и структуры с такой возможностью. Много писать? Ну добавь атрибут для кодогенерации, если это прям так важно ( в жабе вся подобная кодогенерация выполняется через IDE, всем пофиг). Но нет, мы сделаем еще один раз то же самое, но с перламутровыми пуговицами.
S>Но вопрос а зачем эту мутабельность использовать?
Ну я не буду, а другой будет. Да и вообще, так можно много про что сказать.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, Serginio1, Вы писали:
ARK>>>Не понимаю, нахрена делать мутабельность? Вот что это кому дает? S>>Может кому то и нужно раз они оставили такую возможность.
ARK>Уже есть классы и структуры с такой возможностью. Много писать? Ну добавь атрибут для кодогенерации, если это прям так важно ( в жабе вся подобная кодогенерация выполняется через IDE, всем пофиг). Но нет, мы сделаем еще один раз то же самое, но с перламутровыми пуговицами.
Не понимаю твоего возмущения. Да есть лазейка, но если ты её будешь использовать то ССЗС S>>Но вопрос а зачем эту мутабельность использовать?
ARK>Ну я не буду, а другой будет. Да и вообще, так можно много про что сказать.
А вот кому то она возможно и будет нужна. И он будет плеваться из-за того, что такой возможности нет.
Думаю они много времени потратили, что бы такую возможность оставить. Аналоги рекордов есть в куче языков.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> А вот кому то она возможно и будет нужна.
Вот этого я и не могу понять. Если такая возможность нужна — почему просто не взять обычный класс? Для генерации всяких вспомогательных методов вводить в язык фичи необязательно.
Было бы четкое разделение — вот потенциально изменяемый класс, вот гарантированно неизменяемый рекорд.
S>И он будет плеваться из-за того, что такой возможности нет.
Почему тогда, скажем, анонимные классы мутабельными не сделали? Может кто-то плюется, что их нельзя поменять.
Здравствуйте, AlexRK, Вы писали:
НС>>Ты о чем вообще? Оператор with не делает рекорды мутабельными. ARK>"set" тоже не делает?
set ты должен явно прописать в своем рекорде, сделать это намеренно. Ровно так же намеренно ты можешь использовать обычный класс, тоже мутабельный. Непонятно в чем тут криминал, и что бы дал абсолютный запрет на мутабельность рекордов.
Здравствуйте, Ночной Смотрящий, Вы писали:
ARK>>Было бы четкое разделение
НС>В чем профит?
Снижение когнитивной нагрузки. Видишь рекорд — все, ты сразу знаешь, что наломать с ним дров нельзя. Индус Вася не сможет туда завтра добавить мутабельное поле и у тебя где-то в глубине не сломается хеш-таблица, как в примере выше. Меньше обращаешь внимания на это.
Здравствуйте, AlexRK, Вы писали:
ARK>Снижение когнитивной нагрузки. Видишь рекорд — все, ты сразу знаешь, что наломать с ним дров нельзя.
Какой то сомнительный профит.
ARK> Индус Вася не сможет туда завтра добавить мутабельное поле и у тебя где-то в глубине не сломается хеш-таблица, как в примере выше. Меньше обращаешь внимания на это.
Если у тебя индусы-васи могут куда то чего то бездумно в твой код добавить — у тебя большие проблемы вне зависимости от свойств рекордов.
Здравствуйте, Ночной Смотрящий, Вы писали:
ARK>>Снижение когнитивной нагрузки. Видишь рекорд — все, ты сразу знаешь, что наломать с ним дров нельзя. НС>Какой то сомнительный профит.
А какой профит от того, что мутабельность оставили?
ARK>> Индус Вася не сможет туда завтра добавить мутабельное поле и у тебя где-то в глубине не сломается хеш-таблица, как в примере выше. Меньше обращаешь внимания на это. НС>Если у тебя индусы-васи могут куда то чего то бездумно в твой код добавить — у тебя большие проблемы вне зависимости от свойств рекордов.
Тогда и readonly поля не нужны. Ведь можно использовать обычные. Просто не пиши туда ничего лишнего.
Здравствуйте, AlexRK, Вы писали:
ARK>Ога?
Now we're talking.
То есть вы самостоятельно добавили set, и внезапно рекорд перестал себя вести так, как ведёт, я всё правильно понял?
А то, что язык и компилятор подталкивают вас писать public record MyRecord(int Value, bool Danger) — этого недостаточно?
Так-то мы знаем, что и string — нифига не immutable.
ARK>Не понимаю, нахрена делать мутабельность? Вот что это кому дает?
Например, это позволяет мне вносить в рекорды состояние, не влияющее на эквивалентность.
public Manager {
get
{
if (_manager == null)
{
_manager = DB.Instance.Managers.GetManagerById()
}
return _manager;
}
}
Полный запрет изменяемости обрубил бы эту возможность.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Serginio1, Вы писали:
S>>Про рекорды
IT>Что-то я не вижу, деконструктор они туда прикрутили?
Ага. Подозреваю, что можно несколько упростить код Linq.Expressions.Deconstruct.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: S>Спасибо, я ещё не привык
Копец, как это вслух-то читать?
Для ?! есть название — интерробанг.
А ??= как читать? Не произносить же кажный раз "null-coalescing assignment operator"...
manager what-what-assign from deebee dot instance dot managers dot get manager by id
?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, Serginio1, Вы писали:
S>> А вот кому то она возможно и будет нужна.
ARK>Вот этого я и не могу понять. Если такая возможность нужна — почему просто не взять обычный класс? Для генерации всяких вспомогательных методов вводить в язык фичи необязательно.
ARK>Было бы четкое разделение — вот потенциально изменяемый класс, вот гарантированно неизменяемый рекорд.
Не добавляй мутабельные поля вот и все.
S>>И он будет плеваться из-за того, что такой возможности нет.
ARK>Почему тогда, скажем, анонимные классы мутабельными не сделали? Может кто-то плюется, что их нельзя поменять.
Анонимные классы без рефлексии в методах не поиспользуешь. Особого смысла нет.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
ARK>>Было бы четкое разделение — вот потенциально изменяемый класс, вот гарантированно неизменяемый рекорд. S> Не добавляй мутабельные поля вот и все.
Если ты и автор и пользователь рекорда, то да.
Но представь такой случай: ты используешь рекорд Foo, определенный в некоторой библиотеке. Чтобы узнать, изменяемы ли его поля или нет, тебе нужно лезть в сорцы этой библиотеки, т.к. по public contract этого не видно. Даже если в текущей версии библиотеки он неизменяем, ничто не мешает в следующей ее версии сделать некоторые поля рекорда изменяемыми. А все потому, что (не)изменяемость рекорда это деталь его реализации, никак не выраженная в его контракте.
Здравствуйте, AlexRK, Вы писали:
ARK>>>Снижение когнитивной нагрузки. Видишь рекорд — все, ты сразу знаешь, что наломать с ним дров нельзя. НС>>Какой то сомнительный профит. ARK>А какой профит от того, что мутабельность оставили?
В том что сахар будет доступен и там, где дизайн изначально мутабельный. К примеру, не все десериализаторы умеют десериализовать в конструктор, к примеру тот же ConfigurationBinder.
ARK>>> Индус Вася не сможет туда завтра добавить мутабельное поле и у тебя где-то в глубине не сломается хеш-таблица, как в примере выше. Меньше обращаешь внимания на это. НС>>Если у тебя индусы-васи могут куда то чего то бездумно в твой код добавить — у тебя большие проблемы вне зависимости от свойств рекордов. ARK>Тогда и readonly поля не нужны. Ведь можно использовать обычные. Просто не пиши туда ничего лишнего.
Доказательство по аналогии? Еще раз — если ты не напишешь явно, что свойство мутабельное — компилятор тебе его поменять не даст. Ровно тоже делают и readonly. Ты же предлагаешь вообще все поля безусловно сделать readonly чтобы индусы вдруг его не поменяли.
Здравствуйте, Serginio1, Вы писали:
ARK>>Почему тогда, скажем, анонимные классы мутабельными не сделали? Может кто-то плюется, что их нельзя поменять. S> Анонимные классы без рефлексии в методах не поиспользуешь.
Здравствуйте, artelk, Вы писали:
A>Но представь такой случай: ты используешь рекорд Foo, определенный в некоторой библиотеке. Чтобы узнать, изменяемы ли его поля или нет, тебе нужно лезть в сорцы этой библиотеки, т.к. по public contract этого не видно. Даже если в текущей версии библиотеки он неизменяем, ничто не мешает в следующей ее версии сделать некоторые поля рекорда изменяемыми.
И при чем тут рекорды? Ровно то же ты можешь получить и с классами.
A> А все потому, что (не)изменяемость рекорда это деталь его реализации,
Нет. Это деталь его публичного контракта.
A> никак не выраженная в его контракте.
Здрасте, приехали. Наличие set вместо init это контракт и есть.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Serginio1, Вы писали:
ARK>>>Почему тогда, скажем, анонимные классы мутабельными не сделали? Может кто-то плюется, что их нельзя поменять. S>> Анонимные классы без рефлексии в методах не поиспользуешь.
НС>Вот это новость.
Имелось ввиду при передачи объекта анонимного класса в параметрах.
Поэтому и нет особой нужды в мутабельности
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinclair, Вы писали:
S>То есть вы самостоятельно добавили set, и внезапно рекорд перестал себя вести так, как ведёт, я всё правильно понял?
Индус Вася добавил set в общий рекорд для того, чтобы ему в своем кусочке кода было удобнее.
Смотрит — вроде все работает.
Да, да, я знаю, что такого быть не может, и что вся функциональность, внешняя и внутренняя, всегда на 100% покрыта всеми возможными тестами.
S>А то, что язык и компилятор подталкивают вас писать public record MyRecord(int Value, bool Danger) — этого недостаточно? S>Например, это позволяет мне вносить в рекорды состояние, не влияющее на эквивалентность. S>Полный запрет изменяемости обрубил бы эту возможность.
В тех полутора местах, где такое нужно, можно просто использовать обычный класс.
Здравствуйте, Ночной Смотрящий, Вы писали:
ARK>>А какой профит от того, что мутабельность оставили? НС>В том что сахар будет доступен и там, где дизайн изначально мутабельный. К примеру, не все десериализаторы умеют десериализовать в конструктор, к примеру тот же ConfigurationBinder.
Чем плох обычный класс в таком месте?
ARK>>Тогда и readonly поля не нужны. Ведь можно использовать обычные. Просто не пиши туда ничего лишнего. НС>Доказательство по аналогии? Еще раз — если ты не напишешь явно, что свойство мутабельное — компилятор тебе его поменять не даст. Ровно тоже делают и readonly. Ты же предлагаешь вообще все поля безусловно сделать readonly чтобы индусы вдруг его не поменяли.
readonly поле — это гарантия на уровне поля.
record мог бы быть гарантией на уровне композитного объекта с именованным доступом.
Здравствуйте, AlexRK, Вы писали:
НС>>В том что сахар будет доступен и там, где дизайн изначально мутабельный. К примеру, не все десериализаторы умеют десериализовать в конструктор, к примеру тот же ConfigurationBinder. ARK>Чем плох обычный класс в таком месте?
Тем же, чем и в любом другом месте, в котором планируется использовать рекорды.
НС>>Доказательство по аналогии? Еще раз — если ты не напишешь явно, что свойство мутабельное — компилятор тебе его поменять не даст. Ровно тоже делают и readonly. Ты же предлагаешь вообще все поля безусловно сделать readonly чтобы индусы вдруг его не поменяли. ARK>readonly поле — это гарантия на уровне поля. ARK>record мог бы быть гарантией на уровне композитного объекта с именованным доступом.
Вопрос в том насколько такая гарантия нужна. Тем более что просто запрета тут недостаточно, ибо тогда твой неконтролируемый Вася-индус может с таким же успехом поменять рекорд на класс и таки все сломать. Соотв. как минимум нужен еще и констрейн на рекорд. Но в таком случае констрейн лучше сделать не на рекорд, а вообще на иммутабельный класс, неважно, рекорд он или обычный. И в таком разрезе принудительная иммутабильность рекордов опять же лишена практического смысла.
ARK>А что щас сделали, хрен знает.
Сделали удобный инструмент, позволяющий не писать кучу бойлерплейта в ряде ситуаций.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, artelk, Вы писали:
A>>Но представь такой случай: ты используешь рекорд Foo, определенный в некоторой библиотеке. Чтобы узнать, изменяемы ли его поля или нет, тебе нужно лезть в сорцы этой библиотеки, т.к. по public contract этого не видно. Даже если в текущей версии библиотеки он неизменяем, ничто не мешает в следующей ее версии сделать некоторые поля рекорда изменяемыми.
НС>И при чем тут рекорды? Ровно то же ты можешь получить и с классами.
Вот именно, рекорды это просто классы с автогенерированным кодом. Для пользователя рекорда неважно, сгенерирован ли его код или написан вручную. А хотелось бы, чтоб у рекордов была еще какая-то семантика относительно пользователя рекорда.
A>> А все потому, что (не)изменяемость рекорда это деталь его реализации,
НС>Нет. Это деталь его публичного контракта.
A>> никак не выраженная в его контракте.
НС>Здрасте, приехали. Наличие set вместо init это контракт и есть.
Ок, отчасти убедил, изменяемость можно выразить
С неизменяемостью недоработка.
Хотелось бы, например, вот чего: после того как автор рекорда поменял init на set, при компиляции моего кода компилятор бы сказал "эй, мы тут закладывались на неизменяемость, но теперь она не гарантируется".
А лучше бы вообще запретили set.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, AlexRK, Вы писали: ARK>>А какой профит от того, что мутабельность оставили? НС>В том что сахар будет доступен и там, где дизайн изначально мутабельный. К примеру, не все десериализаторы умеют десериализовать в конструктор, к примеру тот же ConfigurationBinder.
На сколько понимаю, init — это compiletime сущность, для рантайма он работает как обычный set, нет?
Десериализаторы используют рефлексию, они и не заметят, что там init вместо set.
Здравствуйте, artelk, Вы писали:
НС>>И при чем тут рекорды? Ровно то же ты можешь получить и с классами. A>Вот именно, рекорды это просто классы с автогенерированным кодом.
Конечно. Разве кто то обещал больше?
A> А хотелось бы, чтоб у рекордов была еще какая-то семантика относительно пользователя рекорда.
Хотелки подкреплены реальными кейсами?
A>Хотелось бы, например, вот чего: после того как автор рекорда поменял init на set, при компиляции моего кода компилятор бы сказал "эй, мы тут закладывались на неизменяемость, но теперь она не гарантируется".
Это отдельный большой вопрос, не имеющий никакого отношения к рекордам.
A>А лучше бы вообще запретили set.
Здравствуйте, AlexRK, Вы писали:
ARK>Индус Вася добавил set в общий рекорд для того, чтобы ему в своем кусочке кода было удобнее. ARK>Смотрит — вроде все работает. ARK>Да, да, я знаю, что такого быть не может, и что вся функциональность, внешняя и внутренняя, всегда на 100% покрыта всеми возможными тестами.
Индус Вася заменил рекорд на класс, потому что AlexRK запретил set в рекордах.
Да, да, я знаю, такого быть не может, потому что Вася такой специальный хитрый индус, который set воткнуть в чужом коде и пройти с таким патчем ревью может, а заменить record на class уже нет.
P.S. Если в твоем проекте куча индусов, плохое покрытие тестами и нет никакого codereview, то ты можешь буквально за пару часиков написать свой простенький анализатор, который таки запретит set в рекордах.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Serginio1, Вы писали:
НС>>>В мутабельность нет нужды по другой причине. S>>По какой?
НС>По той что анонимные типы были придуманы для конкретной задачи ad-hoc типов в запросах, и там мутабельность ен нужна.
Ну анонимные типы используются не только в запросах.
Например для для сериализации в Json и прочего.
И очень похожи на рекорды
var user = new { Name = "Tom", Age = 34 };
В общем то согласен, что и для рекордов не нужно делать мутабельные поля.
Для большинства задач эта мутабельность и не нужна.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, artelk, Вы писали:
A>>На сколько понимаю, init — это compiletime сущность, для рантайма он работает как обычный set, нет? НС>Нет. init добавляет модификатор readonly на backing field
А мог бы и не добавлять, например.
A>>Десериализаторы используют рефлексию, они и не заметят, что там init вместо set. НС>Нормальные десериализаторы используют кодогенерацию и она совершенно точно обломается на readonly полях.
Т.е. когда в язык вводили init-only свойства вопрос "а как же десериализаторы" отметался, а когда вводили рекорды он вдруг стал актуальным, так что-ли?
Напомню: обе фичи добавляются в язык одновременно.
Если уж начали делать кодогенерацию, то проблему десериализаторов (и более общую) могли бы, например, решить классическим образом: сгенерировать вложенный класс Builder, в котором все свойства имеют сеттер. Десериализацию будет нужно делать в него и потом из него получать инстанс рекорда. Популярные десериализаторы уже в следующих версиях позволят пропускать этот шаг.
Другой вариант: не ставить readonly на backing field.
П.С. Кстати, на сколько помню SRE игнорирует readonly на полях (не проверял на новых версиях .Net.)
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, artelk, Вы писали:
НС>>>И при чем тут рекорды? Ровно то же ты можешь получить и с классами. A>>Вот именно, рекорды это просто классы с автогенерированным кодом. НС>Конечно. Разве кто то обещал больше?
Хотелось бы, раз уж вводят новую сущность в язык.
A>> А хотелось бы, чтоб у рекордов была еще какая-то семантика относительно пользователя рекорда. НС>Хотелки подкреплены реальными кейсами?
Да, и ты их знаешь.
A>>Хотелось бы, например, вот чего: после того как автор рекорда поменял init на set, при компиляции моего кода компилятор бы сказал "эй, мы тут закладывались на неизменяемость, но теперь она не гарантируется". НС>Это отдельный большой вопрос, не имеющий никакого отношения к рекордам.
Этот большой вопрос можно было бы элементарно решить. При этом у рекордов появился бы своя семантика и вопрос "нафига вводить новое понятие в язык" имел бы ответ.
A>>А лучше бы вообще запретили set. НС>Лыко-мочало ...
Foo bar...
Здравствуйте, AlexRK, Вы писали:
ARK>>>Было бы четкое разделение НС>>В чем профит?
ARK>Снижение когнитивной нагрузки. Видишь рекорд — все, ты сразу знаешь, что наломать с ним дров нельзя. Индус Вася не сможет туда завтра добавить мутабельное поле и у тебя где-то в глубине не сломается хеш-таблица, как в примере выше. Меньше обращаешь внимания на это.
В этом плане, еще более скользкая конструкция — "in" параметры. В этом примере, если написали "in", чтобы сэкономить на одном клонировании структуры(а больше и незачем его использовать), то вместо этого получаем 1 млрд клонирований.
Достаточно не заметить readonly. Или кто-то удалит readonly, то потом уже этот баг никогда не найдут.
Назвали это "защитная копия"... Тут должна быть строго ошибка компилятора(ну хотя бы warning), т.к. если создаются "защитные копии", то in вообще уже не нужен — без него хоть будет только одно клонирование.
А лучше бы — компилятор создавал только одну защитную копию, оно будет работать как — неявно убрали "in", если есть доступ к не readonly property.
Из-за этого хочется вообще никогда не использовать "in". Лучше "ref" — он не накосячит с защитными копиями.
struct S
{
public S(int i) { Prop = i; }
public/*readonly*/int Prop { get; }
}
static int Func(in S p)
{
int sum = 0;
for (int i = 0; i < 1000_000_000; i++)
sum = p.Prop;
return sum;
}
S_S>struct S
S_S>{
S_S> public S(int i) { Prop = i; }
S_S> public/*readonly*/int Prop { get; }
S_S>}
S_S>static int Func(in S p)
S_S>{
S_S> int sum = 0;
S_S> for (int i = 0; i < 1000_000_000; i++)
S_S> sum = p.Prop;
S_S> return sum;
S_S>}
S_S>
На заметку, в твоем примере, что с readonly что без, не будет никакого клонирования, так как автосвойство и так получает такой модификатор
Здравствуйте, Qbit86, Вы писали:
S>>Копец, как это вслух-то читать? S>>Не произносить же кажный раз "null-coalescing assignment operator"...
Q>Я бы читал: «Элвис-присваивание».
Здравствуйте, AlexRK, Вы писали:
ARK>Индус Вася добавил set в общий рекорд для того, чтобы ему в своем кусочке кода было удобнее. ARK>Смотрит — вроде все работает. ARK>Да, да, я знаю, что такого быть не может, и что вся функциональность, внешняя и внутренняя, всегда на 100% покрыта всеми возможными тестами.
Как уже было справедливо замечено, этот ваш Вася наломает дров в проекте безо всяких рекордов. Что ему помешает заменить record на class?
Более того — с таким подходом к решению проблем он у вас запросто поменяет значение строки через reflection, и взорвётся вообще нахрен всё.
Задача языка, по большому счёту, это проектировать pit of success — чтобы написание хорошего кода было комфортным.
Сделать нормальную иммутабельность на классах — это закат солнца вручную, и большое количество boilerplate, в котором не очень легко отловить ошибку.
Сделать нормальную иммутабельность на рекордах — очень легко. Делать рекорды mutable требует прямо таки сознательного усилия и дополнительного кода — то есть хороший код писать проще, чем плохой.
Пока что всё, что я бы улучшил (если это ещё не сделано) — это добавил compiler warning для свойств с сеттерами.
ARK>В тех полутора местах, где такое нужно, можно просто использовать обычный класс.
Это как раз бы означало, что мелкое изменение сразу требует от меня написать в разы больше кода. Кривая инвестиций должна быть пологой, а не так, что "вот у вас автоматика в одну строку, а если не нравится один байт — переписывайте в 1500 строк".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S_S>>struct S
S_S>>{
S_S>> public S(int i) { Prop = i; }
S_S>> public/*readonly*/int Prop { get; }
S_S>>}
S_S>>static int Func(in S p)
S_S>>{
S_S>> int sum = 0;
S_S>> for (int i = 0; i < 1000_000_000; i++)
S_S>> sum = p.Prop;
S_S>> return sum;
S_S>>}
S_S>>
R>На заметку, в твоем примере, что с readonly что без, не будет никакого клонирования, так как автосвойство и так получает такой модификатор
вот это и есть ужос:
1) Почему тогда у меня одинаково тормозят для "in" варианты и с автосвойствами и ручными.
2) Если у тебя C# 8, и по другому работает. А у меня C# 7.3, так работает (readonly только на struct можно ставить). В любом случае так не должно быть — ненадежная фича.
3) Понятно, что ничего не упадет. Но "in" ввели для экономии на клонированиях. Если вместо этого в 1 млрд больше клонирований, то это уже можно считать багом.
Здравствуйте, artelk, Вы писали:
A>>>На сколько понимаю, init — это compiletime сущность, для рантайма он работает как обычный set, нет? НС>>Нет. init добавляет модификатор readonly на backing field A>А мог бы и не добавлять, например.
Но он добавляет. К чему ты это написал?
A>>>Десериализаторы используют рефлексию, они и не заметят, что там init вместо set. НС>>Нормальные десериализаторы используют кодогенерацию и она совершенно точно обломается на readonly полях. A>Т.е. когда в язык вводили init-only свойства вопрос "а как же десериализаторы" отметался, а когда вводили рекорды он вдруг стал актуальным, так что-ли?
Ничего не понял. Какое отношение к добавлению этой механики имеет наличие возможности делать set поля в рекордах?
A>Напомню: обе фичи добавляются в язык одновременно.
И?
A>Если уж начали делать кодогенерацию, то проблему десериализаторов (и более общую) могли бы, например, решить классическим образом: сгенерировать вложенный класс Builder, в котором все свойства имеют сеттер. Десериализацию будет нужно делать в него и потом из него получать инстанс рекорда.
И все ради того что кому то не понравилась возможность сделать мутабельным рекорды?
A>Другой вариант: не ставить readonly на backing field.
Ну пипец просто. Ради того чтобы не дать сделать мутабельность надо добавить возможность мутабельности любым свойствам безусловно. Абсурд на марше.
Здравствуйте, artelk, Вы писали:
A>>>Вот именно, рекорды это просто классы с автогенерированным кодом. НС>>Конечно. Разве кто то обещал больше? A>Хотелось бы, раз уж вводят новую сущность в язык.
Это отдельный вопрос. Зачем его к рекордам цеплять и делать тем самым убогим — непонятно.
A>>> А хотелось бы, чтоб у рекордов была еще какая-то семантика относительно пользователя рекорда. НС>>Хотелки подкреплены реальными кейсами? A>Да, и ты их знаешь.
Пока что ничего убедительного в этом топике я не увидел.
A>>>Хотелось бы, например, вот чего: после того как автор рекорда поменял init на set, при компиляции моего кода компилятор бы сказал "эй, мы тут закладывались на неизменяемость, но теперь она не гарантируется". НС>>Это отдельный большой вопрос, не имеющий никакого отношения к рекордам. A>Этот большой вопрос можно было бы элементарно решить.
Разве что языком.
A> При этом у рекордов появился бы своя семантика и вопрос "нафига вводить новое понятие в язык" имел бы ответ.
Если просто запретить set — полученное ничего не стоит и проблему не решает.
Здравствуйте, Sinclair, Вы писали:
Q>>Я бы читал: «Элвис-присваивание». S>Эмм, почему?
Родственные операторы типа ?? и ?. в C#, Kotlin и им подобных называются (традиционно/неформально) Elvis operator: «It resembles an emoticon of Elvis Presley with his quiff.»
Здравствуйте, Serginio1, Вы писали:
НС>>По той что анонимные типы были придуманы для конкретной задачи ad-hoc типов в запросах, и там мутабельность ен нужна. S>Ну анонимные типы используются не только в запросах.
Но придумали их именно для этого, и при разработке их дизайна руководствовались именно этим.
S>Для большинства задач эта мутабельность и не нужна.
Не нужна — не делай, никто не заставляет. Аргумент про таинственный Васек вредителей мягко говоря слабенький
Здравствуйте, Silver_S, Вы писали:
S_S>2) Если у тебя C# 8, и по другому работает. А у меня предыдущая версия, так работает (readonly только на struct можно ставить). В любом случае так не должно быть — ненадежная фича.
Просчитались, когда релизили 7 версию, потом исправились. Такие же защитные копии будут создаваться, например, и для ReadOnlySpan и на foreach, и в списках, да везде где есть возврат или прием аргументов толстых структур, если не озаботиться заранее, так что не только в in засада, здесь нужен соответствующий анализ и оптимизации от джита, способный все это устранить (кое-какие работы ведутся по устранению лишних клонирований, частично кое-что уже внедрено в современный Core). Хотя многое можно было бы сделать и на стороне рослина.
Здравствуйте, rameel, Вы писали:
S_S>>2) Если у тебя C# 8, и по другому работает. А у меня предыдущая версия, так работает (readonly только на struct можно ставить). В любом случае так не должно быть — ненадежная фича.
R>Просчитались, когда релизили 7 версию, потом исправились. Такие же защитные копии будут создаваться, например, и для ReadOnlySpan и на foreach, и в списках, да везде где есть возврат или прием аргументов толстых структур, если не озаботиться заранее, так что не только в in засада, здесь нужен соответствующий анализ и оптимизации от джита, способный все это устранить (кое-какие работы ведутся по устранению лишних клонирований, частично кое-что уже внедрено в современный Core). Хотя многое можно было бы сделать и на стороне рослина.
Защитную копию они могли создавать только одну, и все вызовы делать к ней. Поведение будет другое — изменения будут в ней накапливаться. Ну и нормально.
То же самое что вызов без "in" — тоже одна копия, в которой все накапливается. Сомнительно что у кого-то такое по задумке в коде — предполагает что, на каждое обращение создается копия (что изменения не накапливаются).
А в таком виде я бы не рискнул "in" использовать. Проще для "ref" вручную проверить — нет ли модификаций.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>По той что анонимные типы были придуманы для конкретной задачи ad-hoc типов в запросах, и там мутабельность ен нужна.
А почему не нужна-то? Кто это сказал?
Очень даже бывает нужна, какой-нибудь флаг выставить в процессе обработки.
Как ты там говоришь, "сделали анонимные типы тем самым убогими", верно?
Здравствуйте, Silver_S, Вы писали:
S_S>Защитную копию они могли создавать только одну, и все вызовы делать к ней. Поведение будет другое — изменения будут в ней накапливаться. Ну и нормально.
В случае с in — да, но это к команде рослина, можно issue завести.
Здравствуйте, Silver_S, Вы писали:
S_S>То же самое что вызов без "in" — тоже одна копия, в которой все накапливается. Сомнительно что у кого-то такое по задумке в коде — предполагает что, на каждое обращение создается копия (что изменения не накапливаются).
А по поводу кода, странно, что программист in втыкает, зная, что будет копия создаваться in — он для ридонли структур или ридонли-мемберов
Здравствуйте, AlexRK, Вы писали:
НС>>По той что анонимные типы были придуманы для конкретной задачи ad-hoc типов в запросах, и там мутабельность ен нужна. ARK>А почему не нужна-то? Кто это сказал?
На тот момент в той задаче — была не нужна. В отличие от рекордов, которые снаружи совершенно обычный класс, анонимные типы штука весьма специфичная.
ARK>Очень даже бывает нужна, какой-нибудь флаг выставить в процессе обработки.
Заведи новый тип, в который включи старый отдельным свойством. let примерно так и работает
ARK>Как ты там говоришь, "сделали анонимные типы тем самым убогими", верно?
Не надо приписывать мне того, чего я не говорил, некрасиво.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>>>По той что анонимные типы были придуманы для конкретной задачи ad-hoc типов в запросах, и там мутабельность ен нужна. ARK>>А почему не нужна-то? Кто это сказал? НС>На тот момент в той задаче — была не нужна. В отличие от рекордов, которые снаружи совершенно обычный класс, анонимные типы штука весьма специфичная.
Специфичная, но не в контексте потребности мутабельности.
ARK>>Очень даже бывает нужна, какой-нибудь флаг выставить в процессе обработки. НС>Заведи новый тип, в который включи старый отдельным свойством. let примерно так и работает
Ясное дело. Вместо изменения делается новый объект. Только вот для этого надо написать гораздо больше кода. Хммм, что-то мне это напоминает, уж не рассуждения ли о необходимости мутабельных полей в рекордах... Да не, бред какой-то.
ARK>>Как ты там говоришь, "сделали анонимные типы тем самым убогими", верно? НС>Не надо приписывать мне того, чего я не говорил, некрасиво.
Ну, раз ты считаешь рекорды без возможности мутации "убогими", то и анонимные типы ровно в той же степени "убоги".
И там, и там можно найти случаи, когда мутабельность кому-то нужна. И там, и там без нее легко можно обойтись. Твои рассуждения в равной степени применимы в обоих случаях.
Здравствуйте, AlexRK, Вы писали:
НС>>На тот момент в той задаче — была не нужна. В отличие от рекордов, которые снаружи совершенно обычный класс, анонимные типы штука весьма специфичная. ARK>Специфичная, но не в контексте потребности мутабельности.
Возможно. К анонимным типам вообще много претензий.
ARK>>>Очень даже бывает нужна, какой-нибудь флаг выставить в процессе обработки. НС>>Заведи новый тип, в который включи старый отдельным свойством. let примерно так и работает ARK>Ясное дело. Вместо изменения делается новый объект. Только вот для этого надо написать гораздо больше кода.
Если используешь query syntax — не особо.
ARK> Хммм, что-то мне это напоминает, уж не рассуждения ли о необходимости мутабельных полей в рекордах... Да не, бред какой-то.
Тебя не поймешь. Анонимные типы иммутабельны — плохо. Сделали рекордам возможность быть мутабельными — опять плохо.
ARK>Ну, раз ты считаешь рекорды без возможности мутации "убогими", то и анонимные типы ровно в той же степени "убоги".
Нет. Потому что у анонимных типов намного уже сфера применения. А рекорды это ровно те же классы в плане применимости, просто синтаксический сахар над ними, там мутабельность нужна намного сильнее.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Тебя не поймешь. Анонимные типы иммутабельны — плохо. Сделали рекордам возможность быть мутабельными — опять плохо.
Я за максимальную иммутабельность везде. Про мутабельность это я провел параллель — если стоять на позиции "мутабельность нужна", то надо равным образом и про анонимные типы говорить.
ARK>>Ну, раз ты считаешь рекорды без возможности мутации "убогими", то и анонимные типы ровно в той же степени "убоги". НС>Нет. Потому что у анонимных типов намного уже сфера применения. А рекорды это ровно те же классы в плане применимости, просто синтаксический сахар над ними, там мутабельность нужна намного сильнее.
Будет весело, если они в RC2 мутабельность эту уберут.
Здравствуйте, AlexRK, Вы писали:
НС>>Тебя не поймешь. Анонимные типы иммутабельны — плохо. Сделали рекордам возможность быть мутабельными — опять плохо. ARK>Я за максимальную иммутабельность везде.
Это я уже понял. Но подход с мутабельными классами тоже имеет право на жизнь. И не дело проектировать мейнстрим язык исходя только из одной точки зрения на проблему.
ARK> Про мутабельность это я провел параллель — если стоять на позиции "мутабельность нужна", то надо равным образом и про анонимные типы говорить.
Я не понимаю твоего черно/белого подхода. Я тебе привел один из примеров где могут очень пригодится мутабельные рекорды. Анонимные типы в этом случае, очевидно, применить нельзя — они годны для сериализации и абсолютно бесполезны для десериализации.
Рекорды прежде всего нацелены на представление всевозможных DTO. И дизайн мутабельных DTO вполне имеет право на жизнь даже если у нас правильный сериализатор, умеющий в конструкторы, и мы не поленились в сотнях классов эти конструкторы завести.
Анонимные типы при этом заточены под представление ad-hoc типов, требующихся при запросе к БД, чтобы не плодить ORM чудовищ, генерящих select *. Для этого применения мутабельность не нужна, потому что эти типы предназначены для типизации туплов по месту их получения, и их принципиально невозможно использовать в качестве DTO.
Поэтому доказательство по аналогии, чем ты упорно занимаешься уже которое сообщение, это на самом деле никакое не доказательство.
ARK>Будет весело, если они в RC2 мутабельность эту уберут.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, artelk, Вы писали:
A>>Другой вариант: не ставить readonly на backing field. НС>Ну пипец просто. Ради того чтобы не дать сделать мутабельность надо добавить возможность мутабельности любым свойствам безусловно. Абсурд на марше.
Что не понятно-то? От рефлексии readonly тебя не спасет, а без рефлексии backing field недоступен.
Так что вполне можно было сделать так, что десериализация в иммутабельный рекорд (и класс с init-only свойствами) работала бы без проблем.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, AlexRK, Вы писали:
НС>Рекорды прежде всего нацелены на представление всевозможных DTO. И дизайн мутабельных DTO вполне имеет право на жизнь даже если у нас правильный сериализатор, умеющий в конструкторы, и мы не поленились в сотнях классов эти конструкторы завести.
Вот с этим нельзя не согласиться Вручную реализовывать IEquatable, == и т.п. и потом поддерживать их — это проблема актуальна и для мутабельных классов.
Но она актуальна и для структур.
Лучше бы остановились на модификаторе data и разрешили его применять и к классам и к струкурам, чтобы можно было, например, делать "readonly data struct".
А термин record оставили бы на будущее для чего-то более существенного — для именованных туплов с именованными полями. Причем, чтобы выбор о том, использовать под капотом класс или readonly struct для них по дефолту делался компилятором (но можно было и явно задать).
Здравствуйте, artelk, Вы писали:
A>Так что вполне можно было сделать так, что десериализация в иммутабельный рекорд (и класс с init-only свойствами) работала бы без проблем.
Нельзя. Я тебе еще раз напомню, что нормальные десериализаторы не через рефлексию работают.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, artelk, Вы писали:
A>>Так что вполне можно было сделать так, что десериализация в иммутабельный рекорд (и класс с init-only свойствами) работала бы без проблем.
НС>Нельзя. Я тебе еще раз напомню, что нормальные десериализаторы не через рефлексию работают.
А FieldInfo/PropertyInfo они откуда берут? Для кодогеренации они тоже нужны.
Кстати, вот попробовал с SRE:
public delegate void ClassFieldSetter<in T, in TValue>(T target, TValue value) where T : class;
public static class FieldSetterCreator
{
public static ClassFieldSetter<T, TValue> CreateClassFieldSetter<T, TValue>(FieldInfo field)
where T : class
{
return CreateSetter<T, TValue, ClassFieldSetter<T, TValue>>(field);
}
private static TDelegate CreateSetter<T, TValue, TDelegate>(FieldInfo field)
{
return (TDelegate)(object)CreateSetter(field, typeof(T), typeof(TValue), typeof(TDelegate));
}
private static Delegate CreateSetter(FieldInfo field, Type instanceType, Type valueType, Type delegateType)
{
var setter = new DynamicMethod("", typeof(void), new[] { instanceType, valueType }, field.DeclaringType.Module, true);
var generator = setter.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
return setter.CreateDelegate(delegateType);
}
}
public class Foo
{
private readonly int x;
public int X => x;
}
class Program
{
static void Main()
{
var x = typeof(Foo).GetField("x", BindingFlags.Instance | BindingFlags.NonPublic);
var setX = FieldSetterCreator.CreateClassFieldSetter<Foo, int>(x);
var foo = new Foo();
setX(foo, 42);
Console.WriteLine(foo.X); // 42
}
}
Здравствуйте, artelk, Вы писали:
A>Что не понятно-то? От рефлексии readonly тебя не спасет, а без рефлексии backing field недоступен.
Запись в readonly backing field без рефлексии и unsafe:
using System;
using System.Runtime.InteropServices;
internal class Program
{
[StructLayout(LayoutKind.Explicit)]
private struct Union
{
[FieldOffset(0)] public Foo foo;
[FieldOffset(0)] public Bar bar;
}
private class Foo
{
public int Property { get; } = 42;
}
private class Bar
{
public int field;
}
private static void Main()
{
var foo = new Foo();
Console.WriteLine(foo.Property); // 42var union = default(Union);
union.foo = foo;
union.bar.field = -42;
Console.WriteLine(foo.Property); // -42
}
}
Здравствуйте, alexzzzz, Вы писали:
A>Здравствуйте, artelk, Вы писали:
A>>Что не понятно-то? От рефлексии readonly тебя не спасет, а без рефлексии backing field недоступен. A>Запись в readonly backing field без рефлексии и unsafe: A>
[StructLayout(LayoutKind.Explicit)]
A> private struct Union
A> {
A> [FieldOffset(0)] public Foo foo;
A> [FieldOffset(0)] public Bar bar;
A> }
//...
Ээ.. почитай контекст обсуждения. Я возражал на утверждение о том, что если не ставить readonly на backing field у init-only свойств, то они якобы станут мутабельными.
Ты показал трюк еще более низкоуровневый, чем рефлексия, в котором тебе известно о лэйауте класса Foo.
Это еще один аргумент в пользу того, что readonly на backing field у init-only свойств необязателен? Аргумент не очень.
Или это возражение мне? Ну ок, надо было написать как-то так: "От рефлексии readonly тебя не спасет, а без хотя бы рефлексии backing field недоступен."
Или так: "Backing field напрямую недоступен, чтобы до него добраться нужна как минимум рефлексия, но даже от рефлексии readonly тебя уже не спасет".
Здравствуйте, artelk, Вы писали:
A>>>Что не понятно-то? От рефлексии readonly тебя не спасет, а без рефлексии backing field недоступен. A>>Запись в readonly backing field без рефлексии и unsafe: A>>
[StructLayout(LayoutKind.Explicit)]
A>> private struct Union
A>> {
A>> [FieldOffset(0)] public Foo foo;
A>> [FieldOffset(0)] public Bar bar;
A>> }
A>//...
A>
A>Ээ.. почитай контекст обсуждения. Я возражал на утверждение о том, что если не ставить readonly на backing field у init-only свойств, то они якобы станут мутабельными. A>Ты показал трюк еще более низкоуровневый, чем рефлексия, в котором тебе известно о лэйауте класса Foo. A>Это еще один аргумент в пользу того, что readonly на backing field у init-only свойств необязателен? Аргумент не очень.
Нет, это просто рефлекторная реакция на «без рефлексии backing field недоступен»