switch expression кажется довольно простым и интуитивно понятным. Думаю, что небольшого примера может быть вполне достаточно для ознакомления:
// Этот код помещает в переменную result вычисленное значение.
// В зависимости от значения переменной operation, будет
// выполнено либо сложение, либо вычитание, либо деление.
var result = operation switch
{
"+" => a + b,
"-" => a - b,
"/" => a / b
};
Первое отличие со switch statement это, конечно, отсутствие ключевых слов case и break (или return). Второе, не столь очевидное, это то, что выражение после стрелки (=>) должно быть вычисляемым выражением. Как правая часть оператора присваивания.
Если вы хорошо знаете структуру switch, то вы можете спросить, как определить default? Тут надо заметить, что начиная с C#7 в switch можно использовать pattern matching (сопоставление с шаблоном). Поэтому вместо специального default блока используется шаблон, сопоставление с которым всегда вычисляется в true для любого значения. Это просто знак подчеркивания: _.
// Этот код помещает в переменную result вычисленное значение.
// В зависимости от значения переменной operation, будет
// выполнено либо сложение, либо вычитание, либо деление.
// В случае неподдерживаемой операции будет брошено исключение.
var result = operation switch
{
"+" => a + b,
"-" => a - b,
"/" => a / b,
_ => throw new NotSupportedException()
};
Поскольку сопоставление с шаблоном выполняется по очереди, не стоит ставить такую конструкцию первой. Никакие другие сопоставления выполнены не будут.
Другая неочевидная особенность switch expression — все выражения должны возвращать одинаковый тип. Например, только int. Или только string. В этом switch expression похож на тернарный оператор.
Остается только вопрос, как выполнить несколько операций в выражении? Красивого решения пока нет, но использование лямбда функций является наиболее близким к идеалу.
// Этот код помещает в переменную result вычисленное значение.
// В зависимости от значения переменной operation, будет
// выполнено либо сложение, либо вычитание, либо деление.
// В случае неподдерживаемой операции будет брошено исключение.
// Все поддерживаемые операции логируются.
var result = operation switch
{
"+" => ((Func<int>)(() => {
Log("сложение");
return a + b;
}))(),
"-" => ((Func<int>)(() => {
Log("вычитание");
return a - b;
}))(),
"/" => ((Func<int>)(() => {
Log("деление");
return a / b;
}))(),
_ => throw new NotSupportedException()
};
Почему же я считаю это изменение потенциально значительным?
Давайте посмотрим, как switch expression заменяет другие конструкции языка. Пусть есть следующий if:
var error = "";
if (fileSize > 1000) {
error = "file is too large"
} else {
processFile();
}
Аналогичный код с использованием switch expression:
var error = (fileSize > 1000) switch {
true => "file is too large",
_ => ((Func<string>)(() => {
processFile();
return "";
}))()
};
Поскольку switch относительно легко переводится в набор if-ов, заменить любой switch также не представляется сложным. Также тривиально заменяется и тернарный оператор.
Естественно, решение с явным использованием Func<> не назвать красивым. Но если эта техническая проблема будет решена, то switch expression будет использовать намного удобнее и, во многих случаях, предпочтительнее.
Если вам интересны возможности C#8, то вас, возможно заинтересуют скринкасты по nullable reference types и using declaration, которые можно найти на моем youtube канале.
Об авторе: Александр Неткачёв закончил Таврический Национальный Университет по специальности Информатика. Работает и проживает в Великобритании. На протяжении многих лет занимается профессиональной разработкой ПО, ведет персональный сайт [тут была ссылка на сайт автора, но поскольку TM считают, что наличие платного udemy курса и упоминание его на сайте автора — это платная услуга, то она убрана].
Комментарии (77)
AkshinM
14.02.2019 08:10+1Аналогичный код с использованием switch expression:
бредAlex_At_Net Автор
14.02.2019 13:44-1Так никто же не против. Вас же никто не заставляет использовать такой синтаксис, чего вы?
i8008
15.02.2019 00:46Вас же никто не заставляет использовать такой синтаксис, чего вы?
Увы, но на самом деле заставляют. Лично вы можете не использовать данный синтаксис, но другие будут его использовать, а вам придётся работать с чужим кодом.
Я так про С++ думал – я не буду злоупотреблять и упарываться метапрограммированием на шаблонах. Я и не «злоупотреблял». Но очень часто сталкивался с чужим кодом, автор которого хотел получить ачивку «гуру метапрограммирования: потому, что могу»
З.Ы. На всякий случай: это не упрек в ваш адрес, скорее негодование по поводу развития синтаксиса С#. ИМХО, в последних релизах они местами перебарщивают с синтаксическим сахаром
mayorovp
14.02.2019 08:59+1Если вы почему-то считаете использование IIFE нормальным — то для вас switch expression в языке существовал давным давно:
var result = ((Func<int>)(() => { switch(operation) { case "+": Log("сложение"); return a + b; case "-": Log("вычитание"); return a - b; default: throw new NotSupportedException(); } }))();
Alex_At_Net Автор
14.02.2019 13:45Я не считаю такое использование нормальным. Но я думаю, что у C# есть потенциал сделать подобное использование проще. Спасибо за пример.
TheShock
14.02.2019 19:17Если вы почему-то считаете использование IIFE нормальным — то для вас switch expression в языке существовал давным давно:
Я вот не понимаю, люди словно специально ищут способ сделать методы больше, усложнить их, когда это прекрасно разбивается на два метода и не нужно никаких IIFE.
ApeCoder
14.02.2019 09:00+1Остается только вопрос, как выполнить несколько операций в выражении
Оно для того и выражение, чтобы это была одна операция :).
kxl
14.02.2019 10:12В Scala, например, результат вычисления блока = результату последнего выражения в блоке.
Поэтому там пишется как:
val result = operation match { case "+" => Log("сложение"); a + b; case "-" => Log("вычитание"); a - b; case "/" => Log("деление"); a / b; case _ => throw new NotSupportedException() }
case не нужен, ок… Так проще.
Но, почему бы не сделать блочные конструкции подобно этому, пусть и с return, как тут предложено чуть выше…Alex_At_Net Автор
14.02.2019 13:48Слишком изменится концепция языка, я думаю. Но в F# так и есть. Как я вижу, решения понемногу переходят из F# в C#, так что может и дождемся такого подхода. Ну или таки сделают возможность миксировать F# и С# в одном проекте.
yarosroman
14.02.2019 10:58А что такое тринарный оператор? Может всё-таки тернарный?
Alex_At_Net Автор
14.02.2019 13:49Огромное спасибо. Приятно все таки увидеть конструктивный комментарий с предложением улучшения.
Sinatr
14.02.2019 11:42В целом интересно. Непонятно, это только для core или будет и в обычном фреймворке?
Примеры, если честно, не очень. Не обьяснено преимущество по сравнению со switch/case c pattern matching, зачем нужна новая конструкция, если уже давно добавленная может делать это и даже больше?Поскольку switch относительно легко переводится в набор if-ов, заменить любой switch также не представляется сложным.
Непонятно к чему это фраза? Это ли не очевидно? Обе конструкции используют ключевое слово switch и выполняют одинаковую функцию — управление потоком выполнения программы. Новый возвращает результат, работая по сути как inline local function, но внутри — все тот же старый switch, с немного странным синтаксисом.
В принципе чем-то похоже на попытку продолжить добавление expression-bodied members, что в случае методов и property getter/setter было довольно неплохой идеей. В новом switch выглядит немного странно, с одной стороны не нужно писать case/break/default, а с другой теряются некоторые интересные возможности.
Как записать несколько условий для одного тела? Скажем, при значение operation "+" и «add» должен выполниться один и тот же код, в старом switch это элементарно, а в новом как?
Об авторе: Александр Неткачёв закончил Таврический Национальный Университет по специальности Информатика. Работает и проживает в Великобритании. На протяжении многих лет занимается профессиональной разработкой ПО, ведет персональный сайт alexatnet.com.
Слишком пафосно. Где вы живете — неважно. «На протяжении многих лет» — лучше убрать, скорее всего, вы хотели написать так, чтобы не пришлось через 5 лет редактировать статью, меняя 20 лет на 25 лет или сколько там по вашему «многих лет», но получилось странно, будто вы боитесь признаться, сколько именно лет. Если есть сайт — то можно было не писать об университете, а добавить эту инфу там и ограничиться «Заходите на мой сайт» в конце статьи. На хабре любой может кликнуть на пользователе и глянуть профиль, если интересует автор или его background.
Плюс писать о себе в третьем лице немного странно.
«Автор этого комментария на протяжении многих лет работет и живет в Германии, в уважаемой фирме, весьма известной в узких кругах группы. Он окончил университет, любит свою маму и заботиться о своих домашних животных, чтобы еще написать такое написать не по теме...»mayorovp
14.02.2019 12:31Непонятно, это только для core или будет и в обычном фреймворке?
А как эта фича вообще может зависеть от рантайма, хотя бы в теории?
PsyHaSTe
14.02.2019 12:52Генерики требовали изменения рантайма, динамики тоже. async/await тоже не сразу стал доступен, были бекпорты на 3.5.Но там прям понятно было, что от рантайма много чего требуется, а тут по сути портянку if else нагенерить нужно.
PsyHaSTe
14.02.2019 12:51В целом интересно. Непонятно, это только для core или будет и в обычном фреймворке?
Это фича C# 8.0. К фреймворку версия языка никак не относится. CLR не менялся со времен 2.0AnarchyMob
14.02.2019 13:41Так как .NET Framework 4.8 не будет поддерживать .NET Standard 2.1, то некоторые фичи C# 8 в нем работать (из коробки) не будут, к примеру, асинхронные потоки и диапазоны, но при установке необходимой библиотеки из NuGet, с реализацией нужных типов, C# 8 полностью заведется на .NET Framework 4.8.
PsyHaSTe
14.02.2019 14:52Ну это понятно. Ровно та же история была с таплами, а до этого с async/await в .net 3.5.
yarosroman
14.02.2019 15:55-1Ну async/await не требуют поддержки со стороны CIL. фактически это синтаксический сахар.
i8008
14.02.2019 23:55+1Не понимаю, за что поставили минус комментарию выше.
Коллега прав, async/await не потребовало изменений CIL и даже CLR. Эта фича реализуется компилятором, который для async генерирует код, который реализует машину состояний. Аналогично как для yield return компилятор генерирует код реализации энумератора и т. д.
Или я что-то упустил?PsyHaSTe
16.02.2019 09:53Тоже не понимаю, но по факту из коробки не работало в .Net < 4.5. Не потому что рантам нужен был другой, а потому что Task/… типы были только в стандартной библиотеке 4.5. Через какое-то время появился пакет, который таски поставлял в 4.0 (те же типы в нугете).
То есть жесткой необходимости не было, но исторически зависимость была.
yarosroman
14.02.2019 15:53А причем тут .Net Standart? Стандарт гарантирует лишь совместимость библиотек (идентичность классов) на различных рантаймах и к версии C# не имеет отношения.
mayorovp
14.02.2019 15:56Часть фишек C# требуют поддержки от рантайма, а значит и совместимости с определённой версией .Net Standart.
yarosroman
15.02.2019 01:25docs.microsoft.com/ru-ru/dotnet/standard/net-standard тут ни слова нет про рантайм.
mayorovp
14.02.2019 15:59Ну нет, менялся. Тем же ref struct требуется особая поддержка со стороны рантайма.
BkmzSpb
14.02.2019 12:53switch
expressions больше всего подходят именно для pattern-matching (отсюда).
Мне кажется, что с операциями это могло бы выглядеть как-то так
double Compute(double a, double b, object op) => op switch { string s when (s == "add" || s == "+") => a + b, string s when (s == "subtract" || s == "-") => a - b, {} => throw new ArgumentException("Unsupported type"), null => throw new ArgumentNullException(nameof(op)) }
PsyHaSTe
14.02.2019 13:02Проблема в том, что в шарпах statements != expressions, из-за чего и приходится воротить такие ужасные вещи как эти Func. Проще фичей не пользоваться в таких случаях вообще, что крайне снижает её полезность.
BkmzSpb
14.02.2019 13:16Простите, я не совсем понял, а к чему здесь
Func
? Я утверждаю чтоswitch
expressions годятся преимущественно для pattern-matching. Для множетсвенных действий на одинcase
есть стандартныйswitch
.
Кроме pattern-matching, лично мне бы такой синтаксис пригодился, если нужно сопоставить некоторыйenum
и некоторый набор значений. Теперь это можно сделать лаконичнее.
Либо так, либо я упускаю что-то важное.PsyHaSTe
14.02.2019 14:56Потому что почти всегда нужно сделать немного больше, чем просто сделать одно действие на строчку.
Ну вот например, в моем боте сейчас написано
let result = from_slice::<ApiResult<T>>(chunk.as_ref()); match result { Ok(api_result) => { if api_result.ok { Ok(api_result.result) } else { let text: String = String::from_utf8_lossy(chunk.as_ref()).into_owned(); Err(TelegramClientError::ConnectionError(text)) } } Err(e) => Err(TelegramClientError::SerdeError(e)), }
Как это на C# написать с этой фичей? А никак, если только не извращаться как в примерах с Func.
Понятно, что хоть какой-то матчинг лучше, чем никакого, но из-за того, что стейтменты != экспрешны получается часто очень неудобно.
BkmzSpb
14.02.2019 16:50Ну я плохо знаком с таким синтаксисом. Но такое количество операций я бы точно написал с помощью
if
-else
. Например, как-то так.
if(your_query(chunk) is var result && result is ApiResult api_result && Ok(api_result)) { if(api_result.ok) Ok(api_result.result); else throw new ConnectionError(new string(GetFrom(chunk))); } else if(result is Error e) throw new SerdeError(e);
В силу проф. деформации это для меня гораздо читабельнее и работает в приближении когда
Ok
не возвращает значения. Если же возвращает, тогда так
return your_query(chunk) switch { ApiResult api_result when Ok(api_result) => api_result.ok ? Ok(api_result.result) : throw new ConnectionError(new string(GetFrom(chunk))), Error e => throw new SerdeError(e), _ => throw new ThisShouldNeverHappenError() };
Разумеется, я что-то мог не учесть, но мне кажется для относительно простого матчинга это работает неплохо. Если же у вас прямо целые блоки кода на каждый
case
, то лучше использовать либо традиционныйswitch
, либоif
-else
. Но это, естественно, ИМХО.PsyHaSTe
14.02.2019 17:04Ну я плохо знаком с таким синтаксисом.
result — это просто энум, по которому мы делаем свитч (матч?)
pub enum Result<T, E> { Ok(T), Err(E), }
Разница только в том, что в отличие от шарповых энумов кроме собственно тэга разные варианты могут иметь данные какого-либо тип. Соответственно у нас либо
Ok(ApiResult)
, либоErr(SomeError)
Соответственно ваш второй вариант.
Но такое количество операций я бы точно написал с помощью if-else. Например, как-то так.
Так ведь в этом и смысл :) Вы упаковали создание переменной
text
и её использование в одно значение. А я как раз и показывал, что в реальных случаях нам нужно сделать что-то больше, чем просто вернуть результат. Ну допустим мы перепишем это как
let result = from_slice::<ApiResult<T>>(chunk.as_ref()); match result { Ok(api_result) => { if api_result.ok { Ok(api_result.result) } else { let text: String = String::from_utf8_lossy(chunk.as_ref()).into_owned(); debug!("text value = {}", text); Err(TelegramClientError::ConnectionError(text)) } } Err(e) => Err(TelegramClientError::SerdeError(e)), }
В силу проф. деформации это для меня гораздо читабельнее и работает в приближении когда Ok не возвращает значения.
Все ветки возвращают значение, в этом и смысл. И они тоже возвращают либо
Ok(...)
, либоErr(TelegramClientError)
Если же у вас прямо целые блоки кода на каждый case, то лучше использовать либо традиционный switch, либо if-else.
Традиционный
switch
этоstatement
, он не умеет возвращать значение.
По сути претензия в том, что в шарпе нельзя написать
let a = if condition { 10 } else { println!("Assigning a to 20"); 20 }
И это иногда мешает.
В силу проф. деформации это для меня гораздо читабельнее
На самом деле вариант на матчах намного читаемее, просто непривычно + там много написано бойлерплейта который нужен для работы. Если его сократить до рафинированных вариантов как у вас то будет все сильно симпатичнее:
match from_slice(chunk) { Ok(api_result) => { if api_result.ok { Ok(api_result.result) } else { let text = String::from(chunk); Err(text) } } Err(e) => Err(e), }
BkmzSpb
14.02.2019 17:58+1Теперь стало немного понятнее. Мне кажется, то, что вы предлагаете, просто не соответствует парадигме C#. Например возврат ошибок вместо
throw
.
Условно, вот так
if(from_slice(chunk) is var api_result && api_result is null) throw new NullReferenceException(nameof(api_result)); return api_result.result ?? throw new ResultException(chunk);
Вся логика, которую вы добавляете перед возвращением ошибки, должна быть в
catch
блоке, освобождение (условных) ресурсов — где-то вfinally
.catch
блок позволяет дифференцировать эксепшены по типу и по условиям, используя тот же самыйwhen
.
Если взять логику отличного от C# языка и попытаться ее натянуть на него, получится мягко говоря не очень. Верно и обратное.
PsyHaSTe
14.02.2019 18:05Теперь стало немного понятнее. Мне кажется, то, что вы предлагаете, просто не соответствует парадигме C#.
Я просто привел пример на ошибках, но энумы это ведь не только они. Ну допустим будет:
match operator { BinaryOperator(type, a, b) => { if type == Operator::Addition { a + b } else { pritnln!("Doing subtraction"); a - b } } UnaryOperator(a) => { println!("The only unary operator in the language - negation. Doint it..."); -a }, }
Суть осталась ровно та же, но делать тут эксепшны наверное не стоит :)
Если взять логику отличного от C# языка и попытаться ее натянуть на него, получится мягко говоря не очень
Да нет, логика та же самая, как например тут, только чуть более требовательная.
Taraflex
14.02.2019 17:06Как это на C# написать с этой фичей? А никак,
Оператор запятая. Хотя все равно бред конечно.
Alex_At_Net Автор
14.02.2019 14:13Спасибо за развернутый комментарий.
В целом интересно. Непонятно, это только для core или будет и в обычном фреймворке?
Я не нашел где можно точно посмотреть. Надеюсь, что со временем и в обычный фреймворк добавят.
Примеры, если честно, не очень. Не обьяснено преимущество по сравнению со switch/case c pattern matching, зачем нужна новая конструкция, если уже давно добавленная может делать это и даже больше?
Преимущество новой конструкции только в том, что это expression и, соответственно, его можно использовать там, разрешен expression — в инициализации переменных например.
Поскольку switch относительно легко переводится в набор if-ов, заменить любой switch также не представляется сложным.
Непонятно к чему это фраза? Это ли не очевидно? Обе конструкции используют ключевое слово switch и выполняют одинаковую функцию — управление потоком выполнения программы. Новый возвращает результат, работая по сути как inline local function, но внутри — все тот же старый switch, с немного странным синтаксисом.
В принципе чем-то похоже на попытку продолжить добавление expression-bodied members, что в случае методов и property getter/setter было довольно неплохой идеей. В новом switch выглядит немного странно, с одной стороны не нужно писать case/break/default, а с другой теряются некоторые интересные возможности.
Как записать несколько условий для одного тела? Скажем, при значение operation "+" и «add» должен выполниться один и тот же код, в старом switch это элементарно, а в новом как?
Я, честно говоря, тоже задал себе этот вопрос, но пока не разобрался, как это сделать.
Об авторе: Александр Неткачёв закончил Таврический Национальный Университет по специальности Информатика. Работает и проживает в Великобритании. На протяжении многих лет занимается профессиональной разработкой ПО, ведет персональный сайт alexatnet.com.
Слишком пафосно. Где вы живете — неважно. «На протяжении многих лет» — лучше убрать, скорее всего, вы хотели написать так, чтобы не пришлось через 5 лет редактировать статью, меняя 20 лет на 25 лет или сколько там по вашему «многих лет», но получилось странно, будто вы боитесь признаться, сколько именно лет. Если есть сайт — то можно было не писать об университете, а добавить эту инфу там и ограничиться «Заходите на мой сайт» в конце статьи. На хабре любой может кликнуть на пользователе и глянуть профиль, если интересует автор или его background.
Плюс писать о себе в третьем лице немного странно.
«Автор этого комментария на протяжении многих лет работет и живет в Германии, в уважаемой фирме, весьма известной в узких кругах группы. Он окончил университет, любит свою маму и заботиться о своих домашних животных, чтобы еще написать такое написать не по теме...»
Заходя в новое общество принято представляться. Не видел, что бы это считали странным. Более того, сам считаю, что это добавляет тексту некую персонализацию. Вы не с компанией разговариваете, а с человеком, это ли не важно?
Tangeman
14.02.2019 19:40… зачем нужна новая конструкция, если уже давно добавленная может делать это и даже больше?
Вместо многоветочного if или пусть даже обычного switch (где придётся добавлять case и break на каждый вариант) — очень компактная конструкция, которую легко писать (и читать). Впрочем, when-expression было бы ещё лучше (пример чисто для иллюстрации синтаксиса):
var result = when { (a > b) => a - b, (b > a) => b - a, _ => 0, }
По сравнению с многометровыми if-else или switch/when/break это гораздо приятней, и в реальных проектах достаточно ситуаций когда это было бы удобно.
EngineerArt
14.02.2019 14:21Синтаксис потихоньку превращается в JavaScript
if else -> switchvar error = (fileSize > 1000) switch { true => "file is too large", _ => ((Func<string>)(() => { processFile(); return ""; }))() };
megasuperlexa
14.02.2019 14:22Примеры, конечно, мега-неудачные, но как и отмечает автор красоты тут нет. И не будет, пока нет автоматического вывода типов, и пока весь язык построен вокруг иперативного стиля — а тут декларативные экспрешены. Что поделать, это c#
Alex_At_Net Автор
14.02.2019 14:26Ну все-таки движется в правильном направлении? Надеюсь, лет за 20 дождемся и вывода типов.
UnclShura
14.02.2019 21:06Какого вывода типов? Того, который уже есть или утиной типизации? С типами в C# все прекрасно.
Alex_At_Net Автор
15.02.2019 02:45type inference, как в F# — когда не надо устанавливать тип int, если функция возвращает 10.
nomit
15.02.2019 06:52Ну это абзац… Когда обычный pattern marching пытаются запихать во все щели. А потом с таким приходиться работать и рефакторить. И вроде сложный на вид код легко трансформируется в набор if else или switch case.
smind
Аналогичный код с использованием switch expression:
var error = (fileSize > 1000) switch…
...
Вы должно быть шутите?!
Был хорошо читаемый if а вы его заменяете менее понятным куском кода.
Уверен что можно найти более подходящие примеры использования.
alexr64
Да они не шутят — откровенно глумятся. Почему нельзя выводить типы во время компиляции и сократить это непотребство до такой формы?..
amberovsky
В целом выглядит как попытка добавить больше возможностей функциональных языков в условиях ограниченных возможностей.
TheShock
А я бы не отказался от вменяемых иммутабельных структур в C#.
PsyHaSTe
Рекорды же обещают. Хотя это просто следствие бедности языка, на самом деле. Если бы все можно было решить парой атрибутов, было бы намного лучше.
TheShock
А как вы решите парой атрибутов изменение в глубине дерева? Вот представьте:
Как вы себе это представляете на атрибутах? Кстати, в рекордах глубокое изменение как-то так будет делаться?
edit: я посмотрел Proposal. Наверное оно будет как-то так:
Не особо удобно. При чем, что еще хуже, метод with придется писать в каждом рекорде вручную. И в чем смысл?
PsyHaSTe
Мне больше нравится неизменяемость как модификатор переменной, а не самого типа. Беда что нельзя выразить наследование неизменяемости в C#
TheShock
А это как? Вот у вас есть
PsyHaSTe
Ошибка компиляции
TheShock
И что — оно должно пробегать по всем наследникам? А на какой строчке валится — та, которая
foo.Qux();
или та, котораяbar = 2;
?PsyHaSTe
Примерно так
TheShock
А как на Расте будет выглядеть этот пример?
PsyHaSTe
как-то так полагаю:
let foo = foo1.with(|w| w.bar = foo1.bar.with(|b| b.x = foo1.bar.x))
Но полагаю, более идеоматичным будет
Т.к. в отличие от тех же шарпов почти все объекты предоставляют метод клонирования себя.
TheShock
Хм. Ну клонирование — не совсем то. Нельзя сравнить, к примеру, что какое-то другое поле не поменялось.
Вот черт, тоже не то. Как такое делается в исконно функциональных языках? Есть язык, где это делается как-то так?
PsyHaSTe
Странная «предъява» :) С чего бы другому полю меняться? Это ж не С++ где за присваиванием может всевозможная магия происходить.
Это будет нарушать закон деметры, так что не думаю что в практически полезных языках такое практикуется. К
TheShock
Ну это не догма, а скорее рекомендация. Я не очень знаю, как это делается во взрослых языках. Вот у меня есть игра. У игры есть мир, у мира есть дом, в доме есть комнаты, в комнате есть телевизор:
В какой-нибудь игрушке на ФП будет такое дерево стейта?
И вот у меня есть задача — включить телевизор. Мне нужно получить новый мир, в котором этот телевизор включен? Как это сделать?
PsyHaSTe
Ну в том же расте все зависит от владения. При желании — делаете все поля CoW и тогда оверхед будет только у тех полей которые реально перезаписались.
Рантайма который бы магией делал это под ковром в расте нет, но мб в той же скале он имеется.
Alex_At_Net Автор
А вы читать умеете? Я же сказал, что не считаю это красивым на данный момент. Но думаю, что в дальнейшем это улучшится.
amberovsky
Вы написали
и привели далее этот пример. Логично предполагать, что вы приводите этот пример как потенциально значимое улучшение.
vlx
Для if else любителей однострочек вообще есть var error = (filesize > 1000)? Blah(): blahblah();
Vahman
Так тернарный оператор автор тоже хочет заменить на switch expression)))