Can lazy evaluation be implemented by a monadic type?

Viewed 1191

I'm currently studying lazy evaluation in conjunction with monads in Javascript and what use cases may evolve from these. So I tried to implement a lazy type, which implements the functor/monad type class. The corresponding constructor is meant to be lazy in its arguments and in its result. Here is what I came up with:

// a lazy type

// (() -> a) -> () -> b
const Lazy = thunk => () => thunk();

// (b -> a -> b) -> b -> Lazy a -> b
Lazy.fold = f => acc => tx => f(acc) (tx());

// (a -> b) -> Lazy a -> Lazy b
Lazy.map = f => tx => Lazy(() => f(tx()));

// Lazy (a -> b) -> Lazy a -> Lazy b
Lazy.ap = tf => tx => Lazy(() => tf() (tx()));

Lazy.of = Lazy;

// Lazy (Lazy a) -> Lazy a
Lazy.join = ttx => ttx();

// (a -> Lazy b) -> Lazy a -> Lazy b
Lazy.chain = ft => tx => Lazy.join(Lazy.map(ft) (tx));

// recursive bind (or chain in Javascript)

// Number -> (a -> b) -> a -> Lazy b
const repeat = n => f => x => {
  const aux = m => y => m === 0
   ? Lazy(() => y)
   : Lazy.chain(aux(m - 1)) (Lazy(() => f(y)));

  return aux(n) (x);
};

// impure function to observe the computation

const inc = x => (console.log(++x), x);

// and run

console.log(repeat(5) (inc) (0)); // logs 1, 2, 3, 4, 5, () => thunk()

Now this evidently makes no sense, since the action sequence isn't lazy at all. Lazy.join simply triggers the evaluation prematurely. Hence, the following questions have emerged:

  • are sequences of monadic actions in Haskell always eagerly evaluated?
  • is lazy evaluation an effect that cannot be implemented by a monad in a strictly evaluated language?

I am not even sure if my research makes any sense, so feel free to vote for closing this question.

3 Answers
Related