pete-murphy/elm-nlds/2.0.0

NOTE: the latest version is 3.0.1

Peach

A lazy priority search data structure, ported from Unison's pchiusano/peachy library.

Peach is similar to Each but expands branches with smaller weights first. It allows you to explore multiple possibilities, prioritizing by weight.

Core Types

type Peach a

A Peach a represents a lazy priority search computation that can yield multiple weighted results. Results with smaller weights are explored first.

Internally, this is represented as a priority queue of either:

  • Concrete weighted values
  • Thunks that produce more Peach elements when forced

This allows lazy evaluation: results are only computed when extracted.

Primitive Operations

peach : List ( Float, a ) -> Peach a

Create a Peach from a list of weighted values. The values will be explored in order of increasing weight (smallest first).

peach [ ( 2.0, "b" ), ( 1.0, "a" ), ( 3.0, "c" ) ]
    |> head
--> Just ( 1.0, "a" )

A Peach that yields no results (represents failure).

fail
    |> head
--> Nothing
succeed : a -> Peach a

A Peach that yields exactly one result with weight 0.

succeed "hello"
    |> head
--> Just ( 0.0, "hello" )
lazy : Float -> (() -> Peach a) -> Peach a

Create a lazy Peach from a thunk. The thunk will only be evaluated when results are extracted. This is the key primitive for lazy evaluation.

lazy 0.0 (\() -> peach [ ( 1.0, "computed" ) ])
    |> head
--> Just ( 1.0, "computed" )

The first argument is the minimum possible weight of results from this thunk. This is used for priority ordering - thunks with lower weights are forced first.

Combinators

map : (a -> b) -> Peach a -> Peach b

Transform the values in a Peach computation.

peach [ ( 1.0, 2 ), ( 2.0, 3 ) ]
    |> map (\x -> x * 2)
    |> toList
--> [ ( 1.0, 4 ), ( 2.0, 6 ) ]

Note: map is lazy - it wraps transformations in thunks for deferred items.

flatMap : (a -> Peach b) -> Peach a -> Peach b

Chain computations where the second depends on the result of the first. The weights are combined additively.

peach [ ( 1.0, "a" ), ( 2.0, "b" ) ]
    |> flatMap (\x -> peach [ ( 0.5, x ++ "1" ), ( 1.0, x ++ "2" ) ])
    |> take 4
--> [ ( 1.5, "a1" ), ( 2.0, "a2" ), ( 2.5, "b1" ), ( 3.0, "b2" ) ]

Laziness: flatMap is lazy - it doesn't immediately evaluate all branches. Instead, it creates thunks that are evaluated on-demand as results are extracted.

choose : List (Peach a) -> Peach a

Choose between multiple Peach computations, exploring them in priority order.

choose
    [ peach [ ( 1.0, "a" ) ]
    , peach [ ( 2.0, "b" ) ]
    , peach [ ( 0.5, "c" ) ]
    ]
    |> toList
--> [ ( 0.5, "c" ), ( 1.0, "a" ), ( 2.0, "b" ) ]

Extractors

toList : Peach a -> List ( Float, a )

Convert a Peach computation to a list of all results, ordered by weight (smallest first).

Warning: This forces evaluation of all thunks. For infinite or very large search spaces, use take instead.

peach [ ( 2.0, "b" ), ( 1.0, "a" ), ( 3.0, "c" ) ]
    |> toList
--> [ ( 1.0, "a" ), ( 2.0, "b" ), ( 3.0, "c" ) ]
take : Int -> Peach a -> List ( Float, a )

Take the first n results from a Peach computation, ordered by weight.

This is the primary way to extract results lazily. Only the minimum number of thunks needed to produce n results will be evaluated.

peach [ ( 2.0, "b" ), ( 1.0, "a" ), ( 3.0, "c" ) ]
    |> take 2
--> [ ( 1.0, "a" ), ( 2.0, "b" ) ]

Convenience Functions

rankBy : (a -> Float) -> List a -> Peach a

Rank values by a function and create a Peach from them.

rankBy (\s -> toFloat (String.length s)) [ "apple", "kiwi", "banana" ]
    |> head
--> Just ( 4.0, "kiwi" )
each : List a -> Peach a

Create a Peach from a list, treating each element equally (weight 0).

each [ "a", "b", "c" ]
    |> toList
    |> List.length
--> 3
optional : Maybe a -> Peach a

Convert an Optional value to a Peach.

optional (Just "hello")
    |> head
--> Just ( 0.0, "hello" )


optional Nothing
    |> head
--> Nothing