Рамда с несколькими параметрами и условиями - PullRequest
0 голосов
/ 23 января 2019

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

const isNotNil = R.complement(R.isNil);
const isFunction = R.is(Function)

const a = (value, value2, fn) => {
   if (isNotNil(value)) {
     return isFunction(fn) ? fn(value) : value
   }
   return value2;
};

a(5,2, R.add(8))

Ответы [ 4 ]

0 голосов
/ 25 января 2019

Вы в основном изобретаете тип Option ;иногда называется Может быть .Используя простую библиотеку тегов, такую ​​как daggy, мы можем реализовать Option -

const daggy = require('daggy')

const Option = daggy.taggedSum('Option', {
  Some: ['x'],
  None: [],
})

const { Some, None } = Option

Option.prototype.map = function(f) {
  return this.cata({
    Some: x => Some(f(x)),
    None: _ => this
  })
}

const add = x => y => x + y

console.log
  ( Some(1).map(add(10)) // Some(11)
  , None.map(add(10)) // None
  )

Мы можем добавить withDefault метод, который позволяет нам вернуться к нормальному значению -

Option.prototype.withDefault = function (x) {
  return this.cata({
    Some: x => x,
    None: _ => x
  })
}

console.log
  ( Some(1).map(add(10)).withDefault(0) // 11
  , None.map(add(10)).withDefault(0) // 0
  )

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

Option.fromNullable = function (x) {
  if (x == null)
    return None
  else
    return Some(x)
}

console.log
  ( Option.fromNullable(1).map(add(10)).withDefault(0) // 11
  , Option.fromNullable(null).map(add(10)).withDefault(0) // 0
  )

Если вам все еще нужно представить это выражение как функцию, например a в вашемвопрос -

const a = R.curry((f, x, y) =>
  Option.fromNullable(x).map(f).withDefault(y))

console.log
  ( a (R.add(8), 5, 2) // 13
  , a (R.add(8), null, 2) // 2
  )

Рамда не имеет встроенной опции или Возможно , но если вы ищете существующую реализацию, есть популярные модулина нпм как данные / возможно .

0 голосов
/ 23 января 2019

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

Зная это, ваша подпись функции должна выглядеть примерно так: a(defaultValue, func, value)

Это позволяет вам строить карри функции с предопределенным поведением:

const incOr42 = a(42, inc);
incOr42(1);    // 2
incOr42(null); // 42

Теперь давайте разберем проблему на более мелкие части:

Во-первых, давайте проверим func, это может быть сама функция:

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

const checkFn = unless(is(Function), always(identity));
checkFn(add(8))(5); // 13
checkFn('foo')(5);  // 5

Во-вторых, давайте создадим функцию, которая принимает два параметра: defaultValue и func. он возвратит функцию, которая принимает value и возвращает либо defaultValue, если value равен нулю, либо применяет func к нему в противном случае:

const a = useWith(ifElse(isNil), [always, checkFn]);
const incOr42 = a(42, inc);
incOr42(1);    // 2
incOr42(null); // 42

Собираем все вместе:

const {unless, is, always, identity, ifElse, isNil, useWith, inc} = R;

const checkFn = unless(is(Function), always(identity));
const a = useWith(ifElse(isNil), [always, checkFn]);

const incOr42 = a(42, inc);

console.log(incOr42(1));
console.log(incOr42(null));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
0 голосов
/ 23 января 2019

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

Эта версия, кажется, делает именно то, что вы хотите, и вполне читабельна:

const {is, isNil} = R

const a = (val, val2, fn) => isNil(val) ? val2 : is(Function, fn) ? fn(val) : val

console.log(a(5, 2, R.add(8)))       //=> 13
console.log(a(5, 2, 'NonAFunction')) //=> 5
console.log(a(null, 2, R.add(8)))    //=> 2
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
0 голосов
/ 23 января 2019

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

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

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

const { toMaybe, maybe, pipe } = sanctuary

const a = f => value => value2 => pipe ([
    toMaybe, // converts to Just (value) or Nothing, when nil
    maybe (value2) (f) // if Nothing, value2, otherwise, value is passed to f
]) (value)

// Output: 3
const output1 = a (x => x + 1) (2) (4)
console.log ('output1: ', output1)

// Output: 4
const output2 = a (x => x + 1) (null) (4)
console.log ('output2: ', output2)
<script src="https://bundle.run/sanctuary@0.15.0"></script>

Обратите внимание, что я не проверял, что f является Function.JavaScript - это динамически типизированный язык: просто предположим, что он будет Function, и быстро провалится, если это не так.

Рефакторированный подход для использования преимуществ частичного применения:

const { toMaybe, maybe, pipe } = sanctuary

// Re-arranged parameters: now value is the latest one.
const a = f => value2 => pipe ([
    toMaybe, // converts to Just (value) or Nothing, when nil
    maybe (value2) (f) // if Nothing, value2, otherwise, value is passed to f
])

// Output: 3
const output1 = a (x => x + 1) (4) (2)
console.log ('output1: ', output1)

// Output: 4
const output2 = a (x => x + 1) (4) (null)
console.log ('output2: ', output2)
<script src="https://bundle.run/sanctuary@0.15.0"></script>
...