Имеет ли изменяемые локальные переменные в функции, которые используются только для внутренних целей (например, функция не имеет побочных эффектов, по крайней мере, намеренно), все еще считается «не функциональным»?
например, в проверке стиля курса «Функциональное программирование с помощью Scala» любое var
использование считается плохим
Мой вопрос, если функция не имеет побочных эффектов, все еще не рекомендуется писать код императивного стиля?
например, вместо использования хвостовой рекурсии с шаблоном аккумулятора, что плохого в том, чтобы делать локальный цикл for и создавать локальный изменяемый объект ListBuffer
и добавлять к нему, пока ввод не изменяется?
Если ответ «да, они всегда обескураживают, даже если нет побочных эффектов», тогда в чем причина?
источник
var
всегда не работает. Scala имеет ленивые vals и оптимизацию хвостовой рекурсии, которые позволяют полностью избежать vars.Ответы:
Единственная вещь, которая здесь недвусмысленно плоха, это утверждать , что что-то является чистой функцией, когда это не так.
Если изменяемые переменные используются таким образом, который действительно и полностью самодостаточен, функция внешне чиста, и все счастливы. На самом деле Haskell поддерживает это явно , а система типов даже гарантирует, что изменяемые ссылки не могут быть использованы вне функции, которая их создает.
Тем не менее, я думаю, что говорить о «побочных эффектах» не лучший способ взглянуть на это (и именно поэтому я сказал «чистый» выше). Все, что создает зависимость между функцией и внешним состоянием, усложняет процесс рассуждения, включая такие вещи, как знание текущего времени или использование скрытого изменяемого состояния без поточной защиты.
источник
Проблема не в изменчивости как таковой, а в отсутствии ссылочной прозрачности.
Ссылочно-прозрачная вещь и ссылка на нее всегда должны быть равны, поэтому ссылочно-прозрачная функция всегда будет возвращать одинаковые результаты для данного набора входных данных, а ссылочно-прозрачная «переменная» на самом деле является значением, а не переменной, поскольку не может измениться Вы можете сделать ссылочно-прозрачную функцию с изменяемой переменной внутри; это не проблема. Однако может быть сложнее гарантировать, что функция прозрачна по ссылкам, в зависимости от того, что вы делаете.
Я могу вспомнить один случай, когда нужно использовать изменчивость, чтобы сделать что-то очень функциональное: запоминание. Memoization - это кэширование значений из функции, поэтому их не нужно пересчитывать; он прозрачен, но использует мутацию.
Но в целом ссылочная прозрачность и неизменность идут вместе, кроме локальной изменяемой переменной в ссылочно-прозрачной функции и памятке, я не уверен, что есть другие примеры, где это не так.
источник
Не очень хорошо сводить это к «хорошей практике» против «плохой практики». Scala поддерживает изменяемые значения, потому что они решают определенные проблемы гораздо лучше, чем неизменяемые значения, а именно те, которые имеют итеративный характер.
Что касается перспективы, я вполне уверен, что
CanBuildFrom
почти все неизменяемые структуры, предоставляемые scala, делают какие-то мутации внутри себя. Дело в том, что то, что они выставляют, является неизменным. Сохранение максимально возможного количества значений помогает программе легче рассуждать и меньше подвержено ошибкам .Это не означает, что вам обязательно нужно избегать изменяемых структур и значений внутри, когда у вас есть проблема, которая лучше подходит для изменчивости.
Имея это в виду, многие проблемы, которые обычно требуют изменяемых переменных (таких как циклы), могут быть лучше решены с помощью множества функций более высокого порядка, которые предоставляют языки, такие как Scala (map / filter / fold). Будьте в курсе тех.
источник
map
,filter
,foldLeft
ИforEach
сделать трюк большую часть времени, но когда они не делают, будучи в состоянии чувствовать , что я «OK» , чтобы возвращаться к грубой силы императивного кода хорошо. (до тех пор, пока нет никаких побочных эффектов, конечно)Помимо потенциальных проблем с безопасностью потоков, вы также обычно теряете большую безопасность типов. Императивные циклы имеют тип возврата
Unit
и могут принимать практически любое выражение для входных данных. Функции высшего порядка и даже рекурсия имеют гораздо более точную семантику и типы.У вас также есть гораздо больше возможностей для функциональной обработки контейнеров, чем с императивными циклами. С императивом у вас, в основном, есть
for
,while
и незначительные вариации на эти два типаdo...while
иforeach
.В функционале у вас есть агрегат, счетчик, фильтр, поиск, flatMap, свертывание, groupBy, lastIndexWhere, map, maxBy, minBy, разделение, сканирование, sortBy, sortWith, span и takeWhile, просто чтобы назвать несколько более распространенных из Scala стандартная библиотека. Когда вы привыкнете к тому, что они доступны, императивные
for
петли кажутся слишком простыми в сравнении.Единственная реальная причина использовать локальную изменчивость очень редко для производительности.
источник
Я бы сказал, что это в основном нормально. Более того, создание структур таким образом может быть хорошим способом улучшить производительность в некоторых случаях. Clojure решил эту проблему, предоставив временные структуры данных .
Основная идея заключается в том, чтобы разрешить локальные мутации в ограниченном объеме, а затем заморозить структуру перед ее возвратом. Таким образом, ваш пользователь все еще может рассуждать о вашем коде, как если бы он был чистым, но вы можете выполнять преобразования на месте, когда это необходимо.
Как говорится в ссылке:
источник
Отсутствие локальных изменяемых переменных имеет одно преимущество - это делает функцию более дружественной к потокам.
Я был сожжен такой локальной переменной (не в моем коде, и при этом у меня не было источника), вызывающей повреждение данных с низкой вероятностью. Безопасность потоков не упоминалась так или иначе, не было состояния, которое сохранялось между вызовами, и не было никаких побочных эффектов. Мне не пришло в голову, что это не может быть потокобезопасным, погоня за случайным повреждением данных 1 к 100 000 - это королевская боль.
источник