Жаргон функционального программирования

https://github.com/hemanth/functional-programming-jargon/blob/master/readme.md#applicative-functor
  • Перевод


У функционального программирования много преимуществ, и его популярность постоянно растет. Но, как и у любой парадигмы программирования, у ФП есть свой жаргон. Мы решили сделать небольшой словарь для всех, кто знакомится с ФП.


В примерах используется JavaScript ES2015). (Почему JavaScript?)


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


В документе используются термины из спецификации Fantasy Land spec по мере необходимости.


Arity (арность)


Количество аргументов функции. От слов унарный, бинарный, тернарный (unary, binary, ternary) и так далее. Это необычное слово, потому что состоит из двух суффиксов: "-ary" и "-ity.". Сложение, к примеру, принимает два аргумента, поэтому это бинарная функция, или функция, у которой арность равна двум. Иногда используют термин "диадный" (dyadic), если предпочитают греческие корни вместо латинских. Функция, которая принимает произвольное количество аргументов называется, соответственно, вариативной (variadic). Но бинарная функция может принимать два и только два аргумента, без учета каррирования или частичного применения.


const sum = (a, b) => a + b

const arity = sum.length
console.log(arity) // 2

// The arity of sum is 2

Higher-Order Functions (функции высокого порядка)


Функция, которая принимает функцию в качестве аргумента и/или возвращает функцию.


const filter = (predicate, xs) => {
  const result = []
  for (let idx = 0; idx < xs.length; idx++) {
    if (predicate(xs[idx])) {
      result.push(xs[idx])
    }
  }
  return result
}

const is = (type) => (x) => Object(x) instanceof type

filter(is(Number), [0, '1', 2, null]) // [0, 2]

Partial Application (частичное применение)


Частичное применение функции означает создание новой функции с пред-заполнением некоторых аргументов оригинальной функции.


// Helper to create partially applied functions
// Takes a function and some arguments
const partial = (f, ...args) =>
  // returns a function that takes the rest of the arguments
  (...moreArgs) =>
    // and calls the original function with all of them
    f(...args, ...moreArgs)

// Something to apply
const add3 = (a, b, c) => a + b + c

// Partially applying `2` and `3` to `add3` gives you a one-argument function
const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c

fivePlus(4) // 9

Также в JS можно использовать Function.prototype.bind для частичного применения функции:


const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c

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


Currying (каррирование)


Процесс конвертации функции, которая принимает несколько аргументов, в функцию, которая принимает один аргумент за раз.


При каждом вызове функции она принимает один аргумент и возвращает функцию, которая принимает один аргумент до тех пор, пока все аргументы не будут обработаны.


const sum = (a, b) => a + b

const curriedSum = (a) => (b) => a + b

curriedSum(40)(2) // 42.

const add2 = curriedSum(2) // (b) => 2 + b

add2(10) // 12

Auto Currying (автоматическое каррирование)


Трансформация функции, которая принимает несколько аргументов, в новую функцию. Если в новую функцию передать меньшее чем предусмотрено количество аргументов, то она вернет функцию, которая принимает оставшиеся аргументы. Когда функция получает правильное количество аргументов, то она исполняется.


В Underscore, lodash и ramda есть функция curry.


const add = (x, y) => x + y

const curriedAdd = _.curry(add)
curriedAdd(1, 2) // 3
curriedAdd(1) // (y) => 1 + y
curriedAdd(1)(2) // 3

Дополнительные материалы



Function Composition (композиция функций)


Соединение двух функций для формирования новой функции, в которой вывод первой функции является вводом второй.


const compose = (f, g) => (a) => f(g(a)) // Definition
const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage
floorAndToString(121.212121) // '121'

Purity (чистота)


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


const greet = (name) => 'Hi, ' + name

greet('Brianne') // 'Hi, Brianne'

В отличие от:



let greeting

const greet = () => {
  greeting = 'Hi, ' + window.name
}

greet() // "Hi, Brianne"

Side effects (побочные эффекты)


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


const differentEveryTime = new Date()

console.log('IO is a side effect!')

Idempotent (идемпотентность)


Функция является идемпотентной если повторное ее исполнение производит такой же результат.


f(f(x)) ≍ f(x)

Math.abs(Math.abs(10))

sort(sort(sort([2, 1])))

Point-Free Style (бесточечная нотация)


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


// Given
const map = (fn) => (list) => list.map(fn)
const add = (a) => (b) => a + b

// Then

// Not points-free - `numbers` is an explicit argument
const incrementAll = (numbers) => map(add(1))(numbers)

// Points-free - The list is an implicit argument
const incrementAll2 = map(add(1))

Функция incrementAll определяет и использует параметр numbers, так что она не использует бесточечную нотацию. incrementAll2 просто комбинирует функции и значения, не упоминая аргументов. Она использует бесточечную нотацию.


Определения с бесточечной нотацией выглядят как обычные присваивания без function или =>.


Predicate (предикат)


Предикат — это функция, которая возвращает true или false в зависимости от переданного значения. Распространенный случай использования предиката — функция обратного вызова (callback) для фильтра массива.


const predicate = (a) => a > 2

;[1, 2, 3, 4].filter(predicate) // [3, 4]

Categories (категории)


Объекты с функциями, которые подчиняются определенным правилам. Например, моноиды.


Value (значение)


Все, что может быть присвоено переменной.


5
Object.freeze({name: 'John', age: 30}) // The `freeze` function enforces immutability.
;(a) => a
;[1]
undefined

Constant (константа)


Переменная, которую нельзя переназначить после определения.


const five = 5
const john = {name: 'John', age: 30}

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


С константами из предыдущего листинга следующее выражение выше всегда будет возвращать true.


john.age + five === ({name: 'John', age: 30}).age + (5)

Functor (функтор)


Объект, который реализует функцию map, которая при проходе по всем значениям в объекте создает новый объект, и подчиняется двум правилам:


// сохраняет нейтральный элемент (identity)
object.map(x => x) === object

и


// поддерживает композицию
object.map(x => f(g(x))) === object.map(g).map(f)

(f, g — произвольные функции)


В JavaScript есть функтор Array, потому что он подчиняется эти правилам:


[1, 2, 3].map(x => x) // = [1, 2, 3]

и


const f = x => x + 1
const g = x => x * 2

;[1, 2, 3].map(x => f(g(x))) // = [3, 5, 7]
;[1, 2, 3].map(g).map(f)     // = [3, 5, 7]

Pointed Functor (указывающий функтор)


Объект с функцией of с любым значением. В ES2015 есть Array.of, что делает массивы указывающим функтором.


Array.of(1) // [1]

Lift


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


В некоторых реализациях есть функция lift или liftA2, которые используются для упрощения запуска функций на функторах.


const liftA2 = (f) => (a, b) => a.map(f).ap(b)

const mult = a => b => a * b

const liftedMult = liftA2(mult) // this function now works on functors like array

liftedMult([1, 2], [3]) // [3, 6]
liftA2((a, b) => a + b)([1, 2], [3, 4]) // [4, 5, 5, 6]

Подъем функции с одним аргументом и её применение выполняет то же самое, что и map.


const increment = (x) => x + 1

lift(increment)([2]) // [3]
;[2].map(increment) // [3]

Referential Transparency (прозрачность ссылок)


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


Например, есть функция greet:


const greet = () => 'Hello World!'

Любой вызов greet() можно заменить на Hello World!, так что эта функция является прозрачной (referentially transparent).


Lambda (лямбда)


Анонимная функция, которую можно использовать как значение.


;(function (a) {
  return a + 1
})

;(a) => a + 1

Лямбды часто передают в качестве аргументов в функции высокого порядка.


[1, 2].map((a) => a + 1) // [2, 3]

Лямбду можно присвоить переменной.


const add1 = (a) => a + 1

Lambda Calculus (лямбда-исчисление)


Область информатики, в которой функции используются для создания универсальной модели исчисления.


Lazy evaluation (ленивые вычисления)


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


const rand = function*() {
  while (1 < 2) {
    yield Math.random()
  }
}

const randIter = rand()
randIter.next() // Каждый вызов дает случайное значение, выражение исполняется при необходимости.

Monoid (моноид)


Объект с функцией, которая "комбинирует" объект с другим объектом того же типа. Простой пример моноида это сложение чисел:


1 + 1 // 2

В этом случае число — это объект, а + это функция.


Должен существовать нейтральный элемент (identity), так, чтобы комбинирование значения с ним не изменяло значение. В случае сложения таким элементом является 0.


1 + 0 // 1

Также необходимо, чтобы группировка операций не влияла на результат (ассоциативность):


1 + (2 + 3) === (1 + 2) + 3 // true

Конкатенация массивов — это тоже моноид:


;[1, 2].concat([3, 4]) // [1, 2, 3, 4]

Нейтральный элемент — это пустой массив []


;[1, 2].concat([]) // [1, 2]

Если существуют функции нейтрального элемента и композиции, то функции в целом формируют моноид:


const identity = (a) => a
const compose = (f, g) => (x) => f(g(x))

foo — это любая функция с одним аргументом.


compose(foo, identity) ≍ compose(identity, foo) ≍ foo

Monad (монада)


Монада — это объект с функциями of и chain. chain похож на map, но он производит разложение вложенных объектов в результате.


// Implementation
Array.prototype.chain = function (f) {
  return this.reduce((acc, it) => acc.concat(f(it)), [])
}

// Usage
;Array.of('cat,dog', 'fish,bird').chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird']

// Contrast to map
;Array.of('cat,dog', 'fish,bird').map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']]

of также известен как return в других функциональных языках.
chain также известен как flatmap и bind в других языках.


Comonad (комонада)


Объект с функциями extract и extend.


const CoIdentity = (v) => ({
  val: v,
  extract () {
    return this.val
  },
  extend (f) {
    return CoIdentity(f(this))
  }
})

Extract берет значение из функтора.


CoIdentity(1).extract() // 1

Extend выполняет функцию на комонаде. Функция должна вернуть тот же тип, что комонада.


CoIdentity(1).extend((co) => co.extract() + 1) // CoIdentity(2)

Applicative Functor (аппликативный функтор)


Объект с функцией ap. ap применяет функцию в объекте к значению в другом объекте того же типа.


// Implementation
Array.prototype.ap = function (xs) {
  return this.reduce((acc, f) => acc.concat(xs.map(f)), [])
}

// Example usage
;[(a) => a + 1].ap([1]) // [2]

Это полезно, когда есть два объекта, и нужно применить бинарную операцию на их содержимом.


// Arrays that you want to combine
const arg1 = [1, 3]
const arg2 = [4, 5]

// combining function - must be curried for this to work
const add = (x) => (y) => x + y

const partiallyAppliedAdds = [add].ap(arg1) // [(y) => 1 + y, (y) => 3 + y]

В итоге получим массив функций, которые можно вызвать с ap чтобы получить результат:


partiallyAppliedAdds.ap(arg2) // [5, 6, 7, 8]

Morphism (морфизм)


Функция трансформации.


Endomorphism (эндоморфизм)


Функция, у которой ввод и вывод — одного типа.


// uppercase :: String -> String
const uppercase = (str) => str.toUpperCase()

// decrement :: Number -> Number
const decrement = (x) => x - 1

Isomorphism (изоморфизм)


Пара структурных трансформаций между двумя типами объектов без потери данных.


Например, двумерные координаты можно хранить в массиве [2,3] или объекте {x: 2, y: 3}.


// Providing functions to convert in both directions makes them isomorphic.
const pairToCoords = (pair) => ({x: pair[0], y: pair[1]})

const coordsToPair = (coords) => [coords.x, coords.y]

coordsToPair(pairToCoords([1, 2])) // [1, 2]

pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2}

Setoid


Объект, у которого есть функция equals, которую можно использовать для сравнения объектов одного типа.


Сделать массив сетоидом:


Array.prototype.equals = (arr) => {
  const len = this.length
  if (len !== arr.length) {
    return false
  }
  for (let i = 0; i < len; i++) {
    if (this[i] !== arr[i]) {
      return false
    }
  }
  return true
}

;[1, 2].equals([1, 2]) // true
;[1, 2].equals([0]) // false

Semigroup (полугруппа)


Объект с функцией concat, которая комбинирует его с другим объектом того же типа.


;[1].concat([2]) // [1, 2]

Foldable


Объект, в котором есть функция reduce, которая трансформирует объект в другой тип.


const sum = (list) => list.reduce((acc, val) => acc + val, 0)
sum([1, 2, 3]) // 6

Type Signatures (сигнатуры типа)


Часто функции в JavaScript содержат комментарии с указанием типов их аргументов и возвращаемых значений. В сообществе существуют разные подходы, но они все схожи:


// functionName :: firstArgType -> secondArgType -> returnType

// add :: Number -> Number -> Number
const add = (x) => (y) => x + y

// increment :: Number -> Number
const increment = (x) => x + 1

Если функция принимает другую функцию в качестве аргумента, то ее помещают в скобки.


// call :: (a -> b) -> a -> b
const call = (f) => (x) => f(x)

Символы a, b, c, d показывают, что аргументы могут быть любого типа. Следующая версия функции map принимает:


  1. функцию, которая трансформирует значение типа a в другой тип b
  2. массив значений типа a,

и возвращает массив значений типа b.


// map :: (a -> b) -> [a] -> [b]
const map = (f) => (list) => list.map(f)

Дополнительные материалы



Union type (тип-объединение)


Комбинация двух типов в один, новый тип.


В JavaScript нет статических типов, но давайте представим, что мы изобрели тип NumOrString, который является сложением String и Number.


Операция + в JavaScript работает со строками и числами, так что можно использовать наш новый тип для описания его ввода и вывода:


// add :: (NumOrString, NumOrString) -> NumOrString
const add = (a, b) => a + b

add(1, 2) // Возвращает число 3
add('Foo', 2) // Возвращает строку "Foo2"
add('Foo', 'Bar') // Возвращает строку "FooBar"

Тип-объединение также известно как алгебраический тип, размеченное объединение и тип-сумма.


Существует пара библиотек в JavaScript для определения и использования таких типов.


Product type (тип-произведение)


Тип-произведение комбинирует типы таким способом, который вам скорее всего знаком:


// point :: (Number, Number) -> {x: Number, y: Number}
const point = (x, y) => ({x: x, y: y})

Его называют произведением, потому что возможное значение структуры данных это произведение (product) разных значений.


См. также: теория множеств.


Option (опцион)


Тип-объединение с двумя случаями: Some и None. Полезно для композиции функций, которые могут не возвращать значения.


// Naive definition

const Some = (v) => ({
  val: v,
  map (f) {
    return Some(f(this.val))
  },
  chain (f) {
    return f(this.val)
  }
})

const None = () => ({
  map (f) {
    return this
  },
  chain (f) {
    return this
  }
})

// maybeProp :: (String, {a}) -> Option a
const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key])

Используйте chain для построения последовательности функций, которые возвращают Option.



// getItem :: Cart -> Option CartItem
const getItem = (cart) => maybeProp('item', cart)

// getPrice :: Item -> Option Number
const getPrice = (item) => maybeProp('price', item)

// getNestedPrice :: cart -> Option a
const getNestedPrice = (cart) => getItem(obj).chain(getPrice)

getNestedPrice({}) // None()
getNestedPrice({item: {foo: 1}}) // None()
getNestedPrice({item: {price: 9.99}}) // Some(9.99)

Option также известен как Maybe. Some иногда называют Just. None иногда называют Nothing.


Библиотеки функционального программирования в JavaScript


Сколько терминов вам были известны до прочтения статьи?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 113
  • +3
    Очень странное определение категории. Морфизмы в катеориях это не всегда функции. И определенных правил всего три: Существование композиции, существование единицы и ассоциативность композиции.
    • +17
      Тут вообще очень много странных определений
    • +6
      Если реально хотите понять про ФП, то лучше почитайте книги о Haskell. Из русскоязычного настоятельно рекомендую http://anton-k.github.io/ru-haskell-book/book/toc.html тут и про ФП и про ТК есть отчасти достаточно интуитивно понятно (вообще это наверное лучшее, что я читал из бесплатных онлайн книг на русском языке)
    • +2
      Еще по поводу ТК https://www.youtube.com/watch?v=I8LbkfSSR58&list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_ и вот подборка по ФП https://github.com/xgrommx/awesome-functional-programming (welcome to PR)
      • 0

        Дополню, что у Bartosz Milewski помимо ТК, так же можно и по Haskell лекции посмотреть.

      • –16

        По-моему приверженцам "функционального" программирования чего-то в жизни не хватает.
        Вот объясните мне (простому приверженцу принципа "работа должна быть сделана качественно и в сроки") — какую ценность несёт, хотя бы и подразделение функций на унарные, бинарные, тернарные и т. д.?
        Сколько не читаю про так называемое ФП (функциональное программирование), так и не могу понять, что же в нём особенного? Функции используются практически в любом языке, практически в любой архитектуре. Нафига вводить еще один жаргон? Время девать некуда?

        • +4
          Узость мышления зачастую компенсируется яростностью отстаивания точки зрения.
          ФП помогает расширить интеллектуальный горизонт, например :) Как по мне, так хотя бы для этого оно нужно. Наверное.
          • +1
            То есть разложение на категории ради разложения и присвоение терминов ради присвоения — это и есть расширение мышления? По-моему это больше походит на подмену понятий, когда знание большого количества фактов пытаются выдать за умение мыслить.
            Поэтому повторю свой основной вопрос: нафига? Время девать некуда?

            И перенесу вопрос в другую плоскость: вот переписываемся мы с вами на великом и могучем. Я, честно признаюсь, давно уже забыл чем причастие отличается от деепричастия, что такое склонение, и многое другое, относящееся к терминам правил русского языка. Но это не мешает мне формулировать мысли, понимать вас, и вести беседу. Вопрос: как изменится моя речь, если я начну думать не о смысле сказанного, а о терминах?
            • +4
              "работа должна быть сделана качественно и в сроки"

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


              И для получения от ФП профита уже сегодня, совершенно не нужно знать что такое функторы, монады, моноиды и другие термины из Теории Категорий. Не ужели вы не используете лямбда функции и хотя бы такие паттерны как map, reduce, filter ?


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


              разложение на категории ради разложения и присвоение терминов ради присвоения

              • +5

                Есть такое слово: парадигма. Судя по вики это: "совокупность фундаментальных научных установок, представлений и терминов". Т.е. нечто большое, что может повлечь за собой смену образа мышления. Скажем есть декларативность и императивность. Согласитесь, это далеко не одно и тоже. И там и там большая мат. база. И чтобы этим жить "в полный рост" нужно эту базу понимать и уметь применять.


                Вот и с ФП такая же картинка. Если рассматривать отдельные кирпичики из него, возникнет недопонимание: зачем это может быть нужно? Всё так сложно и без явных преимуществ. Но кирпичики комбинируются, соединяются в причудливые схемы. И вот тогда программист пожинает profit. А так как область эта очень большая и развивается десятилетиями, да ещё и сугубо математична (а математика никогда не была особенно простой), то и нечему удивляться, что всё как-то абстрактно и сложно.

                • 0
                  Спасибо за ответ. Ваша точка зрения мне понятна.

                  Всё же останусь при своём. А именно: считаю чрезмерное использование терминов излишней тратой времени и ресурсов.
                  Безусловно, всем, представленным в статье, парадигмам должны быть обучены все, без исключения, программисты. Но не на уровне терминов, а на подсознательном уровне. Ровно так же, как и с языками, на которых мы общаемся.
                  • +3
                    Но не на уровне терминов, а на подсознательном уровне

                    Можно ли понять диффуры без подготовки в виде многих лет изучения алгебры? Думаю да. Но какой ценой.

                    • +1
                      Как же излагать свои мысли, не используя термины? Вот разговаривают Вася с Петей о функторе, но как он называется никто из них не знает. Тогда неизбежно Вася придумает свой термин — А, а Петя — Б. И вот как-то не очень у них разговор сложится то…
                      • 0

                        Запили эту фичу на вот той вот штуке… ну, ты понял, которая принимает один тип, трансформирует данные и возвращает другой тип… Ну мы такое делали уже в проекте X.


                        @greendimka


                        Но не на уровне терминов, а на подсознательном уровне.

                        Вот для этого, например.

                  • +5
                    Прошу заранее прощения за грубость и контрастность того, что будет ниже.

                    То есть разложение на категории ради разложения и присвоение терминов ради присвоения

                    Это зависит от точки зрения.

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

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

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

                    Вопрос: как изменится моя речь, если я начну думать не о смысле сказанного, а о терминах?


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

                    Аналогия с функциональным программированием как парадигмой ясна, я думаю?

                    P.S. Исправил разметку, но суть текста не менял.
                    • +1
                      Спасибо за хороший развёрнутый ответ.

                      P.S. жаль, что докапываться до истины всегда приходится сквозь шквал минусов и грубость
                      • +3
                        Да не за что.

                        Просто вы, в конфронтационной манере типировав, задели тех, для кого эта статья предназначалась. Реакция читателей, думаю, очевидна. Минус поставить проще, чем потратить время на ответ :)
                  • –1
                    Вы абсолютно неправы, а greendimka задал очень важный вопрос: зачем все эти термины и как они помогают программировать?
                    • –1
                      Этот вопрос можно спокойно задавать в любой другой статье по любой парадигме. А ответ везде будет один — «кому-то так удобнее»
                      • –1
                        это глупый ответ людей, которые не смогли до конца разобраться в том, что для них придумали.

                        Человек, который разобрался, даст условия и покажет: как и когда этим пользоваться.

                        Посмотрите Мартина Фаулера Рефакторинг кода: все приемы он сопровождает примерами кода в которых это будет удобно, причем к каждому примеру делает контрпример.
                        • +1
                          Вы путаете теплое с мягким, сравнивая целую книгу с небольшой публикацией.
                      • +3
                        По-моему, любому программисту ответ должен быть интуитивно понятен: без терминологии очень тяжело или невозможно разобраться с самими идеями, заложенными в статью, запись в блоге или целой книге (если она не написана для неподготовленного читателя). И именно эти идеи могут «помочь программировать».
                        • +4

                          Термины программировать не помогают, а вот концепции, которые называются этими терминами — очень даже.


                          Возьмём функтор. Например, есть значение какого-то типа, обёрнутое в какой-то другой тип (Optional, List, Array… — во что угодно). И у вас уже есть функция fun1, которую можно применить к тому типу данных, который находится внутри обёртки (назовём этот тип Int). Но эту функцию нельзя применить к типу Int, который находится в обёртке (скажем, эта обёртка Optional). А вам это нужно. И вот вы пишете функцию fun1Optional. Можете вы её написать, не зная термина "функтор"? Какие проблемы — конечно можете!


                          Но тут у вас возникает задача сделать так, чтобы функция fun2, работающая со значениями типа Int, тоже могла работать со значениями Optional Int. И чтобы функция fun3 тоже так могла работать. И fun15. И вот вы пишете функции fun2Optional, fun3Optionalfun15Optional… Можете? Конечно, почему нет? Не зная ни о какой концепции, называемой термином "функтор".


                          Но если вы знаете о такой концепции, то вместо того, чтобы переписывать вручную кучу функций, работающих с типом Int, чтобы они работали с типом Optional Int, вы просто делаете обёрточный тип Optional функтором, определяя для него всего одну функцию map (или fmap — это одно и то же), которая принимает в качестве первого аргумента функцию, работающую с типом Int, в качестве второго аргумента — значение Optional Int, и применяет эту функцию Int -> ВоЧтоТоЕщё (которая уже имеется у вас!) к значению типа Int внутри обёрточного типа Optional. В результате вам не надо писать кучу функций fun1Optionalfun15Optional, а достаточно написать одну единственную функцию fmap для обёрточного типа Optional. И наличие у обёрточного типа Optional функции fmap делает его функтором.


                          Более того: наличие функции fmap позволяет вам использовать уже имеющиеся у вас функции не только со значениями типа Optional Int, но и с Optional Float, и с Optional String, и с Optional Bool — и т.д. Просто потому, что вы сделали обёрточный тип Optional функтором — путём определения для него всего одной функции.


                          Можно ли программировать без знания концепции функтора? Конечно можно! Просто у вас будет меньше code reuse, больше определений новых функций, а ваш код будет сложнее читаться, рефакториться и дебажиться. Помогает ли знание концепции "функтор" программировать? По-моему, да.

                          • +2
                            Ерунда! Обобщение можно придумать не зная концепции. Ведь кто-то придумал это первым, когда не было еще ни концепции, ни терминологии! Или теперь всем, кроме первого, отказать в уме?!
                            Концепции и терминологии упрощают, систематизируют и ускоряют процессы и структуры.
                            • +2

                              Конечно можно придумать обобщение, не зная концепции. Можно и все алгоритмы самому придумать. Но обычно умные люди изучают то, что уже придумали другие люди, чтобы не изобретать велосипед. Это просто эффективнее. А придуманным алгоритмам и концепциям обычно дают имя, чтобы можно было быстро сказать собеседнику, о чём вы говорите, так, чтобы он понял, о чём идёт речь.

                              • 0
                                Думаю, речь об этом абзаце
                                Но тут у вас возникает задача сделать так, чтобы функция fun2, работающая со значениями типа Int, тоже могла работать со значениями Optional Int. И чтобы функция fun3 тоже так могла работать. И fun15. И вот вы пишете функции fun2Optional, fun3Optional… fun15Optional… Можете? Конечно, почему нет?

                                Это называется подтасовка, нет, я буду интуитивно использовать функтор, но не знать его определения. Мало того, использовать его или нет зависит от моих умственных способностей, а не от прочитанной книжки. Так что, в одиночку, формулировки не нужны. Вот для совместного программирования (> 1) помогают да, как и любые другие слова.
                      • 0

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


                        Но в данном случае вы выбрали неудачный объект для претензий. Разделение функций конечно существует, но большого значения ему не придается, по одной простой причине — в большинстве полноценных ФП языков достаточно иметь унарные функции, которые являются сущностью первого порядка, чтобы свести все остальные к ним. И это, кстати, является одним из достоинств — в ФП есть совсем немного фундаментальных сущностей, которые пришли как правило из математики, при этом все остальные сущности можно из них эффективно выразить.

                        • +2
                          Вот представьте у вас программа не на 2 экрана, а на сотни тысяч строк кода или даже на 1 млн строчек кода (не важно как много), которую писали много лет разные команды, разные люди, разной квалификации в разном настроении и т. д. и т. п… И вот нам бы хотелось как-то иметь возможность научится понимать какими свойствами обладает данная конкретная программа (a reasoning about the code): какой тип результата и какие сайд эффекты мы получим при её выполнении на конкретный / произвольных входных данных? остановится ли она вообще или зависнет в бесконечном цикле? запустит ли она «ракеты» в конце концов :-)? существуют ли вообще такие входные параметры при которых результат будет некорректен / программа упадёт в рантайме? и т. д. Чтобы достичь этой цели мы можем нанять команду математиков и за N лет они проведут формальную верификацию. Но это слишком долго и дорого. А можно поступить разумнее: а пусть верификацией занимается компилятор, пусть он просто запрещает компиляцию логически некорректных программ! И вот ФП это первый шаг к этой цели, призванный дать программистам и компилятору минимальные строительные блоки для этого. Возможно в будущем их назовут не монойдами и монадами, а как-то ещё (см thenCombineAsync в Java8). Не лишним будет упомянуть зависимые типы, когда вы можете указать, например, что функция принимает не прочто Int или Long, а Nat (натуральное число), а значит у вас будет невозможно деление на 0 (аналогично и для выхода за границу диапазаона и т. п.). В общем, высокоуровнево идея очень проста: пишем программу используя некий набор «паттернов», которые позволяют компилятору «ризонить» о вашем коде и компилировать его только тогда, когда он может доказать корректность последнего. Кстати, такой подход помимо корректности, также способен дать автопарралелизацию кода компилятором за бесплатно (сайд эффекты то закодированы в системе типов, а значит компилятор 100% знает, что можно распараллелить и каким подходом, а что нет и т. д. и т. п.). На текущий момент в промышленных языках пока реализовано мало что из этого, но глобальная идея именно такая!
                          • +1
                            Давайте говорить реально: программа на 1 млн строк кода написана на С++, Java или C#. Практически без вариантов.
                            • +1
                              Ок. пусть будет 50 тыс строк кода. Просто большая программа… Не важно на самом деле 20 K, 50 K срок или 1 млн. Тут важна сама концепция (a reasoning about the code).
                              • +2
                                Потому что программа на хаскеле будет существенно короче.

                                Серьёзно, по опыту переписывания прототипов с хаскеля на плюсы (из-за производительности) — код на плюсах получается минимум раза в три длиннее, с учётом всех возможных шаблонных выпендриваний, на которые я вообще способен. Если не выпендриваться, будет ещё в пару раз больше.
                                • 0
                                  тут нет никаких вопросов.

                                  Вопрос только в том, что программы на 1 млн строк кода существуют на С++, Java, C#, PHP, скорее всего Python. Остальные языки, при всей нашей радости от того же эрланга, к сожалению вызывают сомнение в этом (в существовании таких больших программ).

                                  • +1
                                    Я видел весьма большие финансовые системы на окамле.
                                    • +1
                                      к слову сказать, окамль действительно вызывает удивление. На нём существует написанная в одно лицо реализация латеха, которая умела прямую генерацию PDF сильно до того, как этому научился сам латех (название очень неправильное: ant). Пары фич не хватало, но в целом это был восхитительный пример того, как эзотерическая штука реально помогает жить.

                                      Правда, по причинам своей эзотеричности и замкнутости автора, оно вроде не пошло.

                                      Но у окамля есть ряд очень важных практических свойств: компилируемость и мутабельный доступ к структурам.
                                      • +2
                                        Хаскель тоже компилируемый. Мутабельные вычисления там тоже есть, причём, элегантно спрятанные, как раз чуть ниже об этом пишу.
                            • +2
                              Понимание полиморфизма (как в Haskell) и системы типов вообще помогает понять, как именно монада ST, требующая rank-2 polymorphism, гарантирует, что мутабельные вычисления не покинут runST, например.

                              Ну это из относительно недавнего.
                              • +2
                                а теперь можете пояснить свою интересную мысль без спец-терминов?
                                • +4
                                  Проще всего это будет сделать через объяснение спецтерминов, пожалуй.

                                  Впрочем, попробуем с другой стороны, rank-2 polymorphism и экзистенциальные типы позволяют написать такую сигнатуру:
                                  runST :: (forall s. ST s a) -> a
                                  

                                  То, что s стоит под forall и «внутри» первого аргумента (внутри скобочек, если хотите), а a — нет, означает, что результат runST не может зависеть от s (потому что код с типом forall s. ST s a должен тайпчекаться для любого s, но заданного окружением a). Поэтому, если весь ваш инструментарий по работе с мутабельными данными протегирован типом s (как, например, new :: ST s (h s k v) из пакета hashtables для создания мутабельной хешмапы), то мутабельные данные физически не могут убежать вне runST (попасть в возвращаемое значение, если хотите). Тайпчекер это гарантирует и доказывает.
                                  • +4
                                    большое спасибо за попытку донести.
                                  • +3

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

                                    • 0
                                      Монада State?)
                                      • +3

                                        ST (см. сообщение от 0xd34df00d выше). В State на самом деле ничего не мутирует, а только делает вид.

                                        • 0
                                          Да, там правда еще будет нужно Data.STRef. Ну State просто хранит предидущее и оперирует этим как парой.
                                  • 0
                                    Неуважаемый, не стоит называть меня ннвеждой, пусть и чужими словами, коли не поняли суть вопроса.
                                • +8
                                  Демонстрация парадигм ФП на котятах в Javascript-переложении, безусловно, хороша в пропагандистских и общеобразовательных целях, но, к сожалению, чревата импринтингом. Надо бы от этом в комментарию к переводу написать, т.к. монада, например, в общем случае, не есть контракт на chain/of в терминах реализации на Javacript.

                                  Несмотря на это, перевод стоит добавить в избранное как intelligence mock.
                                  • 0
                                    Что такое «intelligence mock»?
                                    • 0
                                      «Интеллектуальный троллинг» или «умное издевательство», но, с оттенками карательной серьёзности.
                                  • +2
                                    Раз уже речь зашла о ФП на примере JavaScript, хочу обратить внимание общественности на один замечательный проект:
                                    PureScript. Компилятор генерирует хороший человекочитаемый код.
                                    Очень похож (практически клон) на Haskell, однако есть некоторые ключевые отличия.
                                    Более глубоко можно ознакомиться в документе PureScript by Example.
                                    • +2

                                      Тогда можно еще и на Elm обратить внимание.

                                      • +1
                                        Тысячи их :) Но из всех них к терминам в статье ближе всего именно PureScript, т.к. он ближе всего к Haskell. Конечно, есть еще ghcjs, но это не особо прагматичный подход и тянет за собой большой рантайм (в первую очередь из-за сохранения семантики Haskell, а именно laziness). У PureScript вообще нет рантайма, его очень легко начать использовать в проекте (например, с webpack loader'ом) и сразу получить все плюсы чисто-функционального языка, не отказываясь при этом от богатой экосистемы Node.js (потому что у PureScript супер простой FFI). Что касается Elm, то это скорее «all-or-nothing» решение. Если захочется использовать third-party js библиотеки, то надо быть готовым к тому, чтобы городить огород кода (как js, так и elm). А еще, в отличие от PureScript, который уже достаточно стабилен (он все-таки копирует Haskell), Elm все еще развивается и буквально в недавнем релизе снова поменялся API. В общем, я считаю, что для продакшена он пока не готов, хотя идея довольно интересная. P.S.: Сам уже устал от js, «undefined is not a function», тонны рантайм ошибок, boilerplate'а из Immutable.js и пр., поэтому потратил довольно много времени на выбор альтернативы и остался с PureScript, т.к. это наиболее прагматичный из всех подход.
                                    • –2
                                      Смотрю многие хвалят функциональное программирование, посоветуйте как его можно приментить для инициализации UEFI. Подойдет любой вариант хоть для x86-64, хоть для ARM.
                                      • +11
                                        «Смотрю, многие хвалят метапрограммирование, паттерны проектирования, контейнеризацию, сервисно-ориентированную архитектуру, посоветуйте, как их можно применить для инициализации UEFI. Подойдёт любой вариант хоть для amd64, хоть для ARM.»

                                        Советовать сварщику варить металл кисточкой для рисования, я думаю, глупо. Глупо и сварщику о таком спрашивать.
                                        • +4

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


                                          Есть такая штука — SSIS. По жизни штуковина довольно сложная, и вызывающая множество проблем и боли у разработчиков, которые её пытаются использовать для каких-то прикладных задач (а она действительно бывает нужна, просто поверьте пока что на слово). Итак, разработчики плачут, колются, но продолжают пожирать кактус с полуимперативными действиями по деплойменту и настройке SSIS-пакетов, воюют с разными версиями IDE, серверов и прочей дребеденью.


                                          И тут появляется Chimayo. С использованием сравнительно небольшого количества паттернов функционального языка она позволяет описывать эти SSIS-задачи, формировать их в функциональном стиле, манипулировать как значениями первого класса, ну и всё такое. В общем, позволяет избавиться от боли и начать программистам, наконец, программировать.


                                          К чему я это сказал? Я думаю, что для вашей инициализации UEFI просто не написан (или пока что вам не встретился) подходящий декларативный инструмент, использующий современные достижения функционального программирования. Если б он был — поверьте, вам бы понравилось, как и нам понравилось работать с Chimayo после ада ручной настройки SSIS.

                                          • 0
                                            http://stackoverflow.com/a/6638207
                                            • +1
                                              Если не ошибаюсь, OpenFirmware в старых Маках и сантехнике использовало Лисп для загрузчика. Потенциальная выгода в том, что скрипт на Лиспе может быть автоматически верифицирован малой кровью (то есть можно, вместо порочной идеи распространения подписей неограниченному кругу китайцев с паяльниками, реально проверять загрузчик на допустимость выполняемых им операций).
                                            • +1
                                              «Глупо и сварщику о таком спрашивать. „

                                              Ну вот опять рабочий класс послали, а так хотелось приобщиться к прекрасному! Не “стрелять себе а ногу», манипулировать обьектами и функциональностью, наслаждаться шаблонами и паттернами.

                                              Увы и ах, ухожу плакать.
                                              • +4
                                                А почему вы уходите плакать? В каждом технологическом стеке есть слои, внутри которых существуют специализированные инструменты, зона применимости которых ограничена этими слоями и какой-то пограничной их областью. Узость пограничной область, в свою очередь, определяется обратной степенной функцией комбинаторной сложности проникающих между слоями решений. Это обуславливается накладными расходами компрессии механики описания прикладной области слоя (т.е. мощностью абстракции) и, соответственно, ресурсами, необходимыми для компенсации этих накладных расходов.

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

                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                  • +2
                                                    ФП хороши для преобразования данных. Собственно, функции в математическом смысле только и делают что отображают элементы одних множеств на элементы других множеств. Идеальная программа на ФП не содержит сайд-эффектов.

                                                    Напротив, вся работа железа на низком уровне построена именно на сайд-эффектах. Настолько, что это даже сносит крышу даже обычным С-компиляторам. Например как вам такое — вы пишете в область памяти одно число, а потом читаете оттуда другое? Или читаете два раза подряд из одной и тоже ячейки памяти и при этом получаете совершенно разные значения?
                                                    Это не глюк, так работает вся memory-mapped периферия. Приходится использовать хитрые конструкции чтобы сишные компиляторы не ломали работу с периферией своими оптимизациями. Более того, нынешние процессоры тоже шибко умные и выбрасывают лишние (по их мнениею) обращения к памяти. А что тогда говорить про языки более высокого уровня? Поэтому и нет применеия ФП в UEFI.

                                                    Зато, если мне надо будет превращать тысячи HTTP запросов в тысячи HTTP ответов — я вользу Эрланг. Потому что ключевое слово — «превращать». Отображать множество HTTP запросов на множество HTTP ответов.
                                                    • +2
                                                      Мне кажется, что то, как сейчас построена работа железа на низком уровне вовсе не означает, что применения ФП там нет и быть не может. Просто так сложилось — мы сделали императивное железо и начали писать императивный код. Но если применить принципы ФП в нужном месте, то можно получить большой плюс, даже в отношении железа. Я считаю, что яркий пример — шейдеры. Раньше компьютерная графика была довольно слабой, но распараллелив вычисления удалось достигнуть большого прироста производительности. А распараллелить удалось применив принципы ФП. Ведь шейдеры, по сути — функции без сайд-эффектов: мы передаем данные в вершинный шейдер, а результат вычисления в фрагментный шейдер. Так что, с «инерцией мышления» я очень согласен.
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        • 0
                                                          Да, шейдеры — отличный пример. Но опять же это пример вычислений. Отображение множеств геометрии и текстур в множество пикселей конечной сцены.

                                                          С другой стороны есть куча задач, где вычисления как таковые не нужны. А нужно просто в нужное время выставлять нужные биты в нужных местах. И ещё — быстро реагировать на изменения битов в других местах. Это приблизительно то, чем занимается приснопамятный UEFI.

                                                          Хотя, если подняться чуточку выше — появляются и задачи для ФП. Мне тут подумалось, что можно например реализовать USB стек на ФП языках. Да и вообще задачи современного прошивкописания всё больше тяготеют к массовой параллельной обработке данных.
                                                      • 0
                                                        Netflix полностью построили свой бизнес на RxJava и RxJS что используется у них в проде. От части в Rx есть некие элементы FP
                                                        • 0

                                                          Пример Clojure показывает что наличием должной инфраструктуры, популяризации должной не добиться. Там через interop в java, доступны все богатства и мощь JVM мира. Но как видим, только небольшая прослойка ценителей ФП осторожно работают с ним или со Scala. Так что инерция мышления, самый главный враг ФП.

                                                          • +1
                                                            Но как видим, только небольшая прослойка ценителей ФП осторожно работают с ним или со Scala.
                                                            Мне кажется, что тут дело в том, что большинство ценителей ФП на дух не переносят Java, включая JVM.
                                                            Это на самом деле странная была затея создать функциональный язык поверх императивной виртуальной машины, которая даже оптимизацию хвостовой рекурсии не поддерживает. Мало нам что-ли императивной процессорной архитектуры?
                                                            • +1

                                                              Просто нужно понимать, что без популярной платформы (JVM, JS) популярность clojure была бы в разы меньше. Как и затраты на реализацию языка. Авторам clojure "всего-лишь" пришлось имплементировать компилятор и стандартную библиотеку, все самое сложное взяла на себя JVM.

                                                              • 0
                                                                С одной стороны — да, а с другой, разве не логичнее было бы компилировать не в java-байт-код, а, к примеру, в Common Lisp?
                                                            • +1
                                                              Потому что задача ковертации видеопотоков более чистая и математичная, чем задача работы с железом.

                                                              Ну 80x86 всё очень правильно ответил, мне точно так же останется ответить тут, что ответы на ваши вполне корректные вопросы показывают несостоятельность местных икспертов по контейнеризации, и далее по тексту.
                                                              • +1
                                                                к слову сказать: в задаче конвертации (очень не люблю этот термин, поэтому заменю его транскодированием) видеопотоков к сожалению по большому счету очень мало места параллелизму (в стиле: 1000 медленных ядер). Нужен голый, беспощадный CPU с его хитростями типа SIMD.
                                                                • +1
                                                                  Теоретически чем больше у компилятора информации о том, что вы хотите сделать, тем более эффективный код он может выдать.

                                                                  На практике всё упирается в quality of implementation :(
                                                                  • +1
                                                                    есть такая долгая история про VLIW процессоры. Лет 20 минимум длится эта канитель со словами: мы сделали самый быстрый процессор, теперь надо только сделать к нему хороший компилятор, а вот компилятор как раз не получается сделать.

                                                                    В итоге самый быстрый как вчера, так и сегодня это интел, а всякие VLIW-ы остались на откуп спец-задачам и академикам.
                                                            • +1
                                                              А потому что для каждой задачи свой инструмент. Я вот тоже по работе занимаюсь тем, что битики флипаю. Но когда мне надо обработать мегабайт трейс-логов я достаю Питон, а не начинаю писать парсер на С. Угадайте, почему?
                                                            • 0
                                                              В школе нас учат арифметики, операции над числами что есть некое множество которое поддается законам моноида. Но если обобщить все это и перенести на различный уровень абстракций то это правило будет справедливо и для других абстракций. Вот в программировании ТК это есть некая категория типов, а морфизмы это ф-ции вида a->b. Также эти ф-ции являются моноидами ибо есть единичный элемент id = x->x и бинарный оператор композиция. Функтор это тоже морфизм просто между 2-категориями (грубо говоря если вы простой тип упакуете в другой, например int в array). Задача ТК научится манипулировать между абстракциями не взирая на содержимое. Если был массив то после морфизма это и должен быть массив. Не важно был это массив целых, а стал массив строк, но ведь условие сохранения внешней структуры осталось это все тот же массив. В древнем Египте все начиналось с того, что люди рисовали 10,20,300 различных объектов (людей, и прочее). Но вскоре они поняли, что всему этому можно придать некую абстрактность и что 2 человека + 1 человек будет 3 и это справедливо к пирамидам, золоту и тд. Далее понимается тот факт что помимо простой манипуляции это все можно объединить в некий класс (множество) и иметь все те же законы (вот например в школе мы учили всякие коммутативные, ассоциативные, дистрибутивные, левая/правая тождественность и прочее) И в чем парадокс, что все эти законы ложаться на любой уровень абстракции. Вот например для моноида работают законы ассоциативности, левой и правой тождественности ну иногда (например как с числами, а более того у них два бинарных оператора типа +/* и два возможных множества, это множество целых и натуральных чисел) закон коммутативности. Об этом можно продолжать еще долго, просто нужно понять одно те, маленькие кирпичики что мы знали раньше при определенных условиях мы можем применять на различных структурах, если поддать их неким общим математическим правилам и законам. И тогда (в отличии от императивных подходов) мы можем строго уверять, что поведение будет строгим и закономерным. Думаю все рассказать в одном комментарии трудно =)
                                                              • 0
                                                                const map = (fn) => (list) => list.map(fn)
                                                                const add = (a) => (b) => a + b

                                                                Совершенно непонятная запись. Что здесь вообще написано?
                                                              • 0
                                                                Вот что мне не нравится в ООП, так то, что там методы объектов имеют побочные эффекты (влияют на члены объекта).

                                                                Ну и вообще не люблю трехэтажные абстракции.

                                                                Сам я не фанат ни одной из парадигм.
                                                                Абстракции повышаю по мере надобности.
                                                                • –8
                                                                  Рахим, прости, но я поставил минус.

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

                                                                  Плюс это всё таки статья для тех, кто уже знает хаскель, а для них она мягко говоря бессмысленна, они уже итак всю терминологию знают.

                                                                  Хотите сделать статью не для любителей хаскеля — используйте ну хотя бы яваскрипт что ли, но ни в коем случае не хаскелевский синтаксис.
                                                                  • +6
                                                                    > используйте ну хотя бы яваскрипт что ли

                                                                    В статье все примеры на javascript.
                                                                    • –2
                                                                      const liftA2 = (f) => (a, b) => a.map(f).ap(b)


                                                                      Да, конечно
                                                                      • +5
                                                                        Это действительно JavaScript. Правда, в примере ошибка, но не в синтаксисе.
                                                                        https://habrahabr.ru/company/plarium/blog/270353/
                                                                        • –2
                                                                          тихий ужас
                                                                          • +1
                                                                            И правда, так же намного нагляднее
                                                                            var liftA2 = function(f){
                                                                                return function(a, b){
                                                                                    return a.map(f).map(function(a){
                                                                                         return a(b);
                                                                                    })
                                                                                }
                                                                            }
                                                                            liftA2 = "haha";
                                                                            var result = typeof(liftA2) === "string" ? liftA2 : liftA2([1,2],[3,4]);
                                                                            
                                                                            • –2
                                                                              Да, так сильно нагляднее для тех, кто привык к меньше экстраваганщине.

                                                                              Только у вас внутри a перекрывается, что делает код не очень наглядным.

                                                                              А зачем вторая часть текста с заменой функции на строку я не понял.

                                                                              И плюс опять возвращаемся назад: зачем этот вычурный кусок кода? что он делает и сколько сотен строк более простого кода он экономит?
                                                                              • +3
                                                                                Вторая часть демонстрирует целесообразность введения const, как следствие решения двух проблем:
                                                                                1. Оптимизация компилятора. Вместо этой переменной всегда можно подставить ее значение, что на больших объемах дает существенный выигрыш.
                                                                                2. Утиная типизация иногда удобна, иногда крайне опасна, к тому же требует вот таких проверок. const решает вопрос указанием типа при первоначальном и единственном присвоении значения.

                                                                                Это экстраваганщина, потому что вы первый раз увидели стрелочку или обожаете return? Завтра привыкнете, не переживайте. Ведь к `map`, который появился совсем недавно, вы претензий не предъявляете и не пробегаете массив циклом.

                                                                                Конкретно этот кусок делает функцию liftA2 независимой от функции f, которую мы передаем. Это дает возможность использовать ее многократно с разными функциями, облегчает тестирование в разы, а значит и ускоряет написание кода. Хотя, подозреваю, до TDD вы еще тоже не добрались. Тогда, предупреждая вопрос, скажу. TDD нужно не для тестирования и даже не для описания требований, это приятный побочный эффект, но достигается и другими вещами. TDD предполагает разбиение кода на мельчайшие детали, проработку каждой из них в отдельности и доказательство поставленных функций. Чаще всего, программа не запускается целиком вообще до описания всех unit'ов. Зато внесение изменений всегда предсказуемо и однозначно. Если код поменяли в этом unit'е, то раз его тест не проходит, его и чинить (все зависимости его замоканы).
                                                                                Так вот, функциональное программирование в разы облегчает создание таких кусочков, чистых функций. Нужные зависимости передаются, как параметры или каррируются и т.д.
                                                                                В статье продемонстрированы инструменты такого подхода, на примере JavaScript.
                                                                                • –3
                                                                                  Расскажите пожалуйста про свой опыт использования ФП на работе. Какого размера проекты вы поддерживали и в течении скольких лет?
                                                                                  • –2
                                                                                    Я могу рассказать про ваш опыт использования ФП на работе. Вы используете ФП и TDD всегда и везде, просто не зная терминологии, не используя современные инструменты и избегая вникания в суть, делаете это чуть менее эффективно.
                                                                                    Например, запуская программу целиком в командной строке или в браузере после каждого изменения и любуясь на ее работу.
                                                                                    Или доказывать то, что сам JS намного более функциональный язык, чем императивный и не надо, с этим никто не спорит.
                                                                                    А может быть вы использовали Angular с его декларативными шаблонами и DI? Или React с pure-компонентами? Нет? Ну lambda-функции и функции первого порядка в чистом виде ничем не хуже.
                                                                                    Вот в QBasic с этим были сложности, конечно.
                                                                                    Размер же проекта и необходимость его поддержки годами тоже определяется качественным кодом, иногда достаточно зайти на github раз в год и обновить dependencies у библиотек. А полезного, бизнес-кода настолько мало, насколько это возможно и его поддержка легка.
                                                                                    А иногда целыми днями сидишь и пилишь монолит на Swing, Laravel, Rails, когда регрессионные тесты вручную — основный вид деятельности.
                                                                                    • 0
                                                                                      Спасибо, но я обойдусь без вашего рассказа про то, как мы используем ФП.

                                                                                      Мы зарабатываем деньги, программируя на Эрланге в то время, как вы занимаетесь неизвестно чем и мне достаточно неприятно, когда какой-то аноним считает возможным меня поучать.
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                    • +2
                                                                      Каррирование — преобразование сигнатуры функции, для получения результата нужно передать то же количество аргументов. После каррирования получается функция, которая принимает часть аргументов и возвращает новую функцию, которая принимает другую часть аргументов и возвращает…

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

                                                                      Каррирование и частичное применение связаны непосредственно друг с другом, поэтому это нормально, что не видно сначала разницы.
                                                                      • 0
                                                                        насколько понял из статьи, каррированная функция всегда принимает строго один аргумент и возвращает либо результат, либо функцию для следующего аргумента, а частично примененная функция просто принимает меньшее число аргументов чем изначальная функция и сразу возвращает результат.
                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                          • 0
                                                                            Судя по всему каррированием обозвали рефакторинг, а автоматическое каррирование это как в Haskell
                                                                            • 0
                                                                              А что за автоматическое каррирование как в хаскеле? Тут просто идиоматично писать функции, которые возвращают функции. Никто вам не мешает написать map :: (a -> b, [a]) -> [b], и компилятор тут ничего автоматически каррировать не будет.
                                                                      • 0

                                                                        Каррирование – способ представить функцию многих переменных в виде набора унарных. Частичное применение – способ получить функцию N переменных из функции N+K переменных, «зафиксировав» значения этих K. Они связаны только в том плане, что каррированные функции очень легко частично применять (передали только один аргумент – вуаля, у нас новая функция); но это вовсе не обязательное требование – см. C++-ный std::bind, например.

                                                                      • –2
                                                                        Тихий ужас. Я мало что понял из определений. Я подавлен. Удручён. У меня слов нет.

                                                                        Я ещё никогда не чувствовал себя настолько дезориентированным.
                                                                        Как я пользовался JS все эти годы без этих определений?

                                                                        Или вопрос по-другому: зачем вы напомнили мне опять про ВУЗ и академические знания? :)
                                                                        • +3
                                                                          С функциональщиками лучше не спорить :)
                                                                          • –3
                                                                            Да ладно, что уж тут поделать.
                                                                            • –2
                                                                              это не функциональщики, а теоретики. Постят черти что в интернете, а потом идут на работу и пишут на своей яве.
                                                                            • +3
                                                                              «…Сорок с лишком лет говорю прозой — и невдомек!»
                                                                              • +1

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

                                                                              • +8
                                                                                Option (опцион)

                                                                                Опцион, вы серьезно?

                                                                                • –5
                                                                                  всё, к черту, сюда налетели оголтелые теоретики. От таких для ФП главный вред.
                                                                                  • 0
                                                                                    Higher Order Function — функция высшего(а не высокого) порядка.
                                                                                    • 0
                                                                                      Foldable — свертываемый.
                                                                                      • +2
                                                                                        >константа — это переменная
                                                                                        • 0

                                                                                          Свобода – это рабство!

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