This modules contains most of the core functionality for creating and
working with Interval values.
Note: most examples assume that you have imported this module as
import Quantity.Interval as IntervalConstruct an interval containing the two given values (which can be provided in either order).
-- "The heights of people participating in the study
-- ranged from 1.2 to 1.9 meters"
heightRange =
Interval.from
(Length.meters 1.2)
(Length.meters 1.9)
-- "Please allow 4 to 6 weeks for delivery"
estimatedShippingTime =
Interval.from
(Duration.weeks 4)
(Duration.weeks 6)Construct an interval from its endpoints (the minimum and maximum values of the interval).
highwayCarSpeeds =
Interval.fromEndpoints
( Speed.kilometersPerHour 90
, Speed.kilometersPerHour 130
)The two values should be given in order but will be swapped if necessary to ensure a valid interval is returned:
Interval.endpoints <|
Interval.fromEndpoints
( Speed.kilometersPerHour 130
, Speed.kilometersPerHour 90
)
--> ( Speed.kilometersPerHour 90
--> , Speed.kilometersPerHour 130
--> )Construct a zero-width interval containing a single value.
Interval.singleton (Length.meters 3)
--> Interval.fromEndpoints
--> ( Length.meters 3
--> , Length.meters 3
--> )Construct an interval containing both of the given intervals.
firstInterval =
Interval.from (Length.feet 1) (Length.feet 2)
secondInterval =
Interval.from (Length.feet 3) (Length.feet 6)
Interval.union firstInterval secondInterval
--> Interval.from (Length.feet 1) (Length.feet 6)(Note that this is not strictly speaking a 'union' in the precise mathematical sense, since the result will contain values that are in between the two given intervals and not actually in either of them if those two intervals do not overlap.)
Attempt to construct an interval containing all the values common to both
given intervals. If the intervals do not intersect, returns Nothing.
Interval.intersection
(Interval.from (Mass.grams 1) (Mass.grams 3))
(Interval.from (Mass.grams 2) (Mass.grams 5))
--> Just (Interval.from (Mass.grams 2) (Mass.grams 3))
Interval.intersection
(Interval.from (Mass.grams 1) (Mass.grams 3))
(Interval.from (Mass.grams 4) (Mass.grams 7))
--> NothingIf the two intervals just touch, a singleton interval will be returned:
Interval.intersection
(Interval.from (Mass.grams 1) (Mass.grams 3))
(Interval.from (Mass.grams 3) (Mass.grams 5))
--> Just (Interval.singleton (Mass.grams 3))These functions let you construct an Interval containing one or more input
values.
Find the interval containing one or more input values, by passing the first value as the first argument and then a list of all other values as the second argument. For example, to find the interval containing the values 5 cm, 2 cm, 3 cm and 4 cm:
Interval.hull
(Length.centimeters 5)
[ Length.centimeters 3
, Length.centimeters 2
, Length.centimeters 4
]
--> Interval.from
--> (Length.centimeters 2)
--> (Length.centimeters 5)Why pass the first and all other values as separate arguments? It lets this
function return an Interval instead of a Maybe Interval. If there was just a
single list as an argument, then this function would have to handle the case of
an empty list being passed by returning Nothing. As a result, when using this
function you often end up using it within a case expression:
case values of
[] ->
-- some default behavior
first :: rest ->
let
interval =
Interval.hull first rest
in
-- normal behavior using 'interval'If you do want the simpler behavior of taking a single list and returning a
Maybe Interval, check out hullN.
Attempt to construct an interval containing all N values in the given
list. If the list is empty, returns Nothing. If you know you have at least one
value, you can use hull instead.
Interval.hullN
[ Duration.hours 2
, Duration.hours 1
, Duration.hours 3
]
--> Just <|
--> Interval.from
--> (Duration.hours 1)
--> (Duration.hours 3)
Interval.hullN [ Duration.hours 5]
--> Just (Interval.singleton (Duration.hours 5))
Interval.hullN []
--> NothingLike hull, but lets you work on any kind of item as long as a
Quantity of some kind can be extracted from it. For example, if you had
type alias Person =
{ name : String
, age : Duration
}then given some people you could find their range of ages as an Interval Float
Seconds using
Interval.hullOf .age
firstPerson
[ secondPerson
, thirdPerson
, fourthPerson
]See also hullOfN.
Construct an interval containing the three given values;
Interval.hull3 a b cis equivalent to
Interval.hull a [ b, c ]but is more efficient. (If you're looking for a hull2 function, from
should do what you want.)
These functions let you 'aggregate' one or more intervals into a single larger interval that contains all of them.
Construct an interval containing one or more given intervals:
Interval.aggregate
(Interval.singleton (Length.feet 2))
[ Interval.from
(Length.feet 3)
(Length.feet 4)
]
--> Interval.from (Length.feet 2) (Length.feet 4)Works much like hull. See also aggregateN.
Attemp to construct an interval containing all of the intervals in the given
list. If the list is empty, returns Nothing. If you know you have at least one
interval, you can use aggregate instead.
Combination of aggregateOf and aggregateN.
Get the endpoints of an interval (its minimum and maximum values) as a tuple. The first value will always be less than or equal to the second.
( minValue, maxValue ) =
Interval.endpoints someIntervalFor any interval,
Interval.endpoints intervalis equivalent to (but more efficient than)
( Interval.minValue interval
, Interval.maxValue interval
)Get the midpoint of an interval.
Interval.midpoint <|
Interval.from
(Duration.hours 2)
(Duration.hours 3)
--> Duration.hours 2.5Get the width of an interval.
Interval.width <|
Interval.from
(Length.meters 1.2)
(Length.meters 1.35)
--> Length.centimeters 15Check if an interval contains a given value:
angleInterval =
Interval.from
(Angle.degrees -10)
(Angle.degrees 30)
angleInterval |> Interval.contains (Angle.degrees 0)
--> True
angleInterval |> Interval.contains (Angle.degrees 45)
--> FalseThe minimum and maximum values of an interval are considered to be contained in the interval (but be careful of numerical roundoff):
angleInterval |> Interval.contains (Angle.degrees 30)
--> TrueCheck if the second interval is fully contained in the first.
angleInterval =
Interval.from
(Angle.degrees -30)
(Angle.degrees 30)
Interval.from (Angle.degrees -5) (Angle.degrees 15)
|> Interval.isContainedIn angleInterval
--> True
Interval.from (Angle.degrees 15) (Angle.degrees 45)
|> Interval.isContainedIn angleInterval
--> FalseBe careful with the argument order! If not using the |> operator, the first
example would be written as:
Interval.isContainedIn angleInterval
(Interval.from
(Angle.degrees -5)
(Angle.degrees 15)
)
--> TrueCheck if two intervals touch or overlap (have any values in common).
distanceInterval =
Interval.from
(Length.kilometers 5)
(Length.kilometers 10)
distanceInterval
|> Interval.intersects
(Interval.from
(Length.kilometers 8)
(Length.kilometers 12)
)
--> True
distanceInterval
|> Interval.intersects
(Interval.from
(Length.kilometers 12)
(Length.kilometers 15)
)
--> FalseIntervals that just touch each other are considered to intersect (this is
consistent with intersection which will return a zero-width interval for the
intersection of two just-touching intervals):
distanceInterval
|> Interval.intersects
(Interval.from
(Length.kilometers 10)
(Length.kilometers 15)
)
--> TrueCheck if the interval is a singleton (the minimum and maximum values are the same).
Interval.isSingleton <|
Interval.from (Length.meters 2) (Length.meters 2)
--> True
Interval.isSingleton <|
Interval.from (Length.meters 2) (Length.meters 3)
--> FalseInterpolate between an interval's endpoints based on a parameter value. A value of 0.0 corresponds to the minimum value of the interval, a value of 0.5 corresponds to its midpoint and a value of 1.0 corresponds to its maximum value:
lengthInterval =
Interval.from (Length.meters 1) (Length.meters 5)
Interval.interpolate lengthInterval 0
--> Length.meters 1
Interval.interpolate lengthInterval 0.75
--> Length.meters 4Values less than 0.0 or greater than 1.0 can be used to extrapolate:
Interval.interpolate lengthInterval 1.5
--> Length.meters 7Note that because of how Interval.from works, the interpolation is in
fact from the minimum value to the maximum, not "from the first
Interval.from argument to the second":
Interval.interpolate
(Interval.from
(Length.meters 0)
(Length.meters 10)
)
0.2
--> 2
Interval.interpolate
(Interval.from
(Length.meters 10)
(Length.meters 0)
)
0.2
--> 2 -- not 8!If you want to interpolate from one number down to another, you can use
Quantity.interpolateFrom
from the elm-units package.
Given an interval and a given value, determine the corresponding
interpolation parameter (the parameter that you would pass to interpolate
to get the given value):
Interval.interpolationParameter
(Interval.from
(Duration.minutes 10)
(Duration.minutes 15)
)
(Duration.minutes 12)
--> 0.4The result will be between 0 and 1 if (and only if) the given value is contained in the given interval:
Interval.interpolationParameter
(Interval.from
(Duration.minutes 10)
(Duration.minutes 15)
)
(Duration.minutes 18)
--> 1.6
Interval.interpolationParameter
(Interval.from
(Duration.minutes 10)
(Duration.minutes 15)
)
(Duration.minutes 9)
--> -0.2This is the inverse of interpolate; for any non-zero-width interval,
Interval.interpolationParameter interval value
|> Interval.interpolate intervalshould be equal to the original value (within numerical roundoff).
These functions let you do math with Interval values, following the rules of
interval arithmetic.
Negate an interval. Note that this will flip the order of the endpoints.
Interval.negate <|
Interval.from
(Angle.degrees 20)
(Angle.degrees 30)
--> Interval.from
--> (Angle.degrees -30)
--> (Angle.degrees -20)Add the given amount to an interval:
lengthInterval =
Interval.from
(Length.meters 2)
(Length.meters 3)
lengthInterval |> Interval.add (Length.centinmeters 20)
--> Interval.from
--> (Length.meters 2.2)
--> (Length.meters 3.2)Subtract the given amount from an interval.
angleInterval =
Interval.from
(Angle.degrees -10)
(Angle.degrees 50)
angleInterval |> Interval.subtract (Angle.degrees 30)
--> Interval.from
--> (Angle.degrees -40)
--> (Angle.degrees 20)Multiply an interval by a given value:
Interval.multiplyBy 5 <|
Interval.from
(Duration.minutes 20)
(Duration.minutes 30)
--> Interval.from
--> (Duration.minutes 100)
--> (Duration.minutes 150)Note that this will flip the order of the interval's endpoints if the given value is negative:
Interval.multiplyBy -2 <|
Interval.from
(Angle.degrees 20)
(Angle.degrees 30)
--> Interval.from
--> (Angle.degrees -60)
--> (Angle.degrees -40)Divide an interval by a given value:
Interval.divideBy 2 <|
Interval.from (Length.feet 2) (Length.feet 3)
--> Interval.from (Length.feet 1) (Length.feet 1.5)Note that this will flip the order of the interval's endpoints if the given value is negative:
Interval.divideBy -2 <|
Interval.from
(Angle.degrees 20)
(Angle.degrees 30)
--> Interval.from
--> (Angle.degrees -15)
--> (Angle.degrees -10)Add two intervals together.
firstInterval =
Interval.from (Length.feet 5) (Length.feet 10)
secondInterval =
Interval.from (Length.feet 2) (Length.feet 3)
firstInterval |> Interval.plus secondInterval
--> Interval.from (Length.feet 7) (Length.feet 13)Subtract the first interval from the second. This means that minus makes
the most sense when using |>:
firstInterval =
Interval.from (Length.feet 5) (Length.feet 10)
secondInterval =
Interval.from (Length.feet 2) (Length.feet 3)
firstInterval |> Interval.minus secondInterval
--> Interval.from (Length.feet 2) (Length.feet 8)Without the pipe operator, the above would be written as:
Interval.minus secondInterval firstInterval
--> Interval.from 2 8Multiply the second interval by the first. The order only matters if the
two intervals have different units (since it will affect the units of the
result) but, like other functions in this module, times is generally designed
to be used with |>:
width =
Interval.from (Length.centimeters 10) (Length.centimeters 12)
height =
Interval.from (Length.centimeters 5) (Length.centimeters 6)
width |> Interval.times height
--> Interval.from
--> (Area.squareCentimeters 50)
--> (Area.squareCentimeters 72)Get the absolute value of an interval.
Interval.abs <|
Interval.from
(Length.meters -3)
(Length.meters 2)
--> Interval.from (Length.meters 0) (Length.meters 3)Create a random generator for quantities within a given interval. For example,
Interval.randomValue <|
Interval.from (Length.meters 5) (Length.meters 10)is a Generator that will produce random lengths between 5 and 10 meters.
Represents a finite, closed interval with a minimum and maximum value.
The two type parameters match those of the
Quantitytype fromelm-units. For example, anInterval Int Pixelsmight represent a value between 200 and 300 pixels, and anInterval Float Metersmight represent a value between 2.5 and 3.7 centimeters. (As with theQuantitytype, theunitstype parameter refers to the base unit of a particular quantity type; lengths in meters, centimeters, feet, miles etc. are all internally stored in meters.)