Metaprogramming
616 subscribers
103 photos
1 video
157 links
μετά- «между, после, через» (греч.)

Жизнь программиста за пределами программирования: алгоритмы, психология, инвестиции, иное.
Download Telegram
О замыканиях в JavaScript.

В JavaScript, на самом деле, не "настоящие" замыкания. Я бы даже сказал, что в JS нет замыканий, а есть некая отдалённо напоминающая их эмуляция.

Суть замыканий (closures) в двух вещах:
- в теле функции можно использовать переменные, которые определены до этой функции
- значения этих переменных "фиксируются" ("запираются") в момент определения функции

Посмотрим пример на Elixir-е — "настоящем" функциональном языке:

x = 2
plus_2 = fn(n) -> n + x end

plus_2.(10) # => 12

x = 3

plus_2.(10) # => 12

Итак, мы внутри функции plus_2 используем переменную x, которая определена до этой функции.

Прикол в том, что в момент определения функции значение этой переменной "фиксируется": внутри функции теперь это уже не какой-то там x, а конкретно оказавшаяся там двойка. На место свободной переменной x, фактически, подставляется конкретное содержащееся там в момент определения значение.

Поэтому в последней строке plus_2 всё также продолжает прибавлять двойку к своему единственному аргументу n, а не новое значение x. Исходное значение x "замкнулось" внутри функции.

В императивных языках, которые эмулируют эту функциональность (простите за тавтологию) функциональных языков, всё не так:

// js
let x = 2
plus_2 = n => n + x

plus_2(10) // 12

x = 3
plus_2(10) // 13

# ruby
x = 2
plus_2 = ->(n) { n + x }

plus_2.(10) # 12

x = 3
plus_2.(10) # 13

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

Конечно, одно из типовых использований замыканий — в "фабрике функций" (функциях, которые возвращают другие функции). И когда вы пишете код "внешней" функции (= самой фабрики), ничто не мешает тщательно контролировать самого себя на предмет того, какие переменные "утекают" во "внутреннюю" функцию (= результат работы фабрики, который будет возвращён коду, вызвавшему фабрику). И убедиться, что эти переменные никто не будет изменять вне внутренней функции, или их изменение учтено by design — например, с "неправильными" JS-замыканиями можно тривиально реализовать функцию, которая считает, сколько раз она была вызвана — а с "правильными" замыканиями это, фактически, невозможно:

// js

timesRunFactory = () => {
let count = 0;

return () => {
count++;
return count
}
}

timesRun = timesRunFactory();

timesRun(); // 1
timesRun(); // 2
...

#programming #fp
"Функциональное программирование" (ФП) - модный термин.

Автор имеет опыт разработки веб-приложений на Elixir-е (с использованием фреймворка Phoenix). Можно ли Elixir считать функциональным языком? Большинство отвечают, мол, конечно, это один из референтных представителей группы. Тот же JavaScript или Ruby могут назвать "языком с *элементами* ФП", но никому не придёт в голову назвать "полноценным" "функциональным языком".

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

В самом деле, ООП (объектно-ориентированный подход/программирование) зиждется на следующей формуле: "состояние" + "поведение". У нас есть некие автономные чёрные ящики (объекты), у которых в каждый момент времени есть некое внутреннее (не видимое снаружи) состояние и есть возможность получать сообщения.

Сейчас обычно говорят не "получать сообщения", а "вызывать метод". Впрочем, обратите внимание, что в Ruby проверка на то, существует ли у объекта метод, делается с помощью метода `respond_to?`— "отвечает ли?". Наследие Smalltalk, где объекты таки посылали друг другу именно "сообщения". В каком-нибудь Objective C (базовом языке для разработки под iOS) до сих пор официально "посылают сообщения".

Но, простите, а что наши разработанные на Elixir "процессы" делают, когда работают в экосистеме Erlang-а? Посылают друг другу сообщения. И так, Elixir-овский "процесс":
— имеет внутреннее состояние (минимальное разнообразие типовых способов работы с состоянием заложено в стандартную библиотеку, называющуюся OTP);
— отвечают на "сообщения".

QED, господа — Elixir это объектно-ориентированный язык (где роль "классов" играют "модули", роль "объектов" — "процессы", роль "методов" — сообщения и функции).

Отличие Elixir в том, что, когда вы пишете код, язык вас вынуждает явно себе представлять, как ваши объекты "раскладываются" по процессам (параллельным потокам выполнения) — какой объект живёт в каком "домике"-процессе на каком "островке"-сетевом узле. Традиционный объектно-ориентированный язык позволяет инкапсулировать даже сетевой вызов к REST-ресурсу, избаловывая программиста этими непрозрачными стенками абстракций.

#programming #fp #ruby #javascript #elixir
Элементы ФП в императивных языках

Мода на "функциональное программирование" задаётся, несомненно, как раз привнесением в более распространённые императивные языки пресловутых "элементов ФП".

Среди всех таких элементов можно (условно — реальная "генеалогия" чуть сложнее) выделить два класса:

1) Элемент LISP: лямбда-функции. В Ruby "лямбды" изначально были заложены в язык в виде "блоков кода" (конечно, в оригинальном LISP это сделано несколько более органично). Вслед за взрывным ростом популярности Ruby и большим признанием удобства использования "лямбд" со стороны прикладных программистов, стали потихоньку добавлять их и в другие языки: кажется, первым среди невымирающих динозавров была Java 8. Ну а теперь даже в C++ вы можете встретить нечто похожее.

2) Элемент LISP: итераторы. Аналогично с итераторами: map, select, reduce, zip, и т. д. и т. п. можно найти в любом уважающем себя современном языке. Популяризирует их в наше время JavaScript, в очередной ревизии которого они наконец появились, чтобы язык не выглядел совсем уж позорно на фоне любительской поделки фанатиков Rails — CoffeScript-а (как раз после появления "лямбд" и итераторов в JS "коффискрипт" и умер).

3) Элемент Haskell: "монады". Мало кто, в сущности, знает хоть что-то о системе категорий, но антураж "матана" привлекает неуверенных в себе программистов. Точно также, как психолог в связи с массовостью профессии и отсутствием профессиональных стандартов считается неполноценным психиатром, и программист на заре развития программирования мог бы считаться неполноценным математиком. В какой-то мере сохраняется подобное смутное чувство и сегодня. Из всего Haskell-а (этакого "языка программирования от математиков") выдёргивают обычно так называемые "монады". Элементарная, в сущности, концепция, которая на языке ООП описывалась бы простым интерфейсом контейнера с 3-8 методами, превращается в жупел, которым неполноценные математики, так и не сумевшие стать программистами, формируют неуверенность в своём интеллекте у программистов, которые не желают становиться математиками.

4) Элемент Haskell: pattern matching. Опять же, JavaScript популяризирует концепцию в наше время, переназвав её как "destructuring". На мой взгляд, впрочем, в таком терминологическом изменении нет ничего страшного: надо быть ближе к народу. Destructuring позволяет, например, прямо в сигнатуре функции (в перечислении аргументов) "выдернуть" ключ хеша или элемент массива из входящего параметра (таким образом "сопоставив", match, входящую структуру некоему "образцу", pattern). Ruby идёт с отставанием: на коленке сделанный case... in в свежей версии это хорошо, но мало: даёшь "перегрузку" функций в зависимости от структуры аргумента, как в том же Elixir.

А кроме этих четырёх пунктов, что-то больше ничего и не приходит на ум.

А вам каких "элементов ФП" не хватает в любимых языках?

#programming #fp #ruby #javascript #elixir