maxsnew/lazy

Basic primitives for working with laziness


License
BSD-3-Clause
Install
elm-package install maxsnew/lazy 1.1.0

Documentation

Laziness in Elm

This package provides the basic primitives for working with laziness in Elm.

Motivating Example

Maybe you have 100 different graphs you want to show at various times, each requiring a decent amount of computation. Here are a couple ways to handle this:

  1. Compute everything up front. This will introduce a delay on startup, but it should be quite fast after that. Depending on how much memory is needed to store each graph, you may be paying a lot there as well.

  2. Compute each graph whenever you need it. This minimizes startup cost and uses a minimal amount of memory, but when you are flipping between two graphs you may be running the same computations again and again.

  3. Compute each graph whenever you need it and save the result. Again, this makes startup as fast as possible fast, but since we save the result, flipping between graphs becomes much quicker. As we look at more graphs we will need to use more and more memory though.

All of these strategies are useful in general, but the details of your particular problem will mean that one of these ways provides the best experience. This library makes it super easy to use strategy #3.

Pitfalls

Laziness + Time — Over time, laziness can become a bad strategy. As a very simple example, think of a timer that counts down from 10 minutes, decrementing every second. Each step is very cheap to compute. You subtract one from the current time and store the new time in memory, so each step has a constant cost and memory usage is constant. Great! If you are lazy, you say “here is how you would subtract one” and store that entire computation in memory. This means our memory usage grows linearly as each second passes. When we finally need the result, we might have 10 minutes of computation to run all at once. In the best case, this introduces a delay that no one really notices. In the worst case, this computation is actually too big to run all at once and crashes. Just like with dishes or homework, being lazy over time can be quite destructive.

Laziness + Concurrency — When you add concurrency into the mix, you need to be even more careful with laziness. As an example, say we are running expensive computations on three worker threads, and the results are sent to a fourth thread just for rendering. If our three worker threads are doing their work lazily, they “finish” super quick and pass the entire workload onto the render thread. All the work we put into designing this concurrent system is wasted, everything is run sequentially on the render thread! It is just like working on a team with lazy people. You have to pay the cost of coordinating with them, but you end up doing all the work anyway. You are better off making things single threaded!

Learn More

One of the most delightful uses of laziness is to create infinite streams of values. Hopefully we can get a set of interesting challenges together so you can run through them and get comfortable.

For a deeper dive, Chris Okasaki's book Purely Functional Data Structures and thesis have interesting examples of data structures that get great benefits from laziness, and hopefully it will provide some inspiration for the problems you face in practice.