liveBook · Manning

Infinite data structure

As explained in Learn Haskell by Example

An infinite list is a lazily generated list with no finite endpoint whose finite prefixes can be evaluated on demand.

An infinite list is a lazily generated list with no finite endpoint. Haskell can define infinitely many values without evaluating them all, creating only the elements demanded by a consuming function. section 10.2.2 section 10.5

Use infinite lists safely by choosing operations that demand only a finite prefix:

  • Create recurring values with repeat, such as repeat 1. section 10.2.2
  • Generate sequences with open-ended range expressions such as [1..] or [1,3..]. section 10.2.2
  • Inspect finite prefixes with take, takeWhile, (!!), or head. section 10.2.2
  • Combine infinite and finite lists with zipWith or zipWith3; zipping stops when the shorter list ends. section 10.2.2
  • Avoid whole-list operations such as length, which never finish on an infinite list. section 10.2.2

Building a music envelope

An ADSR envelope combines finite attack and decay curves with sus = repeat s, an infinite sustain curve, and a release curve. zipWith3 multiplies these curves with signal samples; because the signal is finite, evaluation produces only the required output. section 10.2.2

ghci> ones = 1 : ones :: [Int]
ghci> take 10 ones
[1,1,1,1,1,1,1,1,1,1]

Evaluate take 10 ones to observe a recursively defined infinite list without requesting the entire structure. section 10.2.2

Listing 10.8 A function to apply an ADSR envelope to a signal
import Util.Types

adsr :: ADSR -> Signal -> Signal
adsr (ADSR a d s r) signal =
  zipWith3                          #1
    (\adsCurve rCurve sample ->
 adsCurve * rCurve * sample)     #1
    (att ++ dec ++ sus)       #2
    rel
    signal
  where
    attackSamples = fromIntegral $ samplesPerSecond a    #3
    decaySamples = fromIntegral $ samplesPerSecond d     #3
    releaseSamples = fromIntegral $ samplesPerSecond r   #3

    att = map (/ attackSamples) [0.0 .. attackSamples]   #4
    dec =
      reverse $    #5
        map                                              #6
          (\x -> ((x / decaySamples) * (1 - s)) + s)     #6
          [0.0 .. decaySamples - 1]                      #6
    sus = repeat s             #7
    rel =
      reverse $                            #8
        take                                #9
          (length signal)                  
          (map (/ releaseSamples) #9
 [0.0 .. releaseSamples] ++ repeat 1.0)  #10

Follow the complete ADSR implementation to see infinite sustain values participate in a practical signal-processing pipeline. section 10.2.2

How Learn Haskell by Example covers it

How deep it goes
Explains lazy generation, safe finite-demand operations, recursive definitions, range syntax, and the failure of full evaluation.
Examples
Demonstrates recursive ones, natural and odd-number ranges, repeat, finite zipping, and an ADSR audio envelope.
What it assumes
Assumes familiarity with lists and common list functions, while introducing lazy infinite definitions through concrete Haskell expressions.
What you can do after
Apply it via take, repeat, range syntax, zipWith, and zipWith3 while avoiding operations that demand complete evaluation.
How it connects
Connects lazy evaluation with lists, range expressions, repeat, zipWith, zipWith3, and signal processing.

Also explained in

Compare how 2 books explain infinite data structure →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.