Distance interval support.

Summary: Interval support for distances (common types + EN rules).

Reviewed By: patapizza

Differential Revision: D6942340

fbshipit-source-id: d05ff337167b11c9186f38ca04bacf0d24a41526
This commit is contained in:
Lucas Hosseini 2018-02-09 16:12:48 -08:00 committed by Facebook Github Bot
parent c8501d3e85
commit 1f7290880c
15 changed files with 338 additions and 105 deletions

View File

@ -25,36 +25,36 @@ corpus = (testContext {locale = makeLocale CS Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilometry"
, "3 km"
, "3km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 mil"
, "osm mil"
]
, examples (DistanceValue Metre 1)
, examples (simple Metre 1)
[ "1m"
, "1 metr"
]
, examples (DistanceValue Metre 2)
, examples (simple Metre 2)
[ "2m"
, "2 metry"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9 metrů"
]
, examples (DistanceValue Centimetre 1)
, examples (simple Centimetre 1)
[ "1cm"
, "1 centimetr"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centimetry"
]
, examples (DistanceValue Centimetre 9)
, examples (simple Centimetre 9)
[ "9cm"
, "9 centimetrů"
]

View File

@ -22,29 +22,50 @@ corpus = (testContext, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilometers"
, "3 km"
, "3km"
, "3k"
, "3.0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 miles"
, "eight mile"
, "8 mi"
]
, examples (DistanceValue M 9)
, examples (simple M 9)
[ "9m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centimeters"
]
, examples (DistanceValue Inch 5)
, examples (simple Inch 5)
[ "5 in"
, "5''"
, "five inches"
, "5\""
]
, examples (simple Metre 1.87)
[ "1.87 meters"
]
, examples (between Kilometre (3, 5))
[ "between 3 and 5 kilometers"
, "from 3km to 5km"
, "around 3-5 kilometers"
, "about 3km-5km"
, "3-5 kilometers"
]
, examples (under Mile 3.5)
[ "under 3.5 miles"
, "less than 3.5mi"
, "lower than three point five miles"
]
, examples (above Inch 5)
[ "more than five inches"
, "at least 5''"
, "over 5\""
, "above 5 in"
]
]

View File

@ -7,6 +7,7 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Distance.EN.Rules
@ -20,19 +21,22 @@ import Prelude
import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Distance.Types (DistanceData(..))
import qualified Duckling.Distance.Types as TDistance
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance
import qualified Duckling.Numeral.Types as TNumeral
ruleDistanceFeetInch :: Rule
ruleDistanceFeetInch = Rule
{ name = "<distance|feet> <distance|inch>"
, pattern =
[ unitDistance TDistance.Foot
, unitDistance TDistance.Inch
[ Predicate $ isDistanceOfUnit TDistance.Foot
, Predicate $ isDistanceOfUnit TDistance.Inch
]
, prod = \tokens -> case tokens of
(Token Distance DistanceData {TDistance.value = feet}:
Token Distance DistanceData {TDistance.value = inches}:
, prod = \case
(Token Distance DistanceData {TDistance.value = Just feet}:
Token Distance DistanceData {TDistance.value = Just inches}:
_) -> Just . Token Distance . withUnit TDistance.Inch . distance $
feet * 12 + inches
_ -> Nothing
@ -42,30 +46,42 @@ ruleDistanceFeetAndInch :: Rule
ruleDistanceFeetAndInch = Rule
{ name = "<distance|feet> and <distance|inch>"
, pattern =
[ unitDistance TDistance.Foot
[ Predicate $ isDistanceOfUnit TDistance.Foot
, regex "and"
, unitDistance TDistance.Inch
, Predicate $ isDistanceOfUnit TDistance.Inch
]
, prod = \tokens -> case tokens of
(Token Distance DistanceData {TDistance.value = feet}:
, prod = \case
(Token Distance DistanceData {TDistance.value = Just feet}:
_:
Token Distance DistanceData {TDistance.value = inches}:
Token Distance DistanceData {TDistance.value = Just inches}:
_) -> Just . Token Distance . withUnit TDistance.Inch . distance $
feet * 12 + inches
_ -> Nothing
}
distances :: [(Text, String, TDistance.Unit)]
distances = [ ("<latent dist> km", "k(ilo)?m?(eter)?s?", TDistance.Kilometre)
, ("<latent dist> feet", "('|f(oo|ee)?ts?)", TDistance.Foot)
, ("<latent dist> inch", "(\"|''|in(ch(es)?)?)", TDistance.Inch)
, ("<latent dist> yard", "y(ar)?ds?", TDistance.Yard)
, ("<dist> meters", "meters?", TDistance.Metre)
, ("<dist> centimeters", "cm|centimeters?", TDistance.Centimetre)
, ("<dist> miles", "mi(le(s)?)?", TDistance.Mile)
, ("<dist> m (ambiguous miles or meters)", "m", TDistance.M)
distances = [ ("km", "k(ilo)?m?(eter)?s?", TDistance.Kilometre)
, ("feet", "('|f(oo|ee)?ts?)", TDistance.Foot)
, ("inch", "(\"|''|in(ch(es)?)?)", TDistance.Inch)
, ("yard", "y(ar)?ds?", TDistance.Yard)
, ("meters", "meters?", TDistance.Metre)
, ("centimeters", "cm|centimeters?", TDistance.Centimetre)
, ("miles", "mi(le(s)?)?", TDistance.Mile)
, ("m (ambiguous miles or meters)", "m", TDistance.M)
]
rulePrecision :: Rule
rulePrecision = Rule
{ name = "about|exactly <dist>"
, pattern =
[ regex "exactly|precisely|about|approx(\\.|imately)?|close to| near( to)?|around|almost"
, dimension Distance
]
, prod = \case
(_:token:_) -> Just token
_ -> Nothing
}
ruleDistances :: [Rule]
ruleDistances = map go distances
where
@ -73,14 +89,120 @@ ruleDistances = map go distances
go (name, regexPattern, u) = Rule
{ name = name
, pattern = [ dimension Distance, regex regexPattern ]
, prod = \tokens -> case tokens of
, prod = \case
(Token Distance dd:_) -> Just . Token Distance $ withUnit u dd
_ -> Nothing
}
ruleIntervalBetweenNumeral :: Rule
ruleIntervalBetweenNumeral = Rule
{ name = "between|from <numeral> to|and <dist>"
, pattern =
[ regex "between|from"
, Predicate isPositive
, regex "to|and"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Numeral NumeralData{TNumeral.value = from}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) | from < to ->
Just . Token Distance . withInterval (from, to) $ unitOnly u
_ -> Nothing
}
ruleIntervalBetween :: Rule
ruleIntervalBetween = Rule
{ name = "between|from <dist> to|and <dist>"
, pattern =
[ regex "between|from"
, Predicate isSimpleDistance
, regex "to|and"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
_) | from < to && u1 == u2 ->
Just . Token Distance . withInterval (from, to) $ unitOnly u1
_ -> Nothing
}
ruleIntervalNumeralDash :: Rule
ruleIntervalNumeralDash = Rule
{ name = "<numeral> - <dist>"
, pattern =
[ Predicate isPositive
, regex "-"
, Predicate isSimpleDistance
]
, prod = \case
(Token Numeral NumeralData{TNumeral.value = from}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) | from < to ->
Just . Token Distance . withInterval (from, to) $ unitOnly u
_ -> Nothing
}
ruleIntervalDash :: Rule
ruleIntervalDash = Rule
{ name = "<dist> - <dist>"
, pattern =
[ Predicate isSimpleDistance
, regex "-"
, Predicate isSimpleDistance
]
, prod = \case
(Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
_) | from < to && u1 == u2 ->
Just . Token Distance . withInterval (from, to) $ unitOnly u1
_ -> Nothing
}
ruleIntervalMax :: Rule
ruleIntervalMax = Rule
{ name = "under/less/lower/no more than <dist>"
, pattern =
[ regex "under|(less|lower|not? more) than"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) -> Just . Token Distance . withMax to $ unitOnly u
_ -> Nothing
}
ruleIntervalMin :: Rule
ruleIntervalMin = Rule
{ name = "over/above/at least/more than <dist>"
, pattern =
[ regex "over|above|at least|more than"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) -> Just . Token Distance . withMin to $ unitOnly u
_ -> Nothing
}
rules :: [Rule]
rules =
[ ruleDistanceFeetInch
, ruleDistanceFeetAndInch
]
, ruleIntervalBetweenNumeral
, ruleIntervalBetween
, ruleIntervalMax
, ruleIntervalMin
, ruleIntervalNumeralDash
, ruleIntervalDash
, rulePrecision]
++ ruleDistances

View File

@ -25,24 +25,24 @@ corpus = (testContext {locale = makeLocale ES Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilómetros"
, "3 kilometros"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 miles"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9 metros"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centímetros"
]

View File

@ -24,24 +24,24 @@ corpus = (testContext {locale = makeLocale FR Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilomètres"
, "3 kilometres"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 miles"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9 metres"
, "9m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centimetres"
]

View File

@ -24,23 +24,23 @@ corpus = (testContext {locale = makeLocale GA Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 ciliméadair"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3.0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 mhíle"
, "8 míle"
]
, examples (DistanceValue M 9)
, examples (simple M 9)
[ "9m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 cheintiméadar"
]

View File

@ -23,22 +23,22 @@ corpus = (testContext {locale = makeLocale HR Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilometra"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 milja"
]
, examples (DistanceValue M 9)
, examples (simple M 9)
[ "9m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centimetra"
]

View File

@ -8,34 +8,64 @@
{-# LANGUAGE GADTs #-}
module Duckling.Distance.Helpers
( distance
, unitDistance
, isDistanceOfUnit
, isSimpleDistance
, unitOnly
, withInterval
, withMax
, withMin
, withUnit
, withValue
) where
import Prelude
import Duckling.Dimensions.Types
import Duckling.Distance.Types (DistanceData(..))
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance
-- -----------------------------------------------------------------
-- Patterns
unitDistance :: TDistance.Unit -> PatternItem
unitDistance value = Predicate $ \x -> case x of
(Token Distance DistanceData {TDistance.unit = Just unit}) -> value == unit
_ -> False
isSimpleDistance :: Predicate
isSimpleDistance (Token Distance DistanceData {TDistance.value = Just _
, TDistance.unit = Just _}) = True
isSimpleDistance _ = False
isDistanceOfUnit :: TDistance.Unit -> Predicate
isDistanceOfUnit unit (Token Distance DistanceData {TDistance.unit = Just u}) = unit == u
isDistanceOfUnit _ _ = False
-- -----------------------------------------------------------------
-- Production
distance :: Double -> DistanceData
distance x = DistanceData {TDistance.value = x, TDistance.unit = Nothing}
distance x = DistanceData {TDistance.value = Just x
, TDistance.unit = Nothing
, TDistance.minValue = Nothing
, TDistance.maxValue = Nothing}
unitOnly :: TDistance.Unit -> DistanceData
unitOnly u = DistanceData {TDistance.unit = Just u
, TDistance.value = Nothing
, TDistance.minValue = Nothing
, TDistance.maxValue = Nothing}
withUnit :: TDistance.Unit -> DistanceData -> DistanceData
withUnit value dd = dd {TDistance.unit = Just value}
withUnit u dd = dd {TDistance.unit = Just u}
withValue :: Double -> DistanceData -> DistanceData
withValue value dd = dd {TDistance.value = Just value}
withInterval :: (Double, Double) -> DistanceData -> DistanceData
withInterval (from, to) dd = dd {TDistance.minValue = Just from
, TDistance.maxValue = Just to}
withMin :: Double -> DistanceData -> DistanceData
withMin from dd = dd {TDistance.minValue = Just from}
withMax :: Double -> DistanceData -> DistanceData
withMax to dd = dd {TDistance.maxValue = Just to}

View File

@ -24,28 +24,28 @@ corpus = (testContext {locale = makeLocale KO Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 킬로미터"
, "3 킬로"
, "3 키로"
, "3 km"
, "3km"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3.0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 miles"
, "8 마일"
, "8 마일즈"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9미터"
, "9메터"
, "구메터"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 센치"
, "이센치"

View File

@ -24,25 +24,25 @@ corpus = (testContext {locale = makeLocale NL Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilometer"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
, "3,0km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 mijl"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9 m"
, "9 meter"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 cm"
, "2 centimeter"

View File

@ -24,24 +24,24 @@ corpus = (testContext {locale = makeLocale PT Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilómetros"
, "3 kilometros"
, "3 km"
, "3km"
, "3k"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 milhas"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9 metros"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centímetros"
]

View File

@ -24,24 +24,24 @@ corpus = (testContext {locale = makeLocale RO Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 kilometri"
, "3 km"
, "3km"
, "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 mile"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9m"
, "9 m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2cm"
, "2 centimetri"
]
, examples (DistanceValue Foot 10)
, examples (simple Foot 10)
[ "zece picioare"
]
]

View File

@ -25,41 +25,41 @@ corpus = (testContext {locale = makeLocale RU Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 километра"
, "3 км"
, "3км"
, "3.0 км"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 миль"
, "восемь миль"
]
, examples (DistanceValue Metre 1)
, examples (simple Metre 1)
[ "1 м",
"1 метр",
"один метр"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2см"
, "2 сантиметра"
]
, examples (DistanceValue Millimetre 4)
, examples (simple Millimetre 4)
[ "4мм"
, "4 миллиметра"
]
, examples (DistanceValue Inch 5)
, examples (simple Inch 5)
[ "5 дюймов"
, "5''"
, "пять дюймов"
, "5\""
]
, examples (DistanceValue Foot 35)
, examples (simple Foot 35)
[ "35 футов"
, "35'"
, "тридцать пять футов"
]
, examples (DistanceValue Yard 47)
, examples (simple Yard 47)
[ "47 ярдов"
, "сорок семь ярдов"
]

View File

@ -24,26 +24,26 @@ corpus = (testContext {locale = makeLocale TR Nothing}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (DistanceValue Kilometre 3)
[ examples (simple Kilometre 3)
[ "3 Kilometre"
, "3 kilometre"
, "3 km"
, "3km"
]
, examples (DistanceValue Kilometre 3.0)
, examples (simple Kilometre 3.0)
[ "3,0 km"
]
, examples (DistanceValue Mile 8)
, examples (simple Mile 8)
[ "8 Mil"
, "8 mil"
]
, examples (DistanceValue Metre 9)
, examples (simple Metre 9)
[ "9 Metre"
, "9 metre"
, "9 m"
, "9m"
]
, examples (DistanceValue Centimetre 2)
, examples (simple Centimetre 2)
[ "2 Santimetre"
, "2 santim"
, "2 cm"

View File

@ -21,6 +21,7 @@ import Data.Hashable
import Data.Text (Text)
import GHC.Generics
import Prelude
import qualified Data.HashMap.Strict as H
import qualified Data.Text as Text
import Duckling.Resolve (Resolve(..))
@ -41,26 +42,85 @@ instance ToJSON Unit where
toJSON = String . Text.toLower . Text.pack . show
data DistanceData = DistanceData
{ unit :: Maybe Unit
, value :: Double
{ unit :: Maybe Unit
, value :: Maybe Double
, minValue :: Maybe Double
, maxValue :: Maybe Double
}
deriving (Eq, Generic, Hashable, Ord, Show, NFData)
instance Resolve DistanceData where
type ResolvedValue DistanceData = DistanceValue
resolve _ DistanceData {unit = Nothing} = Nothing
resolve _ DistanceData {unit = Just unit, value} =
Just DistanceValue {vValue = value, vUnit = unit}
resolve _ DistanceData {unit = Just unit, value = Just val} =
Just $ simple unit val
resolve _ DistanceData {unit = Just unit, value = Nothing
, minValue = Just from, maxValue = Just to} =
Just $ between unit (from, to)
resolve _ DistanceData {unit = Just unit, value = Nothing
, minValue = Just from, maxValue = Nothing} =
Just $ above unit from
resolve _ DistanceData {unit = Just unit, value = Nothing
, minValue = Nothing, maxValue = Just to} =
Just $ under unit to
resolve _ _ = Nothing
data DistanceValue = DistanceValue
data IntervalDirection = Above | Under
deriving (Eq, Generic, Hashable, Ord, Show, NFData)
data SingleValue = SingleValue
{ vUnit :: Unit
, vValue :: Double
}
deriving (Eq, Ord, Show)
instance ToJSON DistanceValue where
toJSON (DistanceValue unit value) = object
instance ToJSON SingleValue where
toJSON (SingleValue unit value) = object
[ "type" .= ("value" :: Text)
, "value" .= value
, "unit" .= unit
]
data DistanceValue
= SimpleValue SingleValue
| IntervalValue (SingleValue, SingleValue)
| OpenIntervalValue (SingleValue, IntervalDirection)
deriving (Eq, Ord, Show)
instance ToJSON DistanceValue where
toJSON (SimpleValue value) = case toJSON value of
Object o -> Object $ H.insert "type" (toJSON ("value" :: Text)) o
_ -> Object H.empty
toJSON (IntervalValue (from, to)) = object
[ "type" .= ("interval" :: Text)
, "from" .= toJSON from
, "to" .= toJSON to
]
toJSON (OpenIntervalValue (from, Above)) = object
[ "type" .= ("interval" :: Text)
, "from" .= toJSON from
]
toJSON (OpenIntervalValue (to, Under)) = object
[ "type" .= ("interval" :: Text)
, "to" .= toJSON to
]
-- -----------------------------------------------------------------
-- Value helpers
simple :: Unit -> Double -> DistanceValue
simple u v = SimpleValue $ single u v
between :: Unit -> (Double, Double) -> DistanceValue
between u (from, to) = IntervalValue (single u from, single u to)
above :: Unit -> Double -> DistanceValue
above = openInterval Above
under :: Unit -> Double -> DistanceValue
under = openInterval Under
openInterval :: IntervalDirection -> Unit -> Double -> DistanceValue
openInterval direction u v = OpenIntervalValue (single u v, direction)
single :: Unit -> Double -> SingleValue
single u v = SingleValue {vUnit = u, vValue = v}