Safe Haskell | None |
---|---|
Language | Haskell2010 |
Validity
is used to specify additional invariants upon values that are not
enforced by the type system.
Let's take an example.
Suppose we were to implement a type Prime
that represents prime integers.
If you were to completely enforce the invariant that the represented number is
a prime, then we could use Natural
and only store the index of the
given prime in the infinite sequence of prime numbers.
This is very safe but also very expensive if we ever want to use the number,
because we would have to calculcate all the prime numbers until that index.
Instead we choose to implement Prime
by a newtype Prime = Prime Int
.
Now we have to maintain the invariant that the Int
that we use to represent
the prime is in fact positive and a prime.
The Validity
typeclass allows us to specify this invariant (and enables
testing via the genvalidity
libraries:
https://hackage.haskell.org/package/genvalidity ):
instance Validity Prime where isValid (Prime n) = isPrime n
If certain typeclass invariants exist, you can make these explicit in the
validity instance as well.
For example, 'Fixed a' is only valid if a
has an HasResolution
instance,
so the correct validity instance is HasResolution a => Validity (Fixed a)
.
- class Validity a where
- isInvalid :: Validity a => a -> Bool
- constructValid :: Validity a => a -> Maybe a
- constructValidUnsafe :: (Show a, Validity a) => a -> a
Documentation
class Validity a where Source #
A class of types that have additional invariants defined upon them that aren't enforced by the type system
isValid
should be an underapproximation of actual validity.
This means that if isValid
is not a perfect representation of actual
validity, for safety reasons, it should never return True
for invalid
values, but it may return False
for valid values.
For example:
isValid = const False
is a valid implementation for any type, because it never returns True
for invalid values.
isValid (Even i) = i == 2
is a valid implementation for newtype Even = Even Int
, but
isValid (Even i) = even i || i == 1
is not because it returns True
for an invalid value: '1'.
Validity Bool Source # | Trivially valid |
Validity Char Source # | Trivially valid |
Validity Double Source # | NOT trivially valid:
|
Validity Float Source # | NOT trivially valid:
|
Validity Int Source # | Trivially valid |
Validity Integer Source # | Trivially valid Integer is not trivially valid under the hood, but instantiating
|
Validity Ordering Source # | Trivially valid |
Validity Rational Source # | Valid if the contained |
Validity Word Source # | Trivially valid |
Validity Word8 Source # | Trivially valid |
Validity Word16 Source # | Trivially valid |
Validity () Source # | Trivially valid |
Validity Natural Source # | Valid according to Only available with |
Validity a => Validity [a] Source # | A list of things is valid if all of the things are valid. This means that the empty list is considered valid.
If the empty list should not be considered valid as part of your custom data
type, make sure to write a custom |
Validity a => Validity (Maybe a) Source # | A Maybe thing is valid if the thing inside is valid or it's nothing
It makes sense to assume that |
HasResolution a => Validity (Fixed a) Source # | Valid according to the contained |
(Validity a, Validity b) => Validity (Either a b) Source # | Any Either of things is valid if the contents are valid in either of the cases. |
(Validity a, Validity b) => Validity (a, b) Source # | Any tuple of things is valid if both of its elements are valid |
(Validity a, Validity b, Validity c) => Validity (a, b, c) Source # | Any tuple of things is valid if all three of its elements are valid |
constructValid :: Validity a => a -> Maybe a Source #
Construct a valid element from an unchecked element