Skip to content

Anhang A: Wesentliche Funktionenunterstützung ​

Dieser Anhang enthält grundlegende JavaScript-Implementierungen verschiedener im Buch beschriebener Funktionen. Diese Implementierungen sind möglicherweise nicht die schnellsten oder effizientesten. Sie dienen ausschließlich Lehrzwecken.

Für produktionsreife Implementierungen empfehlen wir Bibliotheken wie ramda, lodash oder folktale.

Einige Funktionen beziehen sich auf algebraische Strukturen aus Anhang B.

always ​

js
// always :: a -> b -> a
const always = curry((a, b) => a);

compose ​

js
// compose :: ((y -> z), (x -> y),  ..., (a -> b)) -> a -> z
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];

curry ​

js
// curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry(fn) {
  const arity = fn.length;

  return function $curry(...args) {
    if (args.length < arity) {
      return $curry.bind(null, ...args);
    }

    return fn.call(null, ...args);
  };
}

either ​

js
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = curry((f, g, e) => {
  if (e.isLeft) {
    return f(e.$value);
  }

  return g(e.$value);
});

identity ​

js
// identity :: x -> x
const identity = x => x;

inspect ​

js
// inspect :: a -> String
const inspect = (x) => {
  if (x && typeof x.inspect === 'function') {
    return x.inspect();
  }

  function inspectFn(f) {
    return f.name ? f.name : f.toString();
  }

  function inspectTerm(t) {
    switch (typeof t) {
      case 'string':
        return `'${t}'`;
      case 'object': {
        const ts = Object.keys(t).map(k => [k, inspect(t[k])]);
        return `{${ts.map(kv => kv.join(': ')).join(', ')}}`;
      }
      default:
        return String(t);
    }
  }

  function inspectArgs(args) {
    return Array.isArray(args) ? `[${args.map(inspect).join(', ')}]` : inspectTerm(args);
  }

  return (typeof x === 'function') ? inspectFn(x) : inspectArgs(x);
};

left ​

js
// left :: a -> Either a b
const left = a => new Left(a);

liftA2 ​

js
// liftA2 :: (Applicative f) => (a1 -> a2 -> b) -> f a1 -> f a2 -> f b
const liftA2 = curry((fn, a1, a2) => a1.map(fn).ap(a2));

liftA3 ​

js
// liftA3 :: (Applicative f) => (a1 -> a2 -> a3 -> b) -> f a1 -> f a2 -> f a3 -> f b
const liftA3 = curry((fn, a1, a2, a3) => a1.map(fn).ap(a2).ap(a3));

maybe ​

js
// maybe :: b -> (a -> b) -> Maybe a -> b
const maybe = curry((v, f, m) => {
  if (m.isNothing) {
    return v;
  }

  return f(m.$value);
});

nothing ​

js
// nothing :: Maybe a
const nothing = Maybe.of(null);

reject ​

js
// reject :: a -> Task a b
const reject = a => Task.rejected(a);