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