This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.
README

Lazy

This library lets you delay a computation until later.

Basics

type Lazy a = Lazy (() -> a) | Evaluated 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 <| List.range 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 <| List.range 1 1000000)

sums : (Int, Int, Int)
sums =
    (force lazySum, force lazySum, force lazySum)
evaluate : Lazy a -> Lazy a

Evaluate the lazy value if it has not already been evaluated. If it has, do nothing.

lazySum : Lazy Int
lazySum =
    lazy (\() -> List.sum <| List.range 1 1000000)

sums : (Int, Int, Int)
sums =
    let
        evaledSum =
            evaluate lazySum
    in
    (force evaledSum, force evaledSum, force evaledSum)

This is mainly useful for cases where you may want to store a lazy value as a lazy value and pass it around. For example, in a list. Where possible, it is better to use force and simply store the computed value seperately.

Mapping

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

Lazily apply a function to a lazy value.

lazySum : Lazy Int
lazySum =
    map List.sum (lazy (\() -> <| List.range 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 <| List.range 1 1000000)

lazySumPair : Lazy (Int, Int)
lazySumPair =
    map2 (,) lazySum lazySum
map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result
map4 : (a -> b -> c -> d -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy d -> Lazy result
map5 : (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.

module Lazy
    exposing
        ( Lazy
        , force
        , lazy
        , evaluate
        , map
        , map2
        , map3
        , map4
        , map5
        , apply
        , andThen
        )

{-| This library lets you delay a computation until later.

# Basics
@docs Lazy, lazy, force, evaluate

# Mapping
@docs map, map2, map3, map4, map5

# Chaining
@docs apply, andThen
-}

-- PRIMITIVES


{-| A wrapper around a value that will be lazily evaluated.
-}
type Lazy a
    = Lazy (() -> a)
    | Evaluated 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 <| List.range 1 1000000 )

Now we only pay for `lazySum` if we actually need it.
-}
lazy : (() -> a) -> Lazy a
lazy thunk =
    Lazy thunk


{-| 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 <| List.range 1 1000000)

    sums : (Int, Int, Int)
    sums =
        (force lazySum, force lazySum, force lazySum)
-}
force : Lazy a -> a
force piece =
    case piece of
        Evaluated a ->
            a

        Lazy thunk ->
            thunk ()


{-| Evaluate the lazy value if it has not already been evaluated. If it has,
do nothing.

    lazySum : Lazy Int
    lazySum =
        lazy (\() -> List.sum <| List.range 1 1000000)

    sums : (Int, Int, Int)
    sums =
        let
            evaledSum =
                evaluate lazySum
        in
        (force evaledSum, force evaledSum, force evaledSum)

This is mainly useful for cases where you may want to store a lazy value as a
lazy value and pass it around. For example, in a list. Where possible, it is better to use
`force` and simply store the computed value seperately.

-}
evaluate : Lazy a -> Lazy a
evaluate piece =
    case piece of
        Evaluated a ->
            Evaluated a

        Lazy thunk ->
            thunk ()
                |> Evaluated



-- COMPOSING LAZINESS


{-| Lazily apply a function to a lazy value.

    lazySum : Lazy Int
    lazySum =
        map List.sum (lazy (\() -> <| List.range 1 1000000)

The resulting lazy value will create a big list and sum it up when it is
finally forced.
-}
map : (a -> b) -> Lazy a -> Lazy b
map f a =
    lazy (\() -> f (force a))


{-| Lazily apply a function to two lazy values.

    lazySum : Lazy Int
    lazySum =
        lazy (\() -> List.sum <| List.range 1 1000000)

    lazySumPair : Lazy (Int, Int)
    lazySumPair =
        map2 (,) lazySum lazySum

-}
map2 : (a -> b -> result) -> Lazy a -> Lazy b -> Lazy result
map2 f a b =
    lazy (\() -> f (force a) (force b))


{-| -}
map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result
map3 f a b c =
    lazy (\() -> f (force a) (force b) (force c))


{-| -}
map4 : (a -> b -> c -> d -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy d -> Lazy result
map4 f a b c d =
    lazy (\() -> f (force a) (force b) (force c) (force d))


{-| -}
map5 : (a -> b -> c -> d -> e -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy d -> Lazy e -> Lazy result
map5 f a b c d e =
    lazy (\() -> f (force a) (force b) (force c) (force d) (force e))


{-| 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.
-}
apply : Lazy (a -> b) -> Lazy a -> Lazy b
apply f x =
    lazy (\() -> (force f) (force x))


{-| 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.
-}
andThen : (a -> Lazy b) -> Lazy a -> Lazy b
andThen callback a =
    lazy (\() -> force (callback (force a)))