Я не уверен, что вы можете легко комбинировать бесточечный стиль и неунарную арность. Сначала подумайте, каким должен быть тип результирующих и составных функций:
// Compose: (B -> C) -> (A -> B) -> A -> C
const compose = f => g => x => f(g(x))
// Add: A -> A -> A
const add = x => y => x + y
// Mul: A -> A -> A
const mul = x => y => x * y
// Add1: A -> A
const add1 = add(1)
// Add1AndMul: A -> A -> A
// because:
// Add1: A -> A
// Mul: A -> A -> A
const add_1_and_mul = compose(mul)(add1)
// Mul4: A -> A
const mul_4 = add_1_and_mul(3)
const result = mul_4(5) //> 20
У Ramda есть uncurryN
, так что вы можете обернуть его вокруг compose
и избавиться от каррирования результирующей функции.
const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20
Чтобы добавить еще одну функцию в «цепочку», вам нужно скомпоновать ее с предыдущей функцией.
// Add1AndMul: A -> A -> A
const add1_mul = compose(mul)(add1)
Это наша желаемая подпись.
// 1 2 3
// Add1AndMulAndAdd: A -> A -> A -> A
// which is: | | |
// Add1: A -> A | |
// Mul: A -> A -> A |
// Add: A -> A -> A
Так что каким-то образом мы должны пройти эти A2 и A3 без каких-либо «очков». Давайте попробуем простую композицию и проанализируем ее:
let add1_mul_add = compose(add)(add1_mul)
Запомните подпись композиции: (E -> F) -> (D -> E) -> D -> F
! Разбираем по шагам:
Мы поставляем нашу сигнатуру функции add
вместо (E -> F)
(E -> F )
(A -> A -> A)
Мы заключаем, что
E = A
F = A -> A
То же самое делаем с (D -> E)
и add1_mul
(D -> E )
(A -> A -> A)
Мы заключаем, что
D = A
E = A -> A
Но мы уже видим здесь противоречие! Вывод на шаге 2 противоречит выводу на шаге 1: E
не может быть A
и A -> A
одновременно.
Поэтому мы не можем составить add
и add1_mul
и наш add1_mul_add
выдаст ошибку.
Давайте попробуем обойти проблему и исправить ее, нарушив наше обещание бесточечного стиля.
const add1_mul_add = x => compose(add)(add1_mul(x))
Я собираюсь нарушить некоторые правила и смешать подписи с кодом, чтобы проиллюстрировать свою точку зрения:
x -> (A -> A -> A) -> (x -> A -> A) -> A -> A -> A
||
\/
x -> (A -> A -> A) -> (A -> A) -> A -> A -> A
(E -> F ) -> (D -> E) -> D -> F
Итак, мы получили нашу правильную подпись! Как избавиться от переменной x
, чтобы вернуться к pointfree? Мы можем попытаться найти очевидные закономерности, например... нашу старую композицию функций!
f(g(x)) => compose(f)(g)
И мы находим эту закономерность в нашем новом add1_mul_add
-
f = compose(add)
g = add1_mul
f(g(x)) = compose(add)(add1_mul(x))
И сводим его к бесточечному и получаем нашу новую функцию add1_mul_add
:
const add1_mul_add = compose(compose(add))(add1_mul)
Но эй - мы можем уменьшить его еще больше!
const add1_mul_add = compose(compose)(compose)(add)(add1_mul)
И там мы нашли то, что уже существует в haskell под названием The Owl
.
Мы можем определить его в Javascript просто как:
const owl = compose(compose)(compose)
Но теперь с каждой новой функцией в цепочке вам придется создавать оператор совы более высокого порядка.
const owl2 = compose(compose)(owl)
const add1_mul_add_mul = owl2(mul)(add1_mul_add)
const owl3 = compose(compose)(owl2)
const add1_mul_add_mul_add = owl3(add)(add1_mul_add_mul)
Поэтому я действительно рекомендую, чтобы ваши функции были унарными в бесточечном стиле. Или используйте другие конструкции, такие как списки:
const actions = [ add, mul, add, mul ]
const values = [ 1, 2, 3, 4 ]
const add_mul_add_mul = (...values) => zip(actions, values).reduce((acc, [action, value]) => action(acc, value), 0)
person
Are
schedule
10.10.2017
multiply(add(1, x), y)
вместоmultiply(y, add(1, x))
:R.compose(R.multiply, R.add(1))
- person Bergi   schedule 10.10.2017