Достаточно! JavaScript долго держал нас в своих руках своими ножными орудиями. Когда я впервые услышал термин Подъем, я понятия не имел об этом и неправильно расслышал термин хостинг. Вы с радостью объявляете переменные, используя var, и вам приходится смириться с тем, что поднимать var (поднимать их все до самой верхней области) — это нормально. . Я не могу поверить, что JS убедила остальных в том, что все в порядке. Затем пришел ES6 и спас нас. let исправил область видимости. const при условии неизменности. По крайней мере, сейчас вы можете сказать, что JavaScript поддерживает функциональное программирование.

У JavaScript есть перья на шляпе — let и const.

Даже ребенок сказал бы вам, что два основных аспекта функционального программирования — это неизменность и функции первоклассных граждан, а именно. Функции высшего порядка (HOF). Другие аспекты не менее важны, но без двух верхних не может быть функционального программирования.

С самого первого дня C# поддерживал возможность обращаться с функциями как с гражданами первого класса через delegates. Да, концепция HOF не была лучшей в C# до появления анонимных делегатов или более поздних лямбд. Но, эй, вы могли передавать функции как данные.

Это была одна сторона медали — HOF. А как насчет другой стороны — неизменности? Как вы уже должны знать, Неизменяемость — непростая тема.

Жаль, что даже спустя 15 с лишним лет C# все еще не поддерживает неизменяемость… полностью.

C# поддерживает константы (времени компиляции) (const) и частичную или довольно своеобразную поддержку неизменяемых переменных (readonly). С помощью readonly вы можете объявлять только неизменяемые переменные member. Его нельзя использовать с локальными переменными или параметрами метода. Но вот что особенного.

readonly по-прежнему можно изменить в конструкторе.

Интересно, почему.

Продажа неизменности

Я большой поклонник неизменяемости. Уверенность, которую обеспечивает неизменяемая переменная, незаменима. Как только вы начинаете объявлять неизменяемые переменные — назначаемые только один раз, сложность вашего кода/логики распутывается и собирается поэтапно. Отдельные шаги вашей логики плавно соединяются в цепочку/конвейер, что позволяет тестировать каждый шаг независимо.

Неизменяемость для C#

Сам я не читал (никаких) предложений, но раз или два слышал слухи о добавлении поддержки неизменяемых переменных в C#. Как я уже сказал, я не просматривал предложения, поэтому я здесь мечтаю.

Есть несколько способов заставить С# поддерживать неизменяемые переменные.

Новые ключевые слова/квантификаторы

val

… как в Scala или в паре с собственным var C#.

Несмотря на то, что он лаконичен и использует соглашение об установке, мне не нравится этот вариант по двум причинам:

  • var был изобретен в первую очередь для автоматического вывода типа или того, что C# называет неявной типизацией, специально для запросов LINQ, которые возвращают анонимные типы. Несмотря на то, что команда разработчиков C# поступила разумно, сделав его более или менее универсальным, его лучше было бы назвать auto. Поскольку C# не является чисто функциональным языком и по своей сути является императивным, так уж получилось, что var естественным образом связано с (изменяемой) переменной.
  • var ограничен локальными переменными. Его нельзя использовать в качестве членов класса, параметров метода и т. д.
  • Следует ли var поднять за пределы области действия функции для членов класса и других, чтобы val стал подходящим аналогом? Другими словами, var и val должны свободно использоваться — локальные переменные, аргументы членов и т. д. Это не сработает, потому что var в параметрах не имеет смысла (указание на то, что var равно auto), а val в членах класса перекрывается с readonly.
  • Если var не поднят, то да, возможно, val может сработать. А может, не совсем так. Например, если мы назначим запрос LINQ, возвращающий анонимный тип, для val, мы ожидаем, что val выполнит автоматический вывод типа. Хотя это приятно, valnow делает больше, чем то, с чего мы начали, а именно. неизменяемые переменные.
  • В последние годы C# пытался понравиться сообществу Java и JavaScript больше, чем его покровителям. val может показаться попыткой обратиться к сообществу Scala.
  • Как я уже сказал, var и val объединяются на словах, но не по своей цели. Там определенно есть какой-то шум.

immutable

… на языке фа-диез mutable.

Из всех способов объявления переменных, изменяемых и неизменяемых, мне больше всего нравится F#; лучше, чем var и val. F# начинается с неизменяемых переменных по умолчанию и требует, чтобы вы явно указали, что хотите сделать переменную изменяемой, как в Scala. Но прелесть F# в том, что нужно украсить let по умолчанию mutable, чтобы он выделялся визуально.

// F# code 
let imCount          = GetNoOfItems();
let mutable mutCount = GetNoOfItems();

Заимствуя идею, мы могли бы придумать ключевое слово immutable.

// C# code
int mutCount          = GetNoOfItems();
immutable int imCount = GetNoOfItems();

Делает работу. Что нам делать с readonly, кроме наглости? Два разных ключевых слова по сути одно и то же. Это послужит катализатором для введения еще одного для неизменяемых параметров метода. Да ладно, это не PHP 😂.

Если бы кинули на голосование, я уверен, этот вариант проиграет.

Повторное использование существующих ключевых слов/квантификаторов

const

Яростное нет. Константа отличается от неизменяемой переменной. Любая попытка повторного использования исказит смысл существующих соглашений в C#. Дело закрыто.

let

Смешанные чувства. let был обучен запросам LINQ. Хотя (промежуточные) переменные, объявленные через let, являются неизменяемыми[^1], это не особенно очевидно в императивном языке, таком как C#. let в C# не предполагает того же, что и в F#.

Предположим, мы используем let, делаем ли мы его независимым ключевым словом или модификатором существующих объявлений переменных?

// C# code 
let immutableCount = GetNoOfItems();

or

// C# code
let int imCount = GetNoOfItems();

Я предпочитаю последнее, потому что первое находится в той же лодке, что и val, и неявная типизация, которую мы обсуждали ранее. Последнее, кажется, соответствует всем требованиям. Только то, что let получило это непреднамеренное ощущение хромой копии из F # и плохо применено. Мы действительно хотим эту вину?

readonly

Если все сделать правильно, то это будет идеальный выбор. Прежде чем заняться идеальной стороной этого…

  • С точки зрения области действия readonly является противоположностью var. var застрял внутри методов, а readonly застрял снаружи. О, влюбленные врозь! Ранее мы обсуждали поднятие var за пределы области действия функции. Здесь нам нравится помещать readonly в область действия функции.
  • var является эквивалентом спецификатора фактического типа (или автоматически определяемого типа). readonly — это модификатор спецификатора типа. Итак, readonly хорошо сочетается и с var. Влюбленные встречаются. Итак, readonly var imCount = … так же легитимно, как и readonly int imCount = ....
  • readonly эквивалентен mutable F#, производя обратный эффект. readonly в C# делает переменную неизменной, а mutable в F# делает наоборот. Оба являются модификаторами объявлений переменных.

Итак, объявим readonly победителем? Да, но не совсем. Это еще не идеальный выбор. Потому что давайте напомним себе, что readonly особенный.

Если мы внесем одно критическое изменение и больше не будем поддерживать readonly переменных-членов, изменяемых внутри конструктора, тогда readonly будет идеальным выбором.

Я утверждаю, что мутированные readonly переменные-члены сегодня можно переписать в однажды назначенные и действительно неизменяемые переменные. Таким образом, критическое изменение здесь действительно для победы.

По ту сторону забора

Языки, которые начинались как функциональные, такие как Scala, F# и другие, процветают; что касается неизменности. На каком-то уровне я бы сказал, что С++ тоже справился с этим; тем не менее остерегайтесь причуд.

Не будет преувеличением сказать, что за JavaScript стоит самое большое сообщество; поддержка которого благословила его let и const.

Давайте уделим немного времени опросу созданных нами главных конкурентов — C# и Java.

Java начиналась с поддержки неизменяемости, но не с HOF; до введения лямбда-выражений в версии 8. Итак, теперь вы можете выполнять функциональное программирование на Java; или так говорят.

С другой стороны, C# начал с (немного грубой) поддержки HOF и все еще ковыляет к поддержке неизменности. Неудивительно, что они заклятые соперники. В каком странном мире мы живем.

Излишне говорить, что должны быть и другие хорошие варианты. Люблю слышать.

C# — очень тщательно разработанный язык. Существует множество письменных материалов, подтверждающих это. В то же время он создан человеком. Таким образом, любые ограничения или недостатки, которые следует видеть, следует рассматривать с некоторой любовью.

К сожалению, каждый язык начинается с определенной разновидности — императивной, объектно-ориентированной, функциональной или любой другой, а позже обнаруживает, что с трудом или не может поддерживать другие разновидности.

Разумныйязык должен поддерживать и позволять выбирать из множества широко применяемых парадигм, методов и стилей программирования и предоставлятьразумномупрограммисту возможность реализовать решение, которое подходит проблема.

Или так сказал Бьерн Страустоуп (что-то похожее), конечно, цитируя C++. Он был прав во всех отношениях.

Этот пост изначально был опубликован на Опыте разработчика.