Skip to content

부록 A: 핵심 함수 지원 ​

본 부록은 서적에서 설명된 다양한 자바스크립트 함수의 기본 구현체를 제공합니다. 이 구현체들은 가장 빠르거나 효율적인 구현체가 아닐 수 있으며, 순전히 교육적 목적으로 제공됨을 유의하십시오.

프로덕션 환경에 적합한 함수를 찾으시려면 ramda, lodash, 또는 folktale 라이브러리를 참조하시기 바랍니다.

일부 함수는 부록 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);