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 tail is 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'.

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

A couple of helper functions are also provided by the lazy vocabulary.

curry1Given a value and a quotation, returns a new quotation that when called will have the value on the stack.
curry2Given two values and a quotation, returns a new quotation that when called will have the two values on the stack.

Reference

lcons ( value promise -- lcons )

Provides the same effect as 'cons' does for normal lists. It creates a cons cell where the first element is the value given and the second element is a promise.

A promise is either a value that has had 'force' called on it, or a quotation that when 'call' is applied to it, returns the actual value.

  ( 1 ) 5 6 delay lcons dup .
       => [ 5 6 ]
  ( 2 ) dup lcar .
       => 5
  ( 3 ) dup lcdr .
       => 6

lunit ( value -- llist )

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

  ( 1 ) 42 lunit dup .
       => [ 42 f ]
  ( 2 ) dup lcar .
       => 42
  ( 3 ) dup lcdr .
       => f
  ( 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.

  ( 1 ) 42 lunit dup .
       => [ 42 f ]
  ( 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 ) 5 [ 5 6 + ] lcons dup .
       => [ 5 5 6 + ]
  ( 2 ) lcdr .
       => 11
  ( 1 ) 5 lfrom dup .
       => [ 5 5 succ lfrom ]
  ( 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 
       => [ 1 1 succ lfrom ]
  ( 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. Note that cdr is forced resulting in it being evaluated.

  ( 1 ) 5 [ 6 ] lcons dup .
       => [ 5 6 ]
  ( 2 ) luncons .s
       => { 5 6 }

lmap ( llist quot -- llist )

Provides the same effect as 'map' does for normal lists. It 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 initially called lmap will only call quot on the first element of the list. It then constructs a lazy list that performs the next 'lmap' operation on the next element when it is evaluated. This allows mapping over infinite lists.

  ( 1 ) 1 lfrom 
       => < infinite list of incrementing numbers >
  ( 2 ) [ 2 * ] lmap
       => < infinite list of numbers incrementing by 2 >
  ( 3 ) 5 swap ltake .
       => [ 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.

When initially called lsubset will only call the predicate quotation on the first element of the list. It then constructs a lazy list that performs the next 'lsubset' operation on the next element when it is evaluated. This allows subsetting over infinite lists.

  ( 1 ) 1 lfrom 
       => < infinite list of incrementing numbers >
  ( 2 ) [ prime? ] lsubset
       => < infinite list of prime numbers >
  ( 3 ) 5 swap ltake .
       => [ 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 -- list )

Iterates over the lazy list 'n' times, appending each element to a normal list. The normal list is returned. This provides a convenient way of getting elements out of a lazy list.

  ( 1 ) : ones 1 [ ones ] lcons ;
  ( 2 ) 5 ones ltake
       => [ 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