russelldavies / elm-range / Range

Model and operate on a range of values in Elm.

Definition


type alias Range subtype =
{ config : TypeConfig subtype
, range : RangeInternal subtype 
}

A range of a certain type that stores information about its bounds.

Be aware that a Range stores functions internally. If you want to use (==) for comparing two Ranges use the eq operator.


type alias TypeConfig subtype =
{ toString : subtype -> String
, fromString : String -> Result String subtype
, compare : subtype -> subtype -> Basics.Order
, canonical : Maybe (Canonical subtype) 
}

When you need to use a custom type you must create an instance of this.

This acts as a value level typeclass.


type alias Canonical subtype =
( ( BoundFlag
, subtype -> subtype )
, ( BoundFlag
, subtype -> subtype ) 
)

A discrete range type should have a canonicalization function that is aware of the desired step size for the element type. The canonicalization function is charged with converting equivalent values of the range type to have identical representations, in particular consistently inclusive or exclusive bounds. If a canonicalization function is not specified, then ranges with different formatting will always be treated as unequal, even though they might represent the same set of values in reality.

Here is the definition for an integer range:

( ( Inc, (+) 1 ), ( Exc, (+) 1 ) )

It specifies that the lower bound should be inclusive and the upper exclusive. If the supplied bound flags are different during creating then step the value by

Creation

empty : TypeConfig subtype -> Range subtype

Create an empty range

Range.empty Range.types.int

create : TypeConfig subtype -> Maybe subtype -> Maybe subtype -> Result String (Range subtype)

Create a range in standard form (lower bound inclusive, upper bound exclusive).

Range.create Range.types.float (Just 1.2) Nothing

createWith : TypeConfig subtype -> Maybe subtype -> Maybe subtype -> Maybe ( BoundFlag, BoundFlag ) -> Result String (Range subtype)

Create a range with whatever user specified flags.

Range.create Range.types.float (Just 1.5) (Just 12.2) (Just ( Range.Inc, Range.Inc ))


type BoundFlag
    = Inc
    | Exc

The kind of bounds, inclusive or exclusive, to be supplied to createWith.

fromString : TypeConfig subtype -> String -> Result String (Range subtype)

Create a range from a string.

Range.fromString Range.types.int "(1,5)"

types : { int : TypeConfig Basics.Int, float : TypeConfig Basics.Float, string : TypeConfig String, timestamp : TypeConfig Time.Posix }

Built-in TypeConfigs for common types.

Serialization

toString : Range subtype -> String

Convert a range to its string representation.

-- Ok [1,2)
Range.create Range.types.int (Just 1) (Just 2)
    |> Result.map Range.toString

decoder : TypeConfig subtype -> Json.Decode.Decoder (Range subtype)

JSON decoder

encode : Range subtype -> Json.Encode.Value

Encode a Range to JSON

Operators

eq : Range subtype -> Range subtype -> Basics.Bool

Equal

-- > Ok True
Result.map2 Range.eq
    (Range.create Range.types.int (Just 1) (Just 5))
    (Range.fromString Range.types.int "[1,4]")

Use this over (==) which may cause a runtime error.

neq : Range subtype -> Range subtype -> Basics.Bool

Not Equal

-- Ok True
Result.map2 Range.neq
    (Range.create Range.types.float (Just 1.1) (Just 2.2))
    (Range.create Range.types.float (Just 1.1) (Just 2.3))

lt : Range subtype -> Range subtype -> Basics.Bool

Less Than

-- Ok True
Result.map2 Range.lt
    (Range.create Range.types.int (Just 1) (Just 10))
    (Range.create Range.types.int (Just 2) (Just 3))

gt : Range subtype -> Range subtype -> Basics.Bool

Greater Than

-- Ok True
Result.map2 Range.gt
    (Range.create Range.types.int (Just 1) (Just 10))
    (Range.create Range.types.int (Just 1) (Just 5))

le : Range subtype -> Range subtype -> Basics.Bool

Less Than or Equal

-- Ok True
Result.map2 Range.lte
    (Range.create Range.types.float (Just 1.1) (Just 2.2))
    (Range.create Range.types.float (Just 1.1) (Just 2.2))

ge : Range subtype -> Range subtype -> Basics.Bool

Greater Than or Equal

-- Ok True
Result.map2 Range.gte
    (Range.create Range.types.float (Just 1.1) (Just 2.2))
    (Range.create Range.types.float (Just 1.1) (Just 2.0))

cr : Range subtype -> Range subtype -> Basics.Bool

Contains Range

-- Ok True
Result.map2 Range.cr
    (Range.create Range.types.int (Just 2) (Just 4))
    (Range.create Range.types.int (Just 2) (Just 3))

ce : Range subtype -> subtype -> Basics.Bool

Contains Element

-- Ok True
Result.map2 Range.ce
    (Range.fromString Range.types.timestamp "[2011-01-01,2011-03-01)"
    (Iso8601.toTime "2011-01-10")

ov : Range subtype -> Range subtype -> Basics.Bool

Overlap (have points in common)

-- Ok True
Result.map2 Range.ov
    (Range.create Range.types.int (Just 3) (Just 7))
    (Range.create Range.types.int (Just 4) (Just 12))

sl : Range subtype -> Range subtype -> Basics.Bool

Strictly Left of

-- Ok True
Result.map2 Range.sl
    (Range.create Range.types.int (Just 1) (Just 10))
    (Range.create Range.types.int (Just 100) (Just 110))

sr : Range subtype -> Range subtype -> Basics.Bool

Strictly Right of

-- Ok True
Result.map2 Range.sr
    (Range.create Range.types.int (Just 50) (Just 60))
    (Range.create Range.types.int (Just 20) (Just 30))

nxr : Range subtype -> Range subtype -> Basics.Bool

Does not extend to the right of

-- Ok True
Result.map2 Range.nxr
    (Range.create Range.types.int (Just 1) (Just 20))
    (Range.create Range.types.int (Just 18) (Just 20))

nxl : Range subtype -> Range subtype -> Basics.Bool

Does not extend to the left of

-- Ok True
Result.map2 Range.nxl
    (Range.create Range.types.int (Just 7) (Just 20))
    (Range.create Range.types.int (Just 5) (Just 10))

adj : Range subtype -> Range subtype -> Basics.Bool

Is Adjacent to

-- Ok True
Result.map2 Range.adj
    (Range.create Range.types.float (Just 1.1) (Just 2.2))
    (Range.create Range.types.float (Just 2.2) (Just 3.3))

union : Range subtype -> Range subtype -> Result String (Range subtype)

Union

-- Ok "[5,20)"
Result.map2 Range.adj
    (Range.create Range.types.float (Just 5) (Just 15))
    (Range.create Range.types.float (Just 10) (Just 20))
    |> Result.map Range.toString

intersect : Range subtype -> Range subtype -> Range subtype

Intersection

-- Ok [10,15)
Result.map2 Range.intersect
    (Range.create Range.types.float (Just 5) (Just 15))
    (Range.create Range.types.float (Just 10) (Just 20))
    |> Result.map Range.toString

diff : Range subtype -> Range subtype -> Result String (Range subtype)

Difference

-- Ok [5,10)
Result.map2 Range.intersect
    (Range.create Range.types.float (Just 5) (Just 15))
    (Range.create Range.types.float (Just 10) (Just 20))
    |> Result.toString

Functions

lowerElement : Range subtype -> Maybe subtype

The element of a range's lower bound.

-- Ok (Just 1.1)
Range.create Range.types.float (Just 1.1) (Just 2.2)
    |> Result.map Range.lowerElement

If the range is empty or the bound infinity then the result is Nothing.

upperElement : Range subtype -> Maybe subtype

The element of a range's upper bound.

-- Ok (Just 2.2)
Range.create Range.types.float (Just 1.1) (Just 2.2)
    |> Result.map Range.upperElement

If the range is empty or the bound infinity then the result is Nothing.

isEmpty : Range subtype -> Basics.Bool

Is the range empty?

-- Ok False
Range.create Range.types.float (Just 1.1) (Just 2.2)
    |> Result.map Range.isEmpty

lowerBoundInclusive : Range subtype -> Basics.Bool

Is the lower bound inclusive.

-- Ok True
Range.create Range.types.float (Just 1.1) (Just 2.2)
    |> Result.map Range.lowerBoundInclusive

If a range is empty or the lower bound is infinite then it is not inclusive.

upperBoundInclusive : Range subtype -> Basics.Bool

Is the upper bound inclusive.

-- Ok False
Range.create Range.types.float (Just 1.1) (Just 2.2)
    |> Result.map Range.upperBoundInclusive

If a range is empty or the upper bound is infinite then it is not inclusive.

lowerBoundInfinite : Range subtype -> Basics.Bool

Is the lower bound infinite.

-- Ok True
Range.create Range.types.float Nothing (Just 2.2)
    |> Result.map Range.lowerBoundInfinite

If a range is empty then it is not infinite.

upperBoundInfinite : Range subtype -> Basics.Bool

Is the upper bound infinite.

-- Ok False
Range.create Range.types.float Nothing (Just 2.2)
    |> Result.map Range.upperBoundInfinite

If a range is empty then it is not infinite.

merge : Range subtype -> Range subtype -> Range subtype

The smallest range which includes both of the given ranges.

Like set union, except also allow and account for non-adjacent input ranges.

-- Ok "[1,4)"
Result.map2 Range.merge
    (Range.create Range.types.int (Just 1) (Just 2))
    (Range.create Range.types.int (Just 3) (Just 4))
    |> Result.map Range.toString