没有 Ramda ...
我可能会用简单的递归来写这个 -
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 库中比较古怪的函数之一。如果您仔细阅读documentation,R.either 有两 (2) 个行为变体:它可以返回 -
一个函数将其参数传递给f 和g 这两个函数中的每一个,并返回第一个truthy 值 - 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)
让我们通过简单的测试来确认这两个变种 -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
// variant 1 returns a function
const test =
either
( always (0)
, always (true)
)
console.log(test()) // => true
// variant 2 returns an applicative functor
const result =
either
( Just (false)
, Just (1)
)
console.log(result) // => Just { 1 }
<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 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" }
您可以将view the source 替换为R.either,并将其与上面的veither 进行比较。 Uncurried 和 restyled,你可以在这里看到它的许多相似之处 -
// 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)
展开下面的sn-p,在自己的浏览器中验证结果-
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))
我向您展示这个是因为它工作得很好并且保留了两种变体的行为,但应该避免它,因为它构建了一个更复杂的计算树。不过,展开下面的 sn-p 以验证它是否有效 -
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>