diff --git a/library/Salve.hs b/library/Salve.hs
--- a/library/Salve.hs
+++ b/library/Salve.hs
@@ -28,12 +28,78 @@
 --
 -- >>> satisfies <$> parseVersion "1.2.3" <*> parseConstraint ">1.2.0"
 -- Just True
---
--- == __Examples__
---
--- === Versions
+
+-- * Types
+Version,
+PreRelease,
+Build,
+Constraint,
+
+-- * Constructors
+makeVersion,
+initialVersion,
+constraintLT,
+constraintLE,
+constraintEQ,
+constraintGE,
+constraintGT,
+constraintAnd,
+constraintOr,
+constraintHyphen,
+constraintTilde,
+constraintCaret,
+
+-- * Parsing
+parseVersion,
+parsePreRelease,
+parseBuild,
+parseConstraint,
+
+-- ** Unsafe
+-- | These functions can be used to unsafely parse strings. Instead of
+-- returning 'Nothing', they raise an exception. Only use these if you are sure
+-- the string can be successfully parsed!
+unsafeParseVersion,
+unsafeParsePreRelease,
+unsafeParseBuild,
+unsafeParseConstraint,
+
+-- * Rendering
+renderVersion,
+renderPreRelease,
+renderBuild,
+renderConstraint,
+
+-- * Predicates
+isUnstable,
+isStable,
+
+-- * Helpers
+bumpMajor,
+bumpMinor,
+bumpPatch,
+satisfies,
+
+-- * Lenses
+-- | These lenses can be used to access and modify specific parts of a
+-- 'Version'.
 --
--- No leading zeros.
+-- Don't be scared by these type signatures. They are provided in full to avoid
+-- the @RankNTypes@ language extension. The type signature
+-- @'Functor' f => (a -> f a) -> 'Version' -> f 'Version'@ is the same as
+-- @'Lens.Micro.Lens'' 'Version' a@ (from "Lens.Micro"), which you may already
+-- be familiar with.
+majorLens,
+minorLens,
+patchLens,
+preReleasesLens,
+buildsLens,
+
+-- * Examples
+-- | TODO
+
+-- ** Versions
+-- | Leading zeros are not allowed.
 --
 -- >>> parseVersion "01.0.0"
 -- Nothing
@@ -42,7 +108,7 @@
 -- >>> parseVersion "0.0.01"
 -- Nothing
 --
--- No negative numbers.
+-- Negative numbers are not allowed.
 --
 -- >>> parseVersion "-1.0.0"
 -- Nothing
@@ -51,7 +117,7 @@
 -- >>> parseVersion "0.0.-1"
 -- Nothing
 --
--- No non-digits.
+-- Non-digits are not allowed.
 --
 -- >>> parseVersion "a.0.0"
 -- Nothing
@@ -60,24 +126,23 @@
 -- >>> parseVersion "0.0.a"
 -- Nothing
 --
--- No partial version numbers.
+-- Partial version numbers are not allowed.
 --
 -- >>> parseVersion "0.0"
 -- Nothing
 --
--- No extra version numbers.
+-- Extra version numbers are not allowed.
 --
 -- >>> parseVersion "0.0.0.0"
 -- Nothing
---
--- === Constraints
---
--- No partial version numbers.
+
+-- ** Constraints
+-- | Partial version numbers are not allowed.
 --
 -- >>> parseConstraint "1.2"
 -- Nothing
 --
--- No wildcards.
+-- Wildcards (also known as "x-ranges") are not allowed.
 --
 -- >>> parseConstraint "1.2.x"
 -- Nothing
@@ -86,127 +151,208 @@
 -- >>> parseConstraint "1.2.*"
 -- Nothing
 --
--- Round-tripping.
+-- Extra spaces are not allowed.
 --
--- >>> renderConstraint <$> parseConstraint "<=1.2.3"
--- Just "<=1.2.3"
+-- >>> parseConstraint " 1.2.3 "
+-- Nothing
+-- >>> parseConstraint "> 1.2.3"
+-- Nothing
+-- >>> parseConstraint "1.2.3  -  2.3.4"
+-- Nothing
+-- >>> parseConstraint "1.2.3  2.3.4"
+-- Nothing
+-- >>> parseConstraint "1.2.3  ||  2.3.4"
+-- Nothing
+--
+-- Most constraints can be round-tripped through parsing and rendering.
+--
 -- >>> renderConstraint <$> parseConstraint "<1.2.3"
 -- Just "<1.2.3"
--- >>> renderConstraint <$> parseConstraint "=1.2.3"
+-- >>> renderConstraint <$> parseConstraint "<=1.2.3"
+-- Just "<=1.2.3"
+-- >>> renderConstraint <$> parseConstraint "1.2.3"
 -- Just "1.2.3"
 -- >>> renderConstraint <$> parseConstraint ">=1.2.3"
 -- Just ">=1.2.3"
 -- >>> renderConstraint <$> parseConstraint ">1.2.3"
 -- Just ">1.2.3"
+-- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"
+-- Just "1.2.3 - 2.3.4"
+-- >>> renderConstraint <$> parseConstraint "~1.2.3"
+-- Just "~1.2.3"
+-- >>> renderConstraint <$> parseConstraint "^1.2.3"
+-- Just "^1.2.3"
 -- >>> renderConstraint <$> parseConstraint ">1.2.3 <2.0.0"
 -- Just ">1.2.3 <2.0.0"
 -- >>> renderConstraint <$> parseConstraint "1.2.3 || >1.2.3"
 -- Just "1.2.3 || >1.2.3"
 --
--- Implicit equals.
+-- Explicit equal signs do not get round-tripped.
 --
--- >>> renderConstraint <$> parseConstraint "1.2.3"
+-- >>> renderConstraint <$> parseConstraint "=1.2.3"
 -- Just "1.2.3"
 --
--- Hyphens.
---
--- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"
--- Just ">=1.2.3 <=2.3.4"
---
--- Tildes.
---
--- >>> renderConstraint <$> parseConstraint "~1.2.3"
--- Just ">=1.2.3 <1.3.0"
--- >>> renderConstraint <$> parseConstraint "~1.2.0"
--- Just ">=1.2.0 <1.3.0"
--- >>> renderConstraint <$> parseConstraint "~1.0.0"
--- Just ">=1.0.0 <1.1.0"
---
--- Carets.
---
--- >>> renderConstraint <$> parseConstraint "^1.2.3"
--- Just ">=1.2.3 <2.0.0"
--- >>> renderConstraint <$> parseConstraint "^0.2.3"
--- Just ">=0.2.3 <0.3.0"
--- >>> renderConstraint <$> parseConstraint "^0.0.3"
--- Just ">=0.0.3 <0.0.4"
---
--- Pre-releases and builds.
+-- Pre-releases and builds are allowed on any constraints.
 --
 -- >>> renderConstraint <$> parseConstraint "1.2.3-p+b"
 -- Just "1.2.3-p+b"
+-- >>> renderConstraint <$> parseConstraint ">1.2.3-p+b"
+-- Just ">1.2.3-p+b"
 -- >>> renderConstraint <$> parseConstraint "1.2.3-p+b - 2.3.4-p+b"
--- Just ">=1.2.3-p+b <=2.3.4-p+b"
+-- Just "1.2.3-p+b - 2.3.4-p+b"
 -- >>> renderConstraint <$> parseConstraint "~1.2.3-p+b"
--- Just ">=1.2.3-p+b <1.3.0"
+-- Just "~1.2.3-p+b"
 -- >>> renderConstraint <$> parseConstraint "^1.2.3-p+b"
--- Just ">=1.2.3-p+b <2.0.0"
-
--- * Types
-Version,
-PreRelease,
-Build,
-Constraint,
-
--- * Constructors
-makeVersion,
-initialVersion,
-constraintLT,
-constraintLE,
-constraintEQ,
-constraintGE,
-constraintGT,
-constraintAnd,
-constraintOr,
-constraintHyphen,
-constraintTilde,
-constraintCaret,
-
--- * Parsing
-parseVersion,
-parsePreRelease,
-parseBuild,
-parseConstraint,
-
--- ** Unsafe
--- | These functions can be used to unsafely parse strings. Instead of
--- returning 'Nothing', they raise an exception. Only use these if you are sure
--- the string can be successfully parsed!
-unsafeParseVersion,
-unsafeParsePreRelease,
-unsafeParseBuild,
-unsafeParseConstraint,
-
--- * Rendering
-renderVersion,
-renderPreRelease,
-renderBuild,
-renderConstraint,
-
--- * Predicates
-isUnstable,
-isStable,
-
--- * Helpers
-bumpMajor,
-bumpMinor,
-bumpPatch,
-satisfies,
+-- Just "^1.2.3-p+b"
+--
+-- These examples show every type of constraint in a single expression.
+--
+-- >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 =1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9"
+-- Just "<1.2.0 <=1.2.1 1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9"
+-- >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 || =1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9"
+-- Just "<1.2.0 <=1.2.1 || 1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9"
+-- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 =1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9"
+-- Just "<1.2.0 || <=1.2.1 1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9"
+-- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 || =1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9"
+-- Just "<1.2.0 || <=1.2.1 || 1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9"
 
--- * Lenses
--- | These lenses can be used to access and modify specific parts of a
--- 'Version'.
+-- ** Satisfying constraints
+-- | Although in general you should use 'satisfies', 'parseVersion', and
+-- 'parseConstraint', doing that here makes it hard to tell what the examples
+-- are doing. An operator makes things clearer.
 --
--- Don't be scared by these type signatures. They are provided in full to avoid
--- the @RankNTypes@ language extension. The type signature
--- @'Functor' f => (a -> f a) -> 'Version' -> f 'Version'@ is the same as
--- @'Lens.Micro.Lens'' 'Version' a@ (from "Lens.Micro"), which you may already
--- be familiar with.
-majorLens,
-minorLens,
-patchLens,
-preReleasesLens,
-buildsLens,
+-- >>> satisfies <$> parseVersion "1.2.3" <*> parseConstraint "=1.2.3"
+-- Just True
+-- >>> let version ? constraint = satisfies (unsafeParseVersion version) (unsafeParseConstraint constraint)
+-- >>> "1.2.3" ? "=1.2.3"
+-- True
+--
+-- -   Less than:
+--
+--     >>> "1.2.2" ? "<1.2.3"
+--     True
+--     >>> "1.2.3" ? "<1.2.3"
+--     False
+--     >>> "1.2.4" ? "<1.2.3"
+--     False
+--     >>> "1.2.3-pre" ? "<1.2.3"
+--     True
+--
+-- -   Less than or equal to:
+--
+--     >>> "1.2.2" ? "<=1.2.3"
+--     True
+--     >>> "1.2.3" ? "<=1.2.3"
+--     True
+--     >>> "1.2.4" ? "<=1.2.3"
+--     False
+--
+-- -   Equal to:
+--
+--     >>> "1.2.2" ? "=1.2.3"
+--     False
+--     >>> "1.2.3" ? "=1.2.3"
+--     True
+--     >>> "1.2.4" ? "=1.2.3"
+--     False
+--     >>> "1.2.3-pre" ? "=1.2.3"
+--     False
+--     >>> "1.2.3+build" ? "=1.2.3"
+--     True
+--
+-- -   Greater than or equal to:
+--
+--     >>> "1.2.2" ? ">=1.2.3"
+--     False
+--     >>> "1.2.3" ? ">=1.2.3"
+--     True
+--     >>> "1.2.4" ? ">=1.2.3"
+--     True
+--
+-- -   Greater than:
+--
+--     >>> "1.2.2" ? ">1.2.3"
+--     False
+--     >>> "1.2.3" ? ">1.2.3"
+--     False
+--     >>> "1.2.4" ? ">1.2.3"
+--     True
+--     >>> "1.2.4-pre" ? ">1.2.3"
+--     True
+--
+--     >>> "1.2.4" ? ">1.2.3-pre"
+--     True
+--
+-- -   And:
+--
+--     >>> "1.2.3" ? ">1.2.3 <1.2.5"
+--     False
+--     >>> "1.2.4" ? ">1.2.3 <1.2.5"
+--     True
+--     >>> "1.2.5" ? ">1.2.3 <1.2.5"
+--     False
+--
+-- -   Or:
+--
+--     >>> "1.2.2" ? "1.2.3 || 1.2.4"
+--     False
+--     >>> "1.2.3" ? "1.2.3 || 1.2.4"
+--     True
+--     >>> "1.2.4" ? "1.2.3 || 1.2.4"
+--     True
+--     >>> "1.2.5" ? "1.2.3 || 1.2.4"
+--     False
+--
+-- -   Hyphen:
+--
+--     >>> "1.2.2" ? "1.2.3 - 1.2.4"
+--     False
+--     >>> "1.2.3" ? "1.2.3 - 1.2.4"
+--     True
+--     >>> "1.2.4" ? "1.2.3 - 1.2.4"
+--     True
+--     >>> "1.2.5" ? "1.2.3 - 1.2.4"
+--     False
+--
+-- -   Tilde:
+--
+--     >>> "1.2.2" ? "~1.2.3"
+--     False
+--     >>> "1.2.3" ? "~1.2.3"
+--     True
+--     >>> "1.2.4" ? "~1.2.3"
+--     True
+--     >>> "1.3.0" ? "~1.2.3"
+--     False
+--
+-- -   Caret:
+--
+--     >>> "1.2.2" ? "^1.2.3"
+--     False
+--     >>> "1.2.3" ? "^1.2.3"
+--     True
+--     >>> "1.2.4" ? "^1.2.3"
+--     True
+--     >>> "1.3.0" ? "^1.2.3"
+--     True
+--     >>> "2.0.0" ? "^1.2.3"
+--     False
+--
+--     >>> "0.2.2" ? "^0.2.3"
+--     False
+--     >>> "0.2.3" ? "^0.2.3"
+--     True
+--     >>> "0.2.4" ? "^0.2.3"
+--     True
+--     >>> "0.3.0" ? "^0.2.3"
+--     False
+--
+--     >>> "0.0.2" ? "^0.0.3"
+--     False
+--     >>> "0.0.3" ? "^0.0.3"
+--     True
+--     >>> "0.0.4" ? "^0.0.3"
+--     False
 ) where
 
 import Salve.Internal
diff --git a/library/Salve/Internal.hs b/library/Salve/Internal.hs
--- a/library/Salve/Internal.hs
+++ b/library/Salve/Internal.hs
@@ -126,7 +126,8 @@
 -- Use 'parseConstraint' to create constraints and 'satisfies' to see if a
 -- version number satisfies a constraint.
 data Constraint
-  = ConstraintCompare Operator Version
+  = ConstraintOperator Operator Version
+  | ConstraintHyphen Version Version
   | ConstraintAnd Constraint Constraint
   | ConstraintOr Constraint Constraint
   deriving (Eq, Show)
@@ -157,103 +158,95 @@
 -- | Makes a new constraint that must be less than the version number.
 --
 -- >>> constraintLT <$> parseVersion "1.2.3"
--- Just (ConstraintCompare OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "<1.2.3"
--- Just (ConstraintCompare OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintLT :: Version -> Constraint
-constraintLT v = ConstraintCompare OperatorLT v
+constraintLT v = ConstraintOperator OperatorLT v
 
 -- | Makes a new constraint that must be less than or euqal to the version
 -- number.
 --
 -- >>> constraintLE <$> parseVersion "1.2.3"
--- Just (ConstraintCompare OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "<=1.2.3"
--- Just (ConstraintCompare OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintLE :: Version -> Constraint
-constraintLE v = ConstraintCompare OperatorLE v
+constraintLE v = ConstraintOperator OperatorLE v
 
 -- | Makes a new constraint that must be equal to the version number.
 --
 -- >>> constraintEQ <$> parseVersion "1.2.3"
--- Just (ConstraintCompare OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "=1.2.3"
--- Just (ConstraintCompare OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintEQ :: Version -> Constraint
-constraintEQ v = ConstraintCompare OperatorEQ v
+constraintEQ v = ConstraintOperator OperatorEQ v
 
 -- | Makes a new constraint that must be greater than or equal to the version
 -- number.
 --
 -- >>> constraintGE <$> parseVersion "1.2.3"
--- Just (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint ">=1.2.3"
--- Just (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintGE :: Version -> Constraint
-constraintGE v = ConstraintCompare OperatorGE v
+constraintGE v = ConstraintOperator OperatorGE v
 
 -- | Makes a new constraint that must be greater than the version number.
 --
 -- >>> constraintGT <$> parseVersion "1.2.3"
--- Just (ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint ">1.2.3"
--- Just (ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintGT :: Version -> Constraint
-constraintGT v = ConstraintCompare OperatorGT v
+constraintGT v = ConstraintOperator OperatorGT v
 
 -- | Makes a new constraint that must satisfy both constraints.
 --
 -- >>> constraintAnd <$> (constraintGE <$> parseVersion "1.2.3") <*> (constraintLT <$> parseVersion "2.0.0")
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
 -- >>> parseConstraint ">=1.2.3 <2.0.0"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
 constraintAnd :: Constraint -> Constraint -> Constraint
 constraintAnd l r = ConstraintAnd l r
 
 -- | Makes a new constraint that must satisfy either constraint.
 --
 -- >>> constraintOr <$> (constraintEQ <$> parseVersion "1.2.3") <*> (constraintGT <$> parseVersion "1.2.3")
--- Just (ConstraintOr (ConstraintCompare OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))
 -- >>> parseConstraint "=1.2.3 || >1.2.3"
--- Just (ConstraintOr (ConstraintCompare OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))
 constraintOr :: Constraint -> Constraint -> Constraint
 constraintOr l r = ConstraintOr l r
 
 -- | Makes a new constraint that must be between the versions, inclusive.
 --
 -- >>> constraintHyphen <$> parseVersion "1.2.3" <*> parseVersion "2.3.4"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLE (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "1.2.3 - 2.3.4"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLE (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))
 constraintHyphen :: Version -> Version -> Constraint
-constraintHyphen v w = constraintAnd (constraintGE v) (constraintLE w)
+constraintHyphen v w = ConstraintHyphen v w
 
 -- | Makes a new constraint that allows changes to the patch version number.
 --
 -- >>> constraintTilde <$> parseVersion "1.2.3"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 1, versionMinor = 3, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "~1.2.3"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 1, versionMinor = 3, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintTilde :: Version -> Constraint
-constraintTilde v = constraintAnd
-  (constraintGE v)
-  (constraintLT (makeVersion (versionMajor v) (versionMinor v + 1) 0 [] []))
+constraintTilde v = ConstraintOperator OperatorTilde v
 
 -- | Makes a new constraint that allows changes that do not modify the
 -- left-most non-zero version number.
 --
 -- >>> constraintCaret <$> parseVersion "1.2.3"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 -- >>> parseConstraint "^1.2.3"
--- Just (ConstraintAnd (ConstraintCompare OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintCompare OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))
+-- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 constraintCaret :: Version -> Constraint
-constraintCaret v = constraintAnd
-  (constraintGE v)
-  (constraintLT (if versionMajor v == 0
-    then if versionMinor v == 0
-      then makeVersion (versionMajor v) (versionMinor v) (versionPatch v + 1) [] []
-      else makeVersion (versionMajor v) (versionMinor v + 1) 0 [] []
-    else makeVersion (versionMajor v + 1) 0 0 [] []))
+constraintCaret v = ConstraintOperator OperatorCaret v
 
 -- | Attempts to parse a version. This parser follows [SemVer's
 -- BNF](https://github.com/mojombo/semver/blob/eb9aac5/semver.md#backusnaur-form-grammar-for-valid-semver-versions).
@@ -317,7 +310,7 @@
 -- not supported. So you cannot use @1.2.x@ or @>1.2@ as version constraints.
 --
 -- >>> parseConstraint ">1.2.3"
--- Just (ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
 --
 -- Returns 'Nothing' if the parse fails.
 --
@@ -380,7 +373,7 @@
 -- | Parses a constraint.
 --
 -- >>> unsafeParseConstraint ">1.2.3"
--- ConstraintCompare OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})
+-- ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})
 --
 -- Raises an exception if the parse fails.
 --
@@ -441,7 +434,7 @@
 -- Just "1.2.3"
 renderConstraint :: Constraint -> String
 renderConstraint c = case c of
-  ConstraintCompare o v ->
+  ConstraintOperator o v ->
     let s = renderVersion v
     in case o of
       OperatorLT -> '<' : s
@@ -449,6 +442,9 @@
       OperatorEQ -> s
       OperatorGE -> '>' : '=' : s
       OperatorGT -> '>' : s
+      OperatorTilde -> '~' : s
+      OperatorCaret -> '^' : s
+  ConstraintHyphen l r -> join ' ' [renderVersion l, "-", renderVersion r]
   ConstraintAnd l r -> join ' ' (map renderConstraint [l, r])
   ConstraintOr l r -> join ' ' [renderConstraint l, "||", renderConstraint r]
 
@@ -537,12 +533,22 @@
 -- Just True
 satisfies :: Version -> Constraint -> Bool
 satisfies v c = case c of
-  ConstraintCompare o w -> case o of
-    OperatorLE -> v <= w
+  ConstraintOperator o w -> case o of
     OperatorLT -> v < w
+    OperatorLE -> v <= w
     OperatorEQ -> compare v w == EQ
-    OperatorGT -> v > w
     OperatorGE -> v >= w
+    OperatorGT -> v > w
+    OperatorTilde -> (v >= w) && (v < makeVersion (versionMajor w) (versionMinor w + 1) 0 [] [])
+    OperatorCaret ->
+      let u =
+            if versionMajor w == 0
+            then if versionMinor w == 0
+            then makeVersion (versionMajor w) (versionMinor w) (versionPatch w + 1) [] []
+            else makeVersion (versionMajor w) (versionMinor w + 1) 0 [] []
+            else makeVersion (versionMajor w + 1) 0 0 [] []
+      in (v >= w) && (v < u)
+  ConstraintHyphen l r -> (l <= v) && (v <= r)
   ConstraintAnd l r -> satisfies v l && satisfies v r
   ConstraintOr l r -> satisfies v l || satisfies v r
 
@@ -611,6 +617,8 @@
   | OperatorEQ
   | OperatorGE
   | OperatorGT
+  | OperatorTilde
+  | OperatorCaret
   deriving (Eq, Show)
 
 -- ** Parsing
@@ -672,13 +680,13 @@
 
 constraintsP :: Parser Constraint
 constraintsP = do
-  _ <- spacesP
   cs <- sepBy1P orP constraintP
-  _ <- spacesP
   pure (foldr1 constraintOr cs)
 
 constraintP :: Parser Constraint
-constraintP = choiceP hyphenatedP simplesP
+constraintP = do
+  cs <- sepBy1P spaceP simpleP
+  pure (foldr1 constraintAnd cs)
 
 hyphenatedP :: Parser Constraint
 hyphenatedP = do
@@ -687,13 +695,8 @@
   w <- versionP
   pure (constraintHyphen v w)
 
-simplesP :: Parser Constraint
-simplesP = do
-  cs <- sepBy1P spaceP simpleP
-  pure (foldr1 constraintAnd cs)
-
 simpleP :: Parser Constraint
-simpleP = choiceP caretP (choiceP tildeP primitiveP)
+simpleP = oneOfP [hyphenatedP, caretP, tildeP, primitiveP]
 
 caretP :: Parser Constraint
 caretP = do
@@ -711,7 +714,7 @@
 primitiveP = do
   o <- operatorP
   v <- versionP
-  pure (ConstraintCompare o v)
+  pure (ConstraintOperator o v)
 
 operatorP :: Parser Operator
 operatorP = oneOfP [leP, geP, ltP, gtP, eqP, pure OperatorEQ]
@@ -746,9 +749,6 @@
 
 orP :: Parser String
 orP = stringP " || "
-
-spacesP :: Parser String
-spacesP = manyP spaceP
 
 spaceP :: Parser Char
 spaceP = charP ' '
diff --git a/salve.cabal b/salve.cabal
--- a/salve.cabal
+++ b/salve.cabal
@@ -3,10 +3,12 @@
 -- see: https://github.com/sol/hpack
 
 name:           salve
-version:        0.0.1
+version:        0.0.2
 synopsis:       Semantic version numbers and constraints.
 description:    Salve provides semantic version numbers and constraints.
 category:       Distribution
+homepage:       https://github.com/tfausak/salve#readme
+bug-reports:    https://github.com/tfausak/salve/issues
 maintainer:     Taylor Fausak
 license:        MIT
 license-file:   LICENSE.markdown
@@ -16,6 +18,10 @@
 extra-source-files:
     CHANGELOG.markdown
     README.markdown
+
+source-repository head
+  type: git
+  location: https://github.com/tfausak/salve
 
 library
   hs-source-dirs:
