Lazy Evaluation

The 'lazy' vocabulary adds lazy lists to Factor. This provides the ability to describe infinite structures, and to delay execution of expressions until they are actually used.

Lazy lists, like normal lists, are composed of a head and tail. In a lazy list the head and tail are something called a 'promise'. To convert a 'promise' into its actual value a word called 'force' is used. To convert a value into a 'promise' the word to use is 'delay'.

delay
force

Many of the lazy list words are named similar to the standard list words but with an 'l' suffixed to it. Here are the commonly used words and their equivalent list operation:

Lazy ListNormal List
lnil[ ]
lnil?Test for nil value
lconscons
lunitunit
lcarcar
lcdrcdr
lnthnth
lunconsuncons
lmapmap
lsubsetsubset
leacheach
lappendappend

A few additional words specific to lazy lists are:

ltakeReturns a normal list containing a specified number of items from the lazy list.
lappend*Given a lazy list of lazy lists, concatenate them together in a lazy manner, returning a single lazy list.
list>llistGiven a normal list, return a lazy list that contains the same elements as the normal list.

Reference

delay ( quot -- <promise> )

'delay' is used to convert a value or expression into a promise. The word 'force' is used to convert that promise back to its value, or to force evaluation of the expression to return a value.

The value on the stack that 'delay' expects must be quoted. This is a requirement to prevent it from being evaluated.

  ( 1 ) [ 42 ] delay dup .
       => << promise [ ] [ 42 ] [ ] [ ] >>
  ( 2 ) force .
       => 42

force ( <promise> -- value )

'force' will evaluate a promises original expression and leave the value of that expression on the stack.

A promise can be forced multiple times but the expression is only evaluated once. Future calls of 'force' on the promise will returned the cached value of the original force. If the expression contains side effects, such as i/o, then that i/o will only occur on the first 'force'. See below for an example (steps 3-5).

If a promise is itself delayed, a force will evaluate all promises until a value is returned. Due to this behaviour it is generally not possible to delay a promise. The example below shows what happens in this case.

       
  ( 1 ) [ 42 ] delay dup .
       => << promise [ ] [ 42 ] [ ] [ ] >>
  ( 2 ) force .
       => 42
       
        #! Multiple forces on a promise returns cached value
  ( 3 ) [ "hello" print 42 ] delay dup .
       => << promise [ ] [ "hello" print 42 ] [ ] [ ] >>
  ( 4 ) dup force .
       => hello
          42
  ( 5 ) force .
       => 42

        #! Forcing a delayed promise cascades up to return
        #! original value, rather than the promise.
  ( 6 ) [ [ 42 ] delay ] delay dup .
       => << promise [ ] [ [ 42 ] delay ] [ ] [ ] >>
  ( 7 ) force .
       => 42

lnil ( -- lcons )

Returns a value representing the empty lazy list.

  ( 1 ) lnil .
       => << promise [ ] [ [ ] ] t [ ] >>

lnil? ( lcons -- bool )

Returns true if the given lazy cons is the value representing the empty lazy list.

  ( 1 ) lnil lnil? .
       => t
  ( 2 ) [ 1 ] list>llist dup lnil? .
       => [ ]
  ( 3 ) lcdr lnil? .
       => t

lcons ( car-promise cdr-promise -- lcons )

Provides the same effect as 'cons' does for normal lists. Both values provided must be promises (ie. expressions that have had delay called on them).

As the car and cdr passed on the stack are promises, they are not evaluated until lcar or lcdr are called on the lazy cons.

  ( 1 ) [ "car" ] delay [ "cdr" ] delay lcons dup .
       => << promise ... >>
  ( 2 ) dup lcar .
       => "car"
  ( 3 ) dup lcdr .
       => "cdr"

lunit ( value-promise -- llist )

Provides the same effect as 'unit' does for normal lists. It creates a lazy list where the first element is the value given.

Like lcons, the value on the stack must be a promise and is not evaluated until the lcar of the list is requested.

  ( 1 ) [ 42 ] delay lunit dup .
       => << promise ... >>
  ( 2 ) dup lcar .
       => 42
  ( 3 ) dup lcdr lnil? .
       => t
  ( 4 ) [ . ] leach
       => 42

lcar ( lcons -- value )

Provides the same effect as 'car' does for normal lists. It returns the first element in a lazy cons cell. This will force the evaluation of that element.

  ( 1 ) [ 42 ] delay lunit dup .
       => << promise ... >>
  ( 2 ) lcar .
       => 42

lcdr ( lcons -- value )

Provides the same effect as 'cdr' does for normal lists. It returns the second element in a lazy cons cell and forces it. This causes that element to be evaluated immediately.

  ( 1 ) [ 1 ] delay [ 5 6 + ] delay lcons dup .
       => << promise ... >>
  ( 2 ) lcdr .
       => 11
  ( 1 ) 5 lfrom dup .
       => << promise ... >>
  ( 2 ) lcdr dup lcar .
       => 6
  ( 3 ) lcdr dup lcar .
       => 7
  ( 4 ) lcdr dup lcar .
       => 8

lnth ( n llist -- value )

Provides the same effect as 'nth' does for normal lists. It returns the nth value in the lazy list. It causes all the values up to 'n' to be evaluated.

  ( 1 ) 1 lfrom dup .
       => << promise ... >>
  ( 2 ) 5 swap lnth .
       => 6

luncons ( lcons -- car cdr )

Provides the same effect as 'uncons' does for normal lists. It returns the car and cdr of the lazy list.

  ( 1 ) [ 5 ] delay [ 6 ] delay lcons dup .
       => << promise ... >>
  ( 2 ) luncons . .
       => 6
          5

lmap ( llist quot -- llist )

Lazily maps over a lazy list applying the quotation to each element. A new lazy list is returned which contains the results of the quotation.

When intially called nothing in the original lazy list is evaluated. Only when lcar is called will the item in the list be evaluated and applied to the quotation. Ditto with lcdr, thus allowing infinite lists to be mapped over.

  ( 1 ) 1 lfrom
       => < infinite list of incrementing numbers >
  ( 2 ) [ 2 * ] lmap
       => < infinite list of numbers incrementing by 2 >
  ( 3 ) 5 swap ltake llist>list .
       => [ 2 4 6 8 10 ]

lsubset ( llist pred -- llist )

Provides the same effect as 'subset' does for normal lists. It lazily iterates over a lazy list applying the predicate quotation to each element. If that quotation returns true, the element will be included in the resulting lazy list. If it is false, the element will be skipped. A new lazy list is returned which contains all elements where the predicate returned true.

Like lmap, when initially called no evaluation will occur. A lazy list is returned that when values are retrieved from in then items are evaluated and checked against the predicate.

  ( 1 ) 1 lfrom
       => < infinite list of incrementing numbers >
  ( 2 ) [ prime? ] lsubset
       => < infinite list of prime numbers >
  ( 3 ) 5 swap ltake llist>list .
       => [ 2 3 5 7 11 ]

leach ( llist quot -- )

Provides the same effect as 'each' does for normal lists. It lazily iterates over a lazy list applying the quotation to each element. If this operation is applied to an infinite list it will never return unless the quotation escapes out by calling a continuation.

  ( 1 ) 1 lfrom
       => < infinite list of incrementing numbers >
  ( 2 ) [ 2 mod 1 = ] lsubset
       => < infinite list of odd numbers >
  ( 3 ) [ . ] leach 
       => 1
          3
          5
          7
          ... for ever ...

ltake ( n llist -- llist )

Iterates over the lazy list 'n' times, appending each element to a lazy list. This provides a convenient way of getting elements out of an infinite lazy list.

  ( 1 ) : ones [ 1 ] delay [ ones ] delay lcons ;
  ( 2 ) 5 ones ltake llist>list .
       => [ 1 1 1 1 1  ]

lappend ( llist1 llist2 -- llist )

Lazily appends two lists together. The actual appending is done lazily on iteration rather than immediately so it works very fast no matter how large the list.

  ( 1 ) [ 1 2 3 ] list>llist [ 4 5 6 ] list>llist lappend
  ( 2 ) [ . ] leach
       => 1
          2
          3
          4
          5
          6

lappend* ( llists -- llist )

Given a lazy list of lazy lists, concatenate them together in a lazy fashion. The actual appending is done lazily on iteration rather than immediately so it works very fast no matter how large the lists.

  ( 1 ) [ 1 2 3 ] list>llist 
  ( 2 ) [ 4 5 6 ] list>llist 
  ( 3 ) [ 7 8 9 ] list>llist
  ( 4 ) 3list list>llist lappend*
  ( 5 ) [ . ] leach
       => 1
          2
          3
          4
          5
          6
          7
          8
          9

list>llist ( list -- llist )

Converts a normal list into a lazy list. This is done lazily so the initial list is not iterated through immediately.

  ( 1 ) [ 1 2 3 ] list>llist 
  ( 2 ) [ . ] leach
       => 1
          2
          3