tkuriyama / elm-generator / Generator

This library provides a way to simulate lazy lists, or streams, in the form of generators.

Definition


type alias Generator a b =
Internal.Types.Generator a b

A generator is parameterized with: (a) the type of value it emits, (b) the type of its internal state. In general, users only care about the former, and the latter is merely something that's required for type checking.


type alias CycleGenerator a =
Generator a ( List a
, a
, List a 
}

A convenience export for generators constructed with cycle.

Constructors

init : b -> (b -> Maybe ( a, b )) -> Generator a b

Construct a generator. Provide an initial state, and a function that takes state and returns a successor in the form of a Maybe (value, next state) tuple. Returning a Nothing signifiies that the generator is empty and will emit no further values.

 init 1 (\n -> Just (n, n+1))
 |> take 5
 --> [1, 2, 3, 4, 5]

repeat : a -> Generator a ()

An infinite generator that repeats the given value.

repeat 1
|> take 5
--> [1, 1, 1, 1, 1]

iterate : (a -> a) -> a -> Generator a a

An infinite generator that repeatedly applies the given function to emit successive values.

iterate ((+) 1) 1
|> take 5
--> [1, 2, 3, 4, 5]

cycle : List a -> CycleGenerator a

An infinite generator that repeated cycles through the list of given values.

cycle [1, 2, 3]
|> take 6
--> [1, 2, 3, 1, 2, 3]

cons : a -> Generator a b -> Generator a ( b, List a )

Cons a value to a generator.

cons "a" (fromList ["b", "c", "d"])
|> take 4
--> ["a", "b", "c", "d"]

prefix : List a -> Generator a b -> Generator a ( b, List a )

Prefix a list of values to a generator.

prefix [1, 2, 3] (fromList [4, 5, 6])
|> take 6
--> [1, 2, 3, 4, 5, 6]

Basic Manipulation

advance : Basics.Int -> Generator a b -> ( List a, Generator a b )

Advance a generator by a given nubmer of steps and collect emitted values. Returns the list of collected values and the updated generated as a pair.

iterate ((+) 1) 1
|> advance 5       -- ([1, 2, 3, 4, 5], updated generator)
|> Tuple.second
|> advance 5        -- ([6, 7, 8, 9, 10], updated generator)
|> Tuple.first
--> [6, 7, 8, 9, 10]

head : Generator a b -> Maybe a

Advance one step and return the emitted value (or Nothing if the generator is empty).

repeat 1
|> head
--> Just 1

tail : Generator a b -> Generator a b

Advance one step and return the updated generator.

iterate ((+) 1) 1
|> tail
|> head
--> Just 2

take : Basics.Int -> Generator a b -> List a

Take the first n items emitted by the generator. Convenience function for advance n >> Tuple.first.

iterate ((+) 1) 1
|> take 5
--> [1, 2, 3, 4, 5]

takeWhile : (a -> Basics.Bool) -> Generator a b -> List a

Take items emitted by the generator while the given predicate holds.

iterate ((+) 1) 1
|> takeWhile ((>) 5)
--> [1, 2, 3, 4]

drop : Basics.Int -> Generator a b -> Generator a b

Advance the generator by n steps, dropping the emitted values. Convenience function for advance n >> Tuple.second.

iterate ((+) 1) 1
|> drop 5
|> take 5
--> [6, 7, 8, 9, 10]

dropWhile : (a -> Basics.Bool) -> Generator a b -> Generator a b

Drop items emitted by the generator while the given predicate holds.

iterate ((+) 1) 1
|> dropWhile ((>) 5)
|> take 5
--> [6, 7, 8, 9, 10]

Transformations

map : (a -> c) -> Generator a b -> Generator c b

Return a new generator that maps the given function to every value emitted.

iterate ((+) 1) 1
|> map ((+) 1)
|> map ((+) 1)
|> take 5
--> [3, 4, 5, 6, 7]

filter : (a -> Basics.Bool) -> Generator a b -> Generator a b

Return a new generator that filters every value emitted.

iterate ((+) 1) 1
|> filter ((<) 3)
|> take 5
--> [4, 5, 6, 7, 8]

Note: using filter without care may produce generators that run forever. For example, this will not terminate:

iterate ((+) 1) 0
    |> filter ((>) 0)
    |> take 1

scanl : (c -> a -> c) -> c -> Generator a b -> Generator c ( b, c )

Foldl for genereators: return a new generator that successively reduces emitted values.

fromList [ 1, 2, 3 ]
|> scanl (+) 0
|> take 3
--> [1, 3, 6]

Zipping and Combining

Note that a zipped generator will terminate when either of its parent generators becomes empty, whereas a merged generator will emit values until both its parent generators are empty.

zip : Generator a b -> Generator c d -> Generator ( a, c ) ( b, d )

Return a new generator that combines values emitted by two generators into pairs.

iterate ((+) 1) 1
|> (\g -> zip g g)
|> take 3
--> [(1, 1), (2, 2), (3, 3)]

zipWith : (a -> c -> e) -> Generator a b -> Generator c d -> Generator e ( b, d )

Return a new generator that combines values emitted by two generators with the given function.

iterate ((+) 1) 1
|> (\g -> zipWith (+) g g)
|> take 5
--> [2, 4, 6, 8, 10]

iterate ((+) 1) 1
|> (\g -> zipWith (+) g (filter ((<) 5) g))
|> take 5
--> [7, 9, 11, 13, 15]

merge : (a -> a -> Basics.Bool) -> Generator a b -> Generator a d -> Generator a ( b, d )

Return a new generator that merges two generators.

The merge rule is a predicate function that compares the values emitted by the two generators and chooses the left value if the predicate is true. Only the generator with the chosen value will be advanced.

fromList [ 1, 3, 4, 8 ]
|> (\g1 -> ( g1, fromList [ 2, 3, 5, 7 ] ))
|> (\(g1, g2) -> merge (<) g1 g2)
|> toList
--> [1, 2, 3, 3, 4, 5, 7, 8]

mergeWith : (a -> c -> ( Maybe e, Basics.Bool, Basics.Bool )) -> (a -> Maybe e) -> (c -> Maybe e) -> Generator a b -> Generator c d -> Generator e ( b, d )

Return a new generator that merges two generators with a custom function. This is the most expressive way to combine two generators, with control over whether to emit values and whether to advance either generator.

The merge function takes two values and returns a triple of (maybe a merged value, bool to advance left generator, bool to advance the right generator). Since the new generator may emit values of any type, additional functions are required to convert the left and right generator values to maybes of the merged value type (in case either generator is empty). Maybe is used to allow values to be skipped entirely.

-- a trivial example
mergeWith
    (\x xs -> (Just <| x :: xs, True, True))
    (\x -> Just [x])
    (\xs -> Just xs)
    (fromList [1, 2, 3, 4])
    (fromList [[10], [11], [12]])
 |> take 10
 --> [[1, 10], [2, 11], [3, 12], [4]]

Also see Examples/Timeseries.elm.

intersperse : a -> Generator a b -> Generator a ( b, (), Basics.Bool )

Return a new generator that intersperses values from the given generator with the given constant value.

intersperse "." (fromList ["a", "b", "c"])
|> take 6
--> ["a", ".", "b", ".", "c", "."]

interleave : Generator a b -> Generator a c -> Generator a ( b, c, Basics.Bool )

Return a new generator that alternates between values emitted by the two given generators.

interleave (repeat 1) (repeat 2)
|> take 6
--> [1, 2, 1, 2, 1, 2]

Finite Generators

fromList : List a -> Generator a (List a)

Construct a finite generator.

fromList [ 1, 2, 3, 4, 5 ]
|> advance 5
|> Tuple.first
--> [1, 2, 3, 4, 5]

toList : Generator a b -> List a

Attempt to collect all values emitted from a generator into a list.

fromList [ 1, 2, 3, 4, 5 ]
|> toList
--> [1, 2, 3, 4, 5]

Note: this function runs forever if the generater is infinite.

foldl : (a -> c -> c) -> c -> Generator a b -> c

Attempt to reduce all values emitted from a generator from the left.

fromList [ 1, 2, 3, 4, 5 ]
|> foldl (::) []
--> [ 5, 4, 3, 2, 1 ]

fromList [ 1, 2, 3, 4, 5 ]
|> foldl (+) 0
--> 15

Note: this function runs forever if the generater is infinite.

Introspection

empty : Generator a b -> Basics.Bool

Test if a generator is empty. An empty generator will emit no further values. Note that it's not necessary to check for emptiness before calling advance or other functions that attempt to advance the generator.

fromList [1, 2, 3, 4, 5]
|> drop 6
|> empty
--> True

inspect : Generator a b -> Maybe b

Retrieve current state from generator (or Nothing if the generator is empty).

This function isn't intended for use in normal interaction with or manipulation of generators. It can be useful for debugging, though, or otherwise studying non-trivial generator state.

fromList [1, 2, 3, 4, 5]
|> drop 6
|> inspect
--> Nothing


fromList [1, 2, 3, 4, 5]
|> drop 2
|> inspect
--> Just [3, 4, 5]