Без Рамды ...
Я бы написал это, используя простую рекурсию -
const always = x =>
_ => x
const identity = x =>
x
const veither = (f = identity, ...more) => (...args) =>
more.length === 0
? f (...args)
: f (...args) || veither (...more) (...args)
const test =
veither
( always (0)
, always (false)
, always ("")
, always (1)
, always (true)
)
console .log (test ())
// 1
Но это еще не все ...
R.either
должна быть одной из самых эксцентричных функций в библиотеке Ramda. Если вы внимательно прочитаете документацию , R.either
имеет два (2) варианта поведения: он может вернуть -
функция, которая передает свой аргумент каждой из двух функций f
и g
и возвращает первое истинное значение - g
будет не оценивать, если результат f
правдив.
Или аппликативный функтор
В подписи R.either
написано -
either : (*… → Boolean) → (*… → Boolean) → (*… → Boolean)
Но это определенно немного обманывает. Для наших двух вышеописанных случаев следующие две подписи гораздо ближе -
// variant 1
either : (*… → a) → (*… → b) → (*… → a|b)
// variant 2
either : Apply f => f a → f b → f (a|b)
Давайте подтвердим эти два варианта простыми тестами -
Удвоить ...
Теперь давайте сделаем супер-мощный veither
, который предлагает ту же двойную возможность, что и R.either
-
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
// variant 1
? (...args) =>
f (...args) || veither (...more) (...args)
// variant 2
: liftN (more.length + 1, vor) (f, ...more)
Он работает так же, как R.either
, за исключением того, что теперь он принимает два или более аргумента. Поведение каждого варианта поддерживается -
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
test () // => "fn"
// variant 2 returns an applicative functor
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
// => Just { "ap" }
Вы можете просмотреть источник для R.either
и сравнить его с veither
выше. Неторопливый и рестайлинговый, вы можете увидеть его много общего здесь -
// either : (*… → a) → (*… → b) → (*… → a|b)
// either : Apply f => f a -> f b -> f (a|b)
const either = (f, g) =>
isFunction (f)
// variant 1
? (...args) =>
f (...args) || g (...args)
// variant 2
: lift (or) (f, g)
Разверните фрагмент ниже, чтобы проверить результаты в вашем собственном браузере -
const { always, either, liftN } =
R
const { Just, Nothing } =
folktale.maybe
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
? (...args) =>
f (...args) || veither (...more) (...args)
: liftN (more.length + 1, vor) (f, ...more)
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
задним числом и предвидением ...
С помощью одной маленькой уловки мы можем пропустить всю церемонию рассуждений о наших собственных veither
. В этой реализации мы просто совершаем повторяющийся вызов R.either
-
const veither = (f, ...more) =>
more.length === 0
? R.either (f, f) // ^_^
: R.either (f, veither (...more))
Я покажу вам это, потому что он хорошо работает и сохраняет поведение обоих вариантов, но этого следует избегать, потому что он создает гораздо более сложное дерево вычислений. Тем не менее, раскройте фрагмент ниже, чтобы убедиться, что он работает -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
const veither = (f, ...more) =>
more.length === 0
? either (f, f)
: either (f, veither (...more))
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>