Copyright | (c) 2024 Eitan Chatav |
---|---|
License | MIT |
Maintainer | eitan.chatav@gmail.com |
Stability | experimental |
Portability | non-portable (GHC extensions) |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This module defines an Opt
ional type with
either a Def
ault promoted value type,
or Some
specific demoted value term.
>>>
definite (Def :: Opt True)
True>>>
definite (Some False :: Opt (def :: Bool))
False>>>
definite (Some True :: Opt False)
True>>>
maybe "def" show (perhaps (Def :: Opt True))
"def">>>
maybe "def" show (perhaps (Some True :: Opt True))
"True">>>
maybe "def" show (perhaps (Some False :: Opt True))
"False"
The correspondence between promoted datakinds
and demoted datatypes is inexact.
Usually, Demote
t ~ t
, but not always such as:
>>>
:kind! Demote Symbol
Demote Symbol :: * = Text
Because there is no promoted Integer
and Rational
datakinds in base,
this module defines them as Z
and Q
.
>>>
:kind! Demote Z
Demote Z :: * = Integer>>>
:kind! Demote Q
Demote Q :: * = Ratio Integer
The Opt
type comes with
Num
, Integral
, Fractional
, and Real
instances,
using definite
values to do arithmetic,
which let you use literals and arithmetic to construct
Some
specific Opt
value;
>>>
4 :: Opt 20
Some 4>>>
(Def + 0) * 1 :: Opt (Pos 8 :: Z)
Some 8>>>
0.5 + Def :: Opt (Neg 1 % 3 :: Q)
Some (1 % 6)
and IsString
and IsList
instances.
>>>
"text" :: Opt ("abc" :: Symbol)
Some "text">>>
"string" :: Opt (['a','b','c'] :: String)
Some "string">>>
[[1, 2],[3,4]] :: Opt ('[] :: [[Natural]])
Some [[1, 2],[3,4]]
Opt
is a Monoid
which yields
the leftmost specific value when there are Some
,
and the Def
ault value when there are none.
>>>
definite (mempty :: Opt "xyz")
"xyz">>>
definite (Def <> "abc" <> "qrs" <> Def :: Opt "xyz")
"abc"
You can use Opt
as an optional function argument.
>>>
:{
greet :: Opt "Anon" -> Text greet name = "Welcome, " <> definite name <> "." :}>>>
greet "Sarah"
"Welcome, Sarah.">>>
greet Def
"Welcome, Anon."
Or, you can use Opt
as an optional field in your record type.
>>>
:{
data Person = Person { name :: Text , age :: Natural , alive :: Opt (True :: Bool) } :}>>>
let isAlive person = definite (alive person)
>>>
let jim = Person {name = "Jim", age = 40, alive = Def}
>>>
isAlive jim
True
You almost never need to include the datakind in your
type signatures since it's usually inferrable from def
.
Synopsis
- data Opt (def :: k) where
- type SingDef (def :: k) = (SingI def, SingKind k)
- optionally :: forall {k} def. SingDef def => Maybe (Demote k) -> Opt (def :: k)
- definite :: forall {k} def. Opt (def :: k) -> Demote k
- perhaps :: forall {k} def m. Alternative m => Opt (def :: k) -> m (Demote k)
- data Z
- type family Neg n where ...
- data Q = (:%) Z Natural
- type family z % n :: Q where ...
- type family Reduce q :: Q where ...
- type family GCD (a :: Natural) (b :: Natural) :: Natural where ...
- data SInteger (n :: Z) where
- SPos :: SNat n -> SInteger (Pos n)
- SNegOneMinus :: SNat n -> SInteger (NegOneMinus n)
- data SRational (n :: Q) where
- demote :: forall {k} (a :: k). (SingKind k, SingI a) => Demote k
- type family Demote k = (r :: Type) | r -> k
Documentation
Optional Datatype
data Opt (def :: k) where Source #
Def :: forall {k} def. SingDef def => Opt (def :: k) | |
Some :: forall {k} def. Demote k -> Opt (def :: k) |
Instances
IsString (Demote k) => IsString (Opt def) Source # | |
Defined in Data.Default.Singletons fromString :: String -> Opt def # | |
SingDef def => Monoid (Opt def) Source # | |
Semigroup (Opt def) Source # | |
IsList (Demote k) => IsList (Opt def) Source # | |
Num (Demote k) => Num (Opt def) Source # | |
(SingDef def, Read (Demote k)) => Read (Opt def) Source # | |
Fractional (Demote k) => Fractional (Opt def) Source # | |
(SingDef def, Show (Demote k)) => Show (Opt def) Source # | |
SingDef def => Default (Opt def) Source # | |
Defined in Data.Default.Singletons | |
(SingDef def, Eq (Demote k)) => Eq (Opt def) Source # | |
(SingDef def, Ord (Demote k)) => Ord (Opt def) Source # | |
type Item (Opt def) Source # | |
Defined in Data.Default.Singletons |
perhaps :: forall {k} def m. Alternative m => Opt (def :: k) -> m (Demote k) Source #
Deconstructs an Opt
to an Alternative
Demote
d value.
Def
maps to empty
,
and Some
maps to pure
,
inverting optionally
.
Promoted Datakinds
Datakind Z
, promoting Integer
,
>>>
:kind! Demote Z
Demote Z :: * = Integer
with Pos
for constructing nonnegative integer types,
and Neg
for constructing nonpositive integer types.
>>>
demote @(Pos 90210)
90210>>>
demote @(Neg 5)
-5>>>
demote @(Neg 0)
0>>>
demote @(Pos 0)
0
NonNeg
ative integer types are matched cardinally by Pos
,
>>>
:kind! Pos 9
Pos 9 :: Z = Pos 9>>>
:kind! Neg 0
Neg 0 :: Z = Pos 0
and Neg
ative integer types are matched ordinally by NegOneMinus
.
>>>
:kind! Neg 6
Neg 6 :: Z = NegOneMinus 5>>>
:kind! Neg 1
Neg 1 :: Z = NegOneMinus 0
Instances
Enum Z Source # | |
Num Z Source # | |
Read Z Source # | |
Integral Z Source # | |
Real Z Source # | |
Defined in Data.Default.Singletons toRational :: Z -> Rational # | |
Show Z Source # | |
Eq Z Source # | |
Ord Z Source # | |
SingKind Z Source # | |
KnownNat n => SingI ('NegOneMinus n :: Z) Source # | |
Defined in Data.Default.Singletons sing :: Sing ('NegOneMinus n) # | |
KnownNat n => SingI ('Pos n :: Z) Source # | |
Defined in Data.Default.Singletons | |
type Demote Z Source # | |
Defined in Data.Default.Singletons | |
type Sing Source # | |
Defined in Data.Default.Singletons |
Datakind Q
, promoting Rational
,
>>>
:kind! Demote Q
Demote Q :: * = Ratio Integer
with :%
for constructing (unreduced) and matching rational types,
>>>
demote @(Pos 7 :% 11)
7 % 11>>>
demote @(Neg 4 :% 6)
(-2) % 3>>>
:kind Pos 10 :% 10
Pos 10 :% 10 :: Q
and %
and Reduce
for constructing reduced rational types.
>>>
:kind! Pos 14 % 49
Pos 14 % 49 :: Q = Pos 2 :% 7>>>
type Percent n = Pos n :% 100
>>>
:kind! Percent 10
Percent 10 :: Q = Pos 10 :% 100>>>
:kind! Reduce (Percent 10)
Reduce (Percent 10) :: Q = Pos 1 :% 10
Instances
Num Q Source # | |
Read Q Source # | |
Fractional Q Source # | |
Real Q Source # | |
Defined in Data.Default.Singletons toRational :: Q -> Rational # | |
Show Q Source # | |
Eq Q Source # | |
Ord Q Source # | |
SingKind Q Source # | |
(SingI num, SingI denom) => SingI (num ':% denom :: Q) Source # | |
Defined in Data.Default.Singletons | |
type Demote Q Source # | |
Defined in Data.Default.Singletons | |
type Sing Source # | |
Defined in Data.Default.Singletons |
type family Reduce q :: Q where ... Source #
Perform reduction on a rational type, idempotently.
Reduce (Reduce q) ~ Reduce q
type family GCD (a :: Natural) (b :: Natural) :: Natural where ... Source #
Construct the greatest common divisor of Natural
types.
data SInteger (n :: Z) where Source #
Singleton representation for the Z
kind.
SPos :: SNat n -> SInteger (Pos n) | |
SNegOneMinus :: SNat n -> SInteger (NegOneMinus n) |
Reexport Demote
demote :: forall {k} (a :: k). (SingKind k, SingI a) => Demote k #
A convenience function that takes a type as input and demotes it to its
value-level counterpart as output. This uses SingKind
and SingI
behind
the scenes, so
.demote
= fromSing
sing
This function is intended to be used with TypeApplications
. For example:
>>>
demote @True
True
>>>
demote @(Nothing :: Maybe Ordering)
Nothing
>>>
demote @(Just EQ)
Just EQ
>>>
demote @'(True,EQ)
(True,EQ)
type family Demote k = (r :: Type) | r -> k #
Get a base type from the promoted kind. For example,
Demote Bool
will be the type Bool
. Rarely, the type and kind do not
match. For example, Demote Nat
is Natural
.