Может кто-нибудь уточнить, что означает эта цитата Joel On Software: (функциональные программы не имеют побочных эффектов) - PullRequest
21 голосов
/ 15 марта 2010

Я читал Joel On Software сегодня и наткнулся на эту цитату :

без понимания функционала программирование, вы не можете придумать MapReduce, алгоритм, который делает Google так масштабно масштабируется. Термины Map и Reduce происходят из Лисп и функциональное программирование. Уменьшение карты Оглядываясь назад, очевидно для всех кто помнит из их 6.001-эквивалентный класс программирования, который имеют чисто функциональные программы нет побочных эффектов и, таким образом, тривиально параллелизуемое.

Что он имеет в виду, когда говорит, что функциональные программы не имеют побочных эффектов? И как это делает распараллеливание тривиальным?

Ответы [ 5 ]

19 голосов
/ 16 марта 2010

Что он имеет в виду, когда говорит функциональные программы не имеют никакой стороны эффекты? * * 1002

Большинство людей считают программирование созданием переменных, присвоением им значений, добавлением элементов в списки и т. Д. Переменные «меняются», отсюда и название.

Функциональное программирование - это стиль разработки программ для исключения переменных - все постоянно или только для чтения.

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

"Но Джульетта! Как написать полезную программу, если она не может изменить что-либо "

Хороший вопрос!

Вы «модифицируете» вещи, создавая новый экземпляр вашего объекта с измененным состоянием. Например:

class Customer
{
    public string Id { get; private set; }
    public string Name { get; private set; }

    public Customer(string id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    public Customer SetName(string name)
    {
        // returns a new customer with the given name
        return new Customer(this.Id, name);
    }
}

Таким образом, вся инициализация происходит в конструкторе, и мы больше не можем модифицировать объект - мы создаем новые экземпляры с нашими изменениями, переданными в конструктор.

Вы будете удивлены, насколько далеко вы сможете перенести этот стиль программирования.

«Но Джульетта !? Как это может быть эффективно со всем этим копированием?»

Хитрость заключается в том, что вам не нужно копировать весь объектный граф, только части, которые изменились. Если части вашего графа объектов не изменились, можете повторно использовать его в новом объекте (скопируйте указатель , не создавайте новый экземпляр каких-либо объектов в этой части графика).

Вы будете удивлены, насколько далеко вы сможете перенести этот стиль программирования. Фактически, его чрезвычайно легко написать неизменяемые версии многих распространенных структур данных - таких как неизменные деревья Avl, красно-черные деревья, много видов куч и т. Д. См. здесь для реализация неизменного трепа.

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

И как это делает распараллеливание тривиальный?

Подумайте об этом: если у вас есть неизменяемое дерево или какая-либо другая структура данных, то вы можете двумя потоками вставлять, удалять и искать элементы в дереве без необходимости блокировки. Поскольку дерево является неизменяемым, один поток не может перевести объект в недопустимое состояние под нос другого потока, поэтому мы исключаем целый класс ошибок многопоточности, связанных с условиями гонки. Поскольку у нас нет условий гонки, нам не нужны блокировки, поэтому мы также исключаем целый класс ошибок, связанных с блокировкой.

Поскольку неизменяемые объекты по своей природе являются поточно-ориентированными, они, как говорят, делают параллелизм «тривиальным». Но это только половина истории. В случаях нам нужно, чтобы изменения в одном потоке были видимы для другого - так как нам это сделать с неизменяемыми объектами?

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

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

Поверьте, приведенная выше стратегия упрощает параллельное программирование в 1000 раз по сравнению с блокировками в изменяемом состоянии. Итак, важная часть комментария Джоэла: «Без понимания функционального программирования вы не сможете изобрести MapReduce, алгоритм, который делает Google настолько масштабируемым».

Традиционная блокировка плохо масштабируется, потому что для блокировки объекта необходимо иметь ссылку на его указатель - заблокированный объект должен находиться в той же памяти, что и объект, выполняющий блокировку. Вы не можете получить блокировку объекта между процессами.

Но подумайте о модели передачи сообщений выше: потоки передают сообщения два и друг от друга. Есть ли разница между передачей сообщения потоку в том же процессе и передачей сообщения потоку, прослушивающему какой-либо IP-адрес? На самом деле, нет. И это именно потому, что потоки могут отправлять и получать сообщения через границу процесса, и передача сообщений масштабируется так же, как это происходит, потому что приложение не привязано к одной машине, и ваше приложение может работать на любом количестве машин.

(Для чего это нужно, вы можете реализовать передачу сообщений с помощью изменяемых сообщений, просто никто этого не хочет, потому что поток не может ничего сделать с сообщением, не заблокировав его - что мы уже знаю, что полно проблем. Таким образом, неизменяемый способ использовать по умолчанию при использовании параллельного обмена сообщениями.)

Несмотря на то, что он очень высокий уровень и скрывает множество фактических деталей реализации, вышеприведенные принципы - это то, как Google MapReduce может масштабироваться в значительной степени до бесконечности.

Смотри также: http://www.defmacro.org/ramblings/fp.html

13 голосов
/ 16 марта 2010

Дайте мне википедию это для вас

Вкратце, чистая функция - это функция, которая вычисляет вещи, основываясь только на заданных аргументах, и возвращает результат.

Запись чего-либо на экран или изменение глобальной переменной (или элемента данных) является побочным эффектом. Использование данных, отличных от данных, указанных в аргументе, также делает вашу функцию не чистой, хотя это не побочный эффект.

Написание «чистой функции» облегчает параллельный вызов многих ее экземпляров. Это главным образом потому, что, будучи чистым, вы можете быть уверены, что он не влияет на внешний мир и не полагается на внешнюю информацию.

7 голосов
/ 16 марта 2010

Функциональное программирование направлено на создание функций, которые зависят только от их входов и не изменяют состояние в других частях системы (т. Е. Не имеют побочных эффектов при их выполнении).

Это означает, среди прочего, что они являются идемпотентными: одну и ту же функцию можно запускать много раз на одном и том же входе, и, поскольку она не имеет побочных эффектов, вам все равно, сколько раз она запускается. Это хорошо для распараллеливания, потому что это означает, что вам не нужно создавать много служебных данных, чтобы отслеживать, происходит ли сбой определенного узла.

Конечно, в реальном мире трудно исключить побочные эффекты из ваших программ (например, запись в файл). Таким образом, реальные программы, как правило, представляют собой комбинацию функциональных и нефункциональных частей.

4 голосов
/ 16 марта 2010

Я считаю, что он имеет в виду, что чисто функциональный код делает явным поток данных через программу. Побочные эффекты позволяют частям кода «общаться» способами, которые трудно анализировать.

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

Это было бы упрощением реальности, потому что существует также проблема разложения кода на «куски», которые составляют примерно равные «усилия». Это требует от человека написания функционального кода таким образом, чтобы он был разумно разложен при распараллеливании.

4 голосов
/ 16 марта 2010

Единицы функциональных программ имеют только свои входные и выходные данные, а не внутреннее состояние. Это отсутствие внутреннего состояния означает, что вы можете разместить функциональные модули на любое количество ядер / узлов, не беспокоясь о том, что предыдущие вычисления в модуле повлияют на следующие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...