Практическое использование K-комбинатора (Kestrel) в javascript

K-комбинатор может быть реализован, как показано ниже, и реализация не должна иметь побочных эффектов.

const K = x => y => x;

Его иногда называют "const" (как в Haskell). Функция K может быть определена как "принимает значение и возвращает (постоянную) унарную функцию, которая всегда возвращает это значение".

Когда это полезно? Пожалуйста, помогите мне с практическими примерами.

Ответ 1

Проблема с K как со всеми примитивными комбинаторами состоит в том, что вы не можете считать это самостоятельно. Примитивные комбинаторы являются фундаментальными строительными блоками функционального программирования. Вам нужен надлежащий контекст, чтобы наблюдать за ними на работе. Задача состоит в том, чтобы понять этот контекст, если вы новичок в функциональной парадигме.

Вот "типичный контекст": Option. Экземпляры типа 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](o.x),

  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);

Ответ 2

Сортировка широкого вопроса, но это приятно, мне это нравится.

Чтобы поддержать мой пример, в этом ответе я собираюсь реализовать & hellip;

abuild :: Number -> (Number -> a) -> [a]

& hellip; который, как предполагает тип, принимает число и функцию для построения массива. Это может быть полезно, если вы хотите построить массив известного размера на основе некоторых вычислений.


Пусть построит массив с 5 элементами, используя функцию идентификации, id. Как вы можете видеть, последовательный числовой индекс, начинающийся с 0, присваивается вашей функции построения

abuild (5) (id)   // = > [0,1,2,3,4]

Пусть на этот раз сделают что-то притворство с застройщиком. Мы будем делать квадратный вход. Очень продвинутый.

abuild (5) (x=> x * x)
// => [0,1,4,9,16]

Или, может быть, нас не волнует вход. Я всегда люблю хороший смех. Я постоянно смеюсь над вещами. Можно сказать, что я K('ha') & hellip;

abuild (5) (K('ha'))
// => ['ha','ha','ha','ha','ha']

Бум! Довольно полезно, не так ли? Это K


Реализация

Идите вперед и запустите его, чтобы увидеть 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']

Ответ 3

Комбинатор

K также может использоваться как значение истины при использовании логических логических элементов. То есть IF-TEST THEN ELSE: если ваш "IF-TEST" возвращает K, тогда "else" будет сброшен, а "then" будет выполнен.