[英]Practical use of K-combinator (Kestrel) in javascript
The K-combinator can be implemented as below and the implementation should not have any side-effects. K-combinator 可以如下实现,并且该实现应该没有任何副作用。
const K = x => y => x;
It is sometimes called "const" (as in Haskell).它有时被称为“const”(如在 Haskell 中)。 The K function might be defined as, "takes a value and returns a (constant) unary function that always returns that value."
K 函数可能被定义为“接受一个值并返回一个始终返回该值的(常量)一元函数。”
When is it useful?什么时候有用? Please help me with practical examples.
请帮助我提供实际示例。
Sort of an broad question, but it's nice, I like it. 一个广泛的问题,但它很好,我喜欢它。
To support my example, in this answer I'm going to implement … 为了支持我的例子,我将在这个答案中实施......
abuild :: Number -> (Number -> a) -> [a]
… which as the type suggests, takes a number and a function to build an array. ...根据类型的建议,需要一个数字和一个函数来构建一个数组。 This could be useful if you want to build an array of a known size based on some computation.
如果要根据某些计算构建已知大小的数组,这可能很有用。
Let's build an array with 5 elements using the identity function, id
. 让我们使用身份函数
id
构建一个包含5个元素的数组。 As you can see, a sequential numerical index starting with 0 is given to your builder function 如您所见,为构建函数提供了以0开头的顺序数字索引
abuild (5) (id) // => [0,1,2,3,4] abuild(5)(id)// => [0,1,2,3,4]
Let's do something mathy with the builder this time. 让我们这次与建造者做些蠢事。 We'll square the input.
我们将对输入进行平方。 Very advanced.
非常先进。
abuild (5) (x=> x * x)
// => [0,1,4,9,16]
Or maybe we don't care about the input. 或者也许我们不关心输入。 I always love a good laugh.
我总是喜欢开怀大笑。 I laugh at things constantly.
我经常嘲笑事物。 One could say I
K('ha')
… 有人可以说我
K('ha')
......
abuild (5) (K('ha'))
// => ['ha','ha','ha','ha','ha']
Boom ! 轰! Pretty useful, right?
非常有用,对吗? That's
K
那是
K
Implementation 履行
Go ahead and run it to see K
in action! 继续运行它,看看
K
在行动!
// id :: a -> a const id = x=> x // K :: a -> b -> a const K = x=> y=> x // add :: Number -> Number -> Number const add = x=> y=> y + x // reduce :: (a -> b) -> b -> [a] -> b const reduce = f=> y=> ([x,...xs])=> { if (x === undefined) return y else return reduce (f) (f (y) (x)) (xs) } // map :: (a -> b) -> [a] -> [b] const map = f=> reduce (xs=> x=> [...xs, f(x)]) ([]) // iterate :: Number -> (a -> a) -> a -> [a] const iterate = n=> f=> x=> n > 0 ? [x, ...iterate (n - 1) (f) (f(x))] : [] // abuild :: Number -> (Number -> a) -> [a] const abuild = n=> f=> map (f) (iterate (n) (add (1)) (0)) console.log(abuild (5) (id)) // => [0,1,2,3,4] console.log(abuild (5) (x=> x * x)) // => [0,1,4,9,16] console.log(abuild (5) (K('ha'))) // => ['ha','ha','ha','ha','ha']
The problem with K
as with all primitive combinators is that you can't consider it on its own. 与所有原始组合子一样,
K
的问题在于你不能单独考虑它。 Primitive combinators are the fundamental building blocks of functional programming. 原始组合器是函数式编程的基本构建块。 You need a proper context to watch them at work.
你需要一个适当的环境来观察他们的工作。 The challenge is to grok this context, if you are new to the functional paradigm.
如果您不熟悉功能范例,那么挑战在于了解这一背景。
Here is a "typical context": Option
. 这是一个“典型背景”:
Option
。 Instances of the Option
type are like values that may be null
, but never throw an error when applied to a function: Option
类型的实例类似于可以为null
,但在应用于函数时从不抛出错误:
// the option type const Option = { some: Symbol.for("ftor/Option.some"), none: Symbol.for("ftor/Option.none"), of: x => factory(Option.some) (x), cata: pattern => o => pattern[o.tag](ox), fold: f => g => o => Option.cata({[Option.some]: f, [Option.none]: g}) (o), map: f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o) // ^^^^ } // a generic map function const map = type => f => o => type.map(f) (o); // functor factory const factory = tag => value => ( {x: value === undefined ? null : value, tag: tag} ); // auxiliary functions const K = x => y => x; const sqr = x => x * x; // a few data to play around const o = factory(Option.some) (5); const p = factory(Option.none) (); // and run let r1 = map(Option) (sqr) (o); let r2 = map(Option) (sqr) (p); console.log("map over o", r1); console.log("map over p", r2);
What does K
do in this implementation? K
在这个实现中做了什么? Let's examine the crucial line: 让我们来看看关键线:
f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o)
Option.fold
expects two functions. Option.fold
需要两个函数。 The first passed function x => Option.of(f(x))
is for the some
case (there is a value). 第一个传递函数
x => Option.of(f(x))
用于some
情况(有一个值)。 The second K(o)
for the none
case (there is no value). none
例子的第二个K(o)
(没有值)。 Let's recall that K
expects two argument K = x => y => {return x}
. 让我们回想一下,
K
期望两个参数K = x => y => {return x}
。 K(o)
assigns o
to x
. K(o)
将o
赋给x
。 No matter what is passed as second argument, K
will always ignore y
and return x
instead. 无论作为第二个参数传递什么,
K
将始终忽略y
并返回x
。
But what does o
represent in the expression K(o)
? 但
o
在表达式K(o)
代表什么? It represents Option.none
that is, the absence of a value. 它代表
Option.none
,即没有值。 So when someone tries to map a function f
over none
, none
is just returned, no matter what f
passes as second argument to K
. 因此,当有人试图将函数
f
映射为none
, none
返回任何函数,无论f
作为第二个参数传递给K
K combinator can be also used as truth-value, when using church-encoded booleans. 当使用教会编码的布尔值时,K组合子也可以用作真值。 Ie IF-TEST THEN ELSE: if your "IF-TEST" returns K, then "else" would be dropped and "then" would be executed.
即IF-TEST那么:如果你的“IF-TEST”返回K,那么“else”将被删除并且“then”将被执行。
Suppose you have an array of numbers xs
:假设您有一个数字数组
xs
:
[10, 5, 12, 7]
You need to transform it in two ways:您需要通过两种方式对其进行转换:
xs.map(x => x % 2 == 0 ? x + 1 : x + 2);
//=> [11, 7, 13, 9]
xs.map(x => x % 2 == 0 ? x + 10 : x + 20);
//=> [20, 25, 22, 27]
I'll quickly acknowledge that the ternary expressions are probably just fine but for the purpose of answering the question I'll make them pointfree :我很快就会承认三元表达式可能很好,但为了回答这个问题,我会让它们变得毫无意义:
const ifelse = (p, t, f) => x => p(x) ? t(x) : f(x);
const add = a => x => a + x;
const even = x => x % 2 == 0;
xs.map(ifelse(even, add(1), add(2)));
//=> [11, 7, 13, 9]
xs.map(ifelse(even, add(10), add(20)));
//=> [20, 25, 22, 27]
Now let's say that we just want to return 'even'
or 'odd'
:现在假设我们只想返回
'even'
或'odd'
:
xs.map(ifelse(even, () => 'even', () => 'odd'));
//=> ['even', 'odd', 'even', 'odd']
The two lambdas behave in the same way: they completely ignore x
and always return the same value.这两个 lambda 表达式的行为方式相同:它们完全忽略
x
并始终返回相同的值。 This is exactly what the K estrel combinator is about:这正是K estrel 组合器的意义所在:
const K = a => x => a;
xs.map(ifelse(even, K('even'), K('odd')));
//=> ['even', 'odd', 'even', 'odd']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.