elm-lang/lazy/2.0.0

NOTE: this package is not compatible with Elm 0.19.1

Lazy

This library lets you delay a computation until later.

Basics

type Lazy a

A wrapper around a value that will be lazily evaluated.

lazy : (() -> a) -> Lazy a

Delay the evaluation of a value until later. For example, maybe we will need to generate a very long list and find its sum, but we do not want to do it unless it is absolutely necessary.

lazySum : Lazy Int
lazySum =
    lazy (\() -> sum [1..1000000])

Now we only pay for lazySum if we actually need it.

force : Lazy a -> a

Force the evaluation of a lazy value. This means we only pay for the computation when we need it. Here is a rather contrived example.

lazySum : Lazy Int
lazySum =
    lazy (\() -> List.sum [1..1000000])


sums : (Int, Int, Int)
sums =
    (force lazySum, force lazySum, force lazySum)

We are forcing this computation three times. The cool thing is that the first time you force a value, the result is stored. This means you pay the cost on the first one, but all the rest are very cheap, basically just looking up a value in memory.

Mapping

map : (a -> b) -> Lazy a -> Lazy b

Lazily apply a function to a lazy value.

lazySum : Lazy Int
lazySum =
    map List.sum (lazy (\() -> [1..1000000]))

The resulting lazy value will create a big list and sum it up when it is finally forced.

map2 : (a -> b -> result) -> Lazy a -> Lazy b -> Lazy result

Lazily apply a function to two lazy values.

lazySum : Lazy Int
lazySum =
    lazy (\() -> List.sum [1..1000000])


lazySumPair : Lazy (Int, Int)
lazySumPair =
    map2 (,) lazySum lazySum
map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result
(a -> b -> c -> d -> result)
-> Lazy a
-> Lazy b
-> Lazy c
-> Lazy d
-> Lazy result
(a -> b -> c -> d -> e -> result)
-> Lazy a
-> Lazy b
-> Lazy c
-> Lazy d
-> Lazy e
-> Lazy result

Chaining

apply : Lazy (a -> b) -> Lazy a -> Lazy b

Lazily apply a lazy function to a lazy value. This is pretty rare on its own, but it lets you map as high as you want.

map3 f a b == f `map` a `apply` b `apply` c

It is not the most beautiful, but it is equivalent and will let you create map9 quite easily if you really need it.

andThen : (a -> Lazy b) -> Lazy a -> Lazy b

Lazily chain together lazy computations, for when you have a series of steps that all need to be performed lazily. This can be nice when you need to pattern match on a value, for example, when appending lazy lists:

type List a = Empty | Node a (Lazy (List a))


cons : a -> Lazy (List a) -> Lazy (List a)
cons first rest =
  Lazy.map (Node first) rest


append : Lazy (List a) -> Lazy (List a) -> Lazy (List a)
append lazyList1 lazyList2 =
  let
    appendHelp list1 =
      case list1 of
        Empty ->
          lazyList2


        Node first rest ->
          cons first (append rest list2))
  in
    lazyList1
      |> Lazy.andThen appendHelp

By using andThen we ensure that neither lazyList1 or lazyList2 are forced before they are needed. So as written, the append function delays the pattern matching until later.