// > foldM :: (Monad a) => (a -> b -> m a) -> a -> [b] -> m a
const foldM = (monad) => (f, a, list) =>
  list.length === 0 
  ? monad.pure(a)
  : monad.bind(
    f(a, list[0]),
    fax => foldM(monad)(f, fax, list.slice(1))
  )
// > foldP :: (a -> b -> p a) -> a -> [b] -> p a
const foldP = foldM({
  pure: x => Promise.resolve(x),
  fmap: (f, g) => g.then(f),
  bind: (f, g) => f.then(g)
})
// > waterfall :: x -> [x -> Promise x] -> Promise x
const waterfall = (x, fs) =>
  foldP((a, y) => y (a), x, fs)
const square = x => Promise.resolve(x * x)
waterfall(2, [square, square, square, square])
.then(console.log)