Как устроено функциональное JavaScript-приложение на основе программирования? - PullRequest
29 голосов
/ 20 апреля 2010

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

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

var external;
function foo() {
   external = 'bar';
}
foo();

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

Хорошо, как это работает, когда вы имеете дело с объектами, а что нет? Например, часто у меня будет конструктор и метод init, который инициализирует объект, например:

var Foo = function(initVars) {
   this.init(initVars);
}

Foo.prototype.init = function(initVars) {
   this.bar1 = initVars['bar1'];
   this.bar2 = initVars['bar2'];
   //....
}

var myFoo = new Foo({'bar1': '1', 'bar2': '2'});

Таким образом, мой метод init намеренно вызывает побочные эффекты, но каков функциональный способ справиться с такой же ситуацией?

Кроме того, если бы кто-нибудь мог указать мне на исходный код Python или JavaScript программы, которая пытается быть настолько функциональной, насколько это возможно, это также будет высоко оценено. Я чувствую, что я близок к тому, чтобы "получить это", но я просто не совсем там. В основном меня интересует, как функциональное программирование работает с традиционной концепцией классов ООП (или отменяет ее для чего-то другого, если это так).

Ответы [ 4 ]

29 голосов
/ 23 апреля 2010

Вы должны прочитать этот вопрос:

Javascript как функциональный язык

Есть много полезных ссылок, в том числе:

Теперь, на мой взгляд. Многие люди неправильно понимают JavaScript , возможно, потому, что его синтаксис похож на большинство других языков программирования (где Lisp / Haskell / OCaml выглядят совершенно по-другому). JavaScript не объектно-ориентированный, на самом деле это язык на основе прототипов . У него нет классов или классического наследования, поэтому его не следует сравнивать с Java или C ++.

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

Давайте рассмотрим пример (используя sys.puts из node.js):

var external;
function foo() {
    external = Math.random() * 1000;
}
foo();

sys.puts(external);

Чтобы избавиться от глобальных побочных эффектов, мы можем заключить его в замыкание:

(function() {
    var external;
    function foo() {
        external = Math.random() * 1000;
    }
    foo();

    sys.puts(external);
})();

Обратите внимание, что мы не можем ничего сделать с external или foo вне области видимости. Они полностью завернуты в свое собственное закрытие, неприкасаемые.

Теперь, чтобы избавиться от побочного эффекта external:

(function() {
    function foo() {
        return Math.random() * 1000;
    }

    sys.puts(foo());
})();

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

Я также хочу отметить, что смешивание функционального программирования с объектами прекрасно. Возьмем для примера:

var Square = function(x, y, w, h) {
   this.x = x;
   this.y = y;
   this.w = w;
   this.h = h;
};

function getArea(square) {
    return square.w * square.h;
}

function sum(values) {
    var total = 0;

    values.forEach(function(value) {
        total += value;
    });

    return total;
}

sys.puts(sum([new Square(0, 0, 10, 10), new Square(5, 2, 30, 50), new Square(100, 40, 20, 19)].map(function(square) {
    return getArea(square);
})));

Как видите, использование объектов на функциональном языке может быть просто нормальным. В некоторых Лиспах даже есть вещи, называемые списками свойств, которые можно рассматривать как объекты.

Реальный трюк с использованием объектов в функциональном стиле - убедиться, что вы не полагаетесь на их побочные эффекты, а вместо этого рассматриваете их как неизменные. Самый простой способ - всякий раз, когда вы хотите изменить свойство, просто создайте новый объект с новыми деталями и передайте его вместо этого (этот подход часто используется в Clojure и Haskell).

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

5 голосов
/ 22 апреля 2010

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

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

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

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

Я стараюсь придерживаться следующих правил:

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

При выполнении этих правил можно получить ряд преимуществ:

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

  2. Надежность кода. Правильность кода легче рассуждать, если он менее сложный.

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

Обновление:

Включенное предложение от комментария.

Обновление 2:

Добавлены некоторые полезные ссылки.

2 голосов
/ 22 апреля 2010

Я думаю, http://documentcloud.github.com/underscore/ должно хорошо подходить для того, что вам нужно - он предоставляет наиболее важные функции высшего порядка для функционального программирования и не имеет клиентских функций для манипулирования DOM, которые вам не нужны для серверной части. Хотя у меня нет с этим опыта.

В качестве примечания: IMHO основной особенностью функционального программирования является Ссылочная прозрачность функции - результат функции зависит только от ее параметров - функция не зависит от изменений других объектов и не вносит никаких изменений кроме его значения результата. Это позволяет легко рассуждать о правильности программы и очень ценно для реализации предсказуемой многопоточности (если применимо). Хотя JavaScript не является языком ставок для FP - я ожидаю, что неизменяемые структуры данных будут очень дорогими в плане производительности.

0 голосов
/ 20 апреля 2010

Итак, 2 вещи, на которые стоит обратить внимание,

  1. В вашем первом примере ваша переменная не будет просачиваться в глобальную область, и именно так это и должно быть, старайтесь никогда не использовать переменные без их объявления, т.е. test = 'data' приведет к утечке данных в глобальная зона.

  2. Ваш второй пример также верен, bar1 и bar2 будут объявлены только для объекта Foo.

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

Если вы ищете среду разработки приложений, взгляните на ExtJs . Лично я думаю, что это идеально вписалось бы в модель, против которой вы пытаетесь развиваться. Просто помните, как работает их модель лицензирования, прежде чем вкладывать в нее значительные средства.

...