Skip to content

Appendix C: Pointfree Utilities

In this appendix, you'll find pointfree versions of rather classic JavaScript functions described in the book. All of the following functions are seemingly available in exercises, as part of the global context. Keep in mind that these implementations may not be the fastest or the most efficient implementation out there; they solely serve an educational purpose.

In order to find functions that are more production-ready, have a peek at ramda, lodash, or folktale.

Note that functions refer to the curry & compose functions defined in Appendix A

add

js
// add :: Number -> Number -> Number
const add = curry((a, b) => a + b);

append

js
// append :: String -> String -> String
const append = flip(concat);

chain

js
// chain :: Monad m => (a -> m b) -> m a -> m b
const chain = curry((fn, m) => m.chain(fn));

concat

js
// concat :: String -> String -> String
const concat = curry((a, b) => a.concat(b));

eq

js
// eq :: Eq a => a -> a -> Boolean
const eq = curry((a, b) => a === b);

filter

js
// filter :: (a -> Boolean) -> [a] -> [a]
const filter = curry((fn, xs) => xs.filter(fn));

flip

js
// flip :: (a -> b -> c) -> b -> a -> c
const flip = curry((fn, a, b) => fn(b, a));

forEach

js
// forEach :: (a -> ()) -> [a] -> ()
const forEach = curry((fn, xs) => xs.forEach(fn));
js
// head :: [a] -> a
const head = xs => xs[0];

intercalate

js
// intercalate :: String -> [String] -> String
const intercalate = curry((str, xs) => xs.join(str));

join

js
// join :: Monad m => m (m a) -> m a
const join = m => m.join();

last

js
// last :: [a] -> a
const last = xs => xs[xs.length - 1];

map

js
// map :: Functor f => (a -> b) -> f a -> f b
const map = curry((fn, f) => f.map(fn));

match

js
// match :: RegExp -> String -> Boolean
const match = curry((re, str) => re.test(str));

prop

js
// prop :: String -> Object -> a
const prop = curry((p, obj) => obj[p]);

reduce

js
// reduce :: (b -> a -> b) -> b -> [a] -> b
const reduce = curry((fn, zero, xs) => xs.reduce(fn, zero));

replace

js
// replace :: RegExp -> String -> String -> String
const replace = curry((re, rpl, str) => str.replace(re, rpl));

reverse

js
// reverse :: [a] -> [a]
const reverse = x => (Array.isArray(x) ? x.reverse() : x.split('').reverse().join(''));

safeHead

js
// safeHead :: [a] -> Maybe a
const safeHead = compose(Maybe.of, head);

safeLast

js
// safeLast :: [a] -> Maybe a
const safeLast = compose(Maybe.of, last);

safeProp

js
// safeProp :: String -> Object -> Maybe a
const safeProp = curry((p, obj) => compose(Maybe.of, prop(p))(obj));

sequence

js
// sequence :: (Applicative f, Traversable t) => (a -> f a) -> t (f a) -> f (t a)
const sequence = curry((of, f) => f.sequence(of));

sortBy

js
// sortBy :: Ord b => (a -> b) -> [a] -> [a]
const sortBy = curry((fn, xs) => xs.sort((a, b) => {
  if (fn(a) === fn(b)) {
    return 0;
  }

  return fn(a) > fn(b) ? 1 : -1;
}));

split

js
// split :: String -> String -> [String]
const split = curry((sep, str) => str.split(sep));

take

js
// take :: Number -> [a] -> [a]
const take = curry((n, xs) => xs.slice(0, n));

toLowerCase

js
// toLowerCase :: String -> String
const toLowerCase = s => s.toLowerCase();

toString

js
// toString :: a -> String
const toString = String;

toUpperCase

js
// toUpperCase :: String -> String
const toUpperCase = s => s.toUpperCase();

traverse

js
// traverse :: (Applicative f, Traversable t) => (a -> f a) -> (a -> f b) -> t a -> f (t b)
const traverse = curry((of, fn, f) => f.traverse(of, fn));

unsafePerformIO

js
// unsafePerformIO :: IO a -> a
const unsafePerformIO = io => io.unsafePerformIO();