Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
# Laziness in Elm
# DEPRECATED

This package provides the basic primitives for working with laziness in Elm.
It turned out that many folks were unclear on the difference between laziness
and delayed computation:

- **Delayed Computation** is when you have a value like `answer : () -> Int`
that you can evaluate later. You only do all the computation when you say
`answer ()` at some later time. If you call `answer ()` four times, you do
the computation four times.

# Motivating Example
- **Laziness** is an optimization on top of delayed computation. It is just
like having `answer : () -> Int` but when this function is evaluated for the
first time, the results are saved. So if you call `answer ()` four times, you
do the computation *one* time.

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:
In all the cases in Elm that I have heard of that use this library, folks only
really needed *delayed computation* and ended up with simpler code when they
went that way.

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.
So in the end, there are two major reasons to stop supporting laziness:

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.
1. It is overkill for all the scenarios I have seen in Elm.
2. It allows the creation of cyclic data, significantly complicating GC.

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.
With laziness you can create a list like `ones = 1 :: ones` that refers to
itself. Without laziness, there is no way to create cyclic data in Elm. That
means we can use a naive reference counting approach to collect garbage if we
wanted. So although people have dreamed up data structures that use laziness
in interesting ways, I do not feel these cases are compelling enough to commit
to the collateral complications.

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.
<br>

* * *

What follows is some of content from the old README.

* * *

<br>


# Pitfalls
Expand Down
8 changes: 4 additions & 4 deletions elm-package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "1.1.0",
"version": "2.0.0",
"summary": "Basic primitives for working with laziness",
"repository": "http://github.com/maxsnew/lazy.git",
"repository": "http://github.com/elm-lang/lazy.git",
"license": "BSD3",
"source-directories": [
"src"
Expand All @@ -11,7 +11,7 @@
],
"native-modules": true,
"dependencies": {
"elm-lang/core": "2.0.0 <= v < 4.0.0"
"elm-lang/core": "5.0.0 <= v < 6.0.0"
},
"elm-version": "0.15.0 <= v < 0.17.0"
"elm-version": "0.18.0 <= v < 0.19.0"
}
40 changes: 27 additions & 13 deletions src/Lazy.elm
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module Lazy
( Lazy, force, lazy
module Lazy exposing
( Lazy
, force, lazy
, map, map2, map3, map4, map5
, apply, andThen
)
where

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

Expand All @@ -20,8 +20,14 @@ module Lazy
import Native.Lazy



-- PRIMITIVES


{-| A wrapper around a value that will be lazily evaluated. -}
type Lazy a = Lazy (() -> a)
type 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
Expand Down Expand Up @@ -59,6 +65,10 @@ force (Lazy thunk) =
thunk ()



-- COMPOSING LAZINESS


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

lazySum : Lazy Int
Expand Down Expand Up @@ -128,23 +138,27 @@ pattern match on a value, for example, when appending lazy lists:

cons : a -> Lazy (List a) -> Lazy (List a)
cons first rest =
Lazy.map (Node 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
`andThen` \list1 ->
case list1 of
Empty ->
lazyList2
|> Lazy.andThen appendHelp

Node first rest ->
cons first (append rest list2))

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 : Lazy a -> (a -> Lazy b) -> Lazy b
andThen a callback =
andThen : (a -> Lazy b) -> Lazy a -> Lazy b
andThen callback a =
lazy (\() -> force (callback (force a)))
39 changes: 17 additions & 22 deletions src/Native/Lazy.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
Elm.Native.Lazy = {};
Elm.Native.Lazy.make = function(localRuntime) {
var _elm_lang$lazy$Native_Lazy = function() {

localRuntime.Native = localRuntime.Native || {};
localRuntime.Native.Lazy = localRuntime.Native.Lazy || {};
if (localRuntime.Native.Lazy.values) {
return localRuntime.Native.Lazy.values;
}

function memoize(thunk) {
var value;
var isForced = false;
return function(tuple0) {
if (!isForced) {
value = thunk(tuple0);
isForced = true;
}
return value;
};
}

return localRuntime.Native.Lazy.values = {
memoize: memoize
function memoize(thunk)
{
var value;
var isForced = false;
return function(tuple0) {
if (!isForced) {
value = thunk(tuple0);
isForced = true;
}
return value;
};
}

return {
memoize: memoize
};

}();