函子或monad分别可以用更高阶函数表示吗?

时间:2017-02-28 12:00:15

标签: javascript functional-programming monads functor higher-order-functions

我试图在不使用容器类型([] / {})的情况下在Javascript中实现仿函数。因此,我只使用纯高阶函数来构造它们:



const option = x => f => isAssigned(x) ? option(f(x)) : none;

const none = option(null);


const isAssigned = x => x !== null && x !== undefined;
const inc = x => x + 1;
const sqr = x => x * x;
const head = xs => xs[0]
const log = x => (console.log(x), x);


option(head([4])) (inc) (sqr) (log); // 25

option(head([])) (inc) (sqr) (log); // not executed




option接受一个值和一个纯函数,将函数提升到其上下文中,将其应用于值并在同一上下文中返回结果。我想这是一个算子。但是,它并没有遵循Javascript中的functor prototcol,每个仿函数必须在其原型上拥有一个map函数。

option显然可以扩展为类似monad的类型(至少它在我的示例中表现得像):



const option = x => f => isAssigned(x) ? option(f(x)) : none;

const option_ = x => f => isAssigned(x) ? flatten(option(f(x))) : none;

const none = option(null);

const of = x => option(x); // return

const flatten = F => { // it gets a bit ugly here
  let y;

  F(z => (y = z, z));
  return y;
};

// auxiliary functions

const compn = (...fs) => x => fs.reduceRight((acc, f) => f(acc), x);

const getOrElse = x => F => {
  let y;

  F(z => (y = z, z));
  return isAssigned(y) ? y : x;
};

const isAssigned = x => x !== null && x !== undefined;
const log = prefix => x => (console.log(prefix, x), x);
const head = xs => xs[0];
const head_ = xs => option(xs[0]);
const sqr = x => x * x;

// some data

const xs = [5],
 ys = [];

// run

const w = option(xs) (head),
 x = option(ys) (head),
 y = option_(xs) (head_),
 z = option_(ys) (head_);

log("square head  of xs:") (compn(sqr, getOrElse(1)) (w)); // 25

log("square head  of ys:") (compn(sqr, getOrElse(0)) (x)); // 0

log("square head_ of xs:") (compn(sqr, getOrElse(0)) (y)); // 25

log("square head_ of ys:") (compn(sqr, getOrElse(0)) (z)); // 0




如果option实际上是一个仿函数我的问题是:是否可以用(纯)高阶函数表达每个仿函数/ monad,其中在调用中保持上下文(或有效)计算的结果堆栈?

1 个答案:

答案 0 :(得分:1)

当然。功能几乎可以做任何事情。无论如何,你要求它,所以你知道我会尽力提供^ _ ^

除了几个JS原语(*+NumberString)以演示功能之外,您只会看到:

  1. 变量
  2. lambda abstractions
  3. 应用
  4. 这些(基本上)是λ演算的3个基本构建块并非巧合。

    const identity = x => x
    const fromNullable = x => x == null ? None : Option(x)
    
    const Option = (value) => k => {
      const join = () => value
      const map = f => Option(f(value))
      const bind = f => f(value)
      const ap = m => optionMap(value)(m)
      const fold = f => f(value)
      return k (value, join, map, bind, ap, fold)
    }
    
    const None = () => k => {
      const join = identity
      const map = f => None()
      const bind = f => None()
      const ap = m => None()
      const fold = f => f(null)
      return k (null, join, map, bind, ap, fold)
    }
    
    const optionJoin = m => m((value, join, map, bind, ap, fold) => join())
    const optionMap = f => m => m((value, join, map, bind, ap, fold) => map(f))
    const optionBind = f => m => m((value, join, map, bind, ap, fold) => bind(f))
    const optionAp = n => m => m((value, join, map, bind, ap, fold) => ap(n))
    const optionFold = f => m => m((value, join, map, bind, ap, fold) => fold(f))
    
    optionFold (console.log) (Option(5)) // 5
    optionFold (console.log) (None()) // null
    
    optionFold (console.log) (optionMap (x => x * 2) (Option(5))) // 10
    optionFold (console.log) (optionMap (x => x * 2) (None()))// null
    
    optionFold (console.log) (optionAp(Option(3)) (Option(x => x + 4))) // 7
    optionFold (console.log) (optionAp(Option(3)) (None())) // null
    
    optionFold (console.log) (optionBind(x => Option(x * x)) (Option(16))) // 256
    optionFold (console.log) (optionBind(x => Option(x * x)) (None())) // null
    
    optionFold (console.log) (optionJoin (Option(Option('hi')))) // 'hi'
    optionFold (console.log) (optionJoin (Option(None())))// null