| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Data.HList.FakePrelude
Contents
Description
The HList library
(C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
Some very basic technology for faking dependent types in Haskell.
- class Apply f a where
- class ApplyAB f a b where
- applyAB :: f -> a -> b
- data Fun cxt getb = Fun (forall a. FunCxt cxt a => a -> FunApp getb a)
- data Fun' cxt geta = Fun' (forall b. FunCxt cxt b => FunApp geta b -> b)
- type family FunApp fns a
- type family FunCxt cxts a :: Constraint
- data HPrint = HPrint
- data HRead = HRead
- data HShow = HShow
- data HComp g f = HComp g f
- data Comp = Comp
- newtype HSeq x = HSeq x
- data HFlip = HFlip
- data Label l = Label
- labelToProxy :: Label l -> Proxy l
- class ShowLabel l where
- hTrue :: Proxy True
- hFalse :: Proxy False
- type family HAnd t1 t2 :: Bool
- hAnd :: Proxy t1 -> Proxy t2 -> Proxy (HAnd t1 t2)
- type family HOr t1 t2 :: Bool
- hOr :: Proxy t1 -> Proxy t2 -> Proxy (HOr t1 t2)
- class HCond t x y z | t x y -> z where
- type family HBoolEQ t1 t2 :: Bool
- data HNat
- hZero :: Proxy HZero
- hSucc :: Proxy (n :: HNat) -> Proxy (HSucc n)
- hPred :: Proxy (HSucc n) -> Proxy n
- class HNat2Integral n where
- hNat2Integral :: Integral i => Proxy n -> i
- type family HNatEq t1 t2 :: Bool
- type family HLt x y :: Bool
- hLt :: Proxy x -> Proxy y -> Proxy (HLt x y)
- data HNothing = HNothing
- newtype HJust x = HJust x
- class HEq x y b | x y -> b
- hEq :: HEq x y b => x -> y -> Proxy b
- class Fail x
- module Data.Proxy
A heterogeneous apply operator
simpler/weaker version where type information only propagates forward
with this one. app defined below, is more complicated / verbose to define,
but it offers better type inference. Most uses have been converted to
app, so there is not much that can be done with Apply.
Minimal complete definition
Nothing
Instances
| HLookupByHNat n l => Apply (FHLookupByHNat l) (Proxy HNat n) | |
| Apply (FHUProj sel ns) (HList l, Proxy HNat (HSucc n)) => Apply (Proxy Bool False, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| Apply (Proxy Bool True, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| ((~) * ch (Proxy Bool (HBoolEQ sel (KMember n ns))), Apply (ch, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n)) => Apply (FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| Apply (FHUProj sel ns) (HList ([] *), n) |
Polymorphic functions are not first-class in haskell. One solution is to
write an instance of ApplyAB for a data type that takes the place of
the original function. In other words,
data Fn = Fn instance ApplyAB Fn a b where applyAB Fn a = actual_fn a
Normally you would have been able to pass around the definition actual_fn.
Type inference / Local functional dependencies
Note that class ApplyAB has three parameters and no functional dependencies.
Instances should be written in the style:
instance (int ~ Int, double ~ Double) => ApplyAB Fn int double where applyAB _ = fromIntegral
rather than the more natural
instance ApplyAB Fn Int Double
The first instance allows types to be inferred as if we had
class ApplyAB a b c | a -> b c, while the second instance
only matches if ghc already knows that it needs
ApplyAB Fn Int Double. Additional explanation can be found
in local functional dependencies
class ApplyAB f a b where Source
No constraints on result and argument types
Minimal complete definition
Nothing
Instances
| ((~) * f1 (a -> b -> c), (~) * f2 (b -> a -> c)) => ApplyAB HFlip f1 f2 | |
| ((~) * y y', (~) * fg (x -> y, y' -> z), (~) * r (x -> z)) => ApplyAB Comp fg r | |
| ((~) * String string, Show a) => ApplyAB HShow a string | |
| ((~) * String string, Read a) => ApplyAB HRead string a | |
| ((~) * io (IO ()), Show x) => ApplyAB HPrint x io | |
| (~) * hJustA (HJust a) => ApplyAB HFromJust hJustA a | |
| ((~) * x (e, HList l), (~) * y (HList ((:) * e l))) => ApplyAB FHCons x y | |
| (HZip3 a b c, (~) * x (HList a, HList b), (~) * y (HList c)) => ApplyAB HZipF x y | |
| ((~) * (Tagged k l (Proxy * t)) a, (~) * b (Tagged k l (Maybe t))) => ApplyAB HMaybeF a b | |
| (~) * e' e => ApplyAB HRmTag (e, t) e' | |
| (~) * hJustA (HJust a) => ApplyAB (HJust t) a hJustA |
|
| (Monad m, ApplyAB f x fx, (~) * fx (m ()), (~) * pair (x, m ()), ApplyAB f x (m ())) => ApplyAB (HSeq f) pair fx | |
| (~) * et (e, t) => ApplyAB (HAddTag t) e et | |
| ((~) * l [e'], ApplyAB f e e', (~) * el (e, l)) => ApplyAB (Mapcar f) el l | |
| (ApplyAB f (x, y) z, (~) * mz (m z), (~) * mxy (m x, m y), Applicative m) => ApplyAB (LiftA2 f) mxy mz | |
| HMapCxt f as bs as' bs' => ApplyAB (HMap f) as bs | |
| (Data b, (~) * x (t, c (b -> r)), (~) * y (c r)) => ApplyAB (GunfoldK c) x y | |
| (Data d, (~) * (c (d -> b), d) x, (~) * (c b) y) => ApplyAB (GfoldlK c) x y | |
| ApplyAB f e e' => ApplyAB (MapCar f) (e, HList l) (HList ((:) * e' l)) | |
| ((~) * x' x, (~) * y' y) => ApplyAB (x' -> y') x y | note this function will only be available at a single type
(that is, |
| (ApplyAB f a b, ApplyAB g b c) => ApplyAB (HComp g f) a c | |
| (FunCxt k cxt b, (~) * (FunApp k1 geta b) a) => ApplyAB (Fun' k k cxt geta) a b | |
| (FunCxt k cxt a, (~) * (FunApp k1 getb a) b) => ApplyAB (Fun k k cxt getb) a b |
Fun can be used instead of writing a new instance of
ApplyAB. Refer to the definition/source for the the most
concise explanation. A more wordy explanation is given below:
A type signature needs to be provided on Fun to make it work.
Depending on the kind of the parameters to Fun, a number of
different results happen.
ex1
A list of kind [* -> Constraint] produces those
constraints on the argument type:
>>>:set -XDataKinds>>>let plus1 = Fun (\x -> if x < 5 then x+1 else 5) :: Fun '[Num, Ord] '()>>>:t applyAB plus1applyAB plus1 :: (Num a, Ord a) => a -> a
Also note the use of '() to signal that the result
type is the same as the argument type.
A single constraint can also be supplied:
>>>let succ1 = Fun succ :: Fun Enum '()>>>:t applyAB succ1applyAB succ1 :: Enum a => a -> a
>>>let just = Fun Just :: Fun '[] Maybe>>>:t applyAB justapplyAB just :: a -> Maybe a
see Fun. The only difference here is that the argument
type is calculated from the result type.
>>>let rd = Fun' read :: Fun' Read String>>>:t applyAB rdapplyAB rd :: Read b => [Char] -> b
>>>let fromJust' = Fun' (\(Just a) -> a) :: Fun' '[] Maybe>>>:t applyAB fromJust'applyAB fromJust' :: Maybe b -> b
Note this use of Fun' means we don't have to get the b out of Maybe b,
type family FunCxt cxts a :: Constraint Source
Instances
| type FunCxt * cxt a = (~) * cxt a | |
| type FunCxt () cxt a = () | should there be so many ways to write no constraint? |
| type FunCxt [k] ([] k) a = () | |
| type FunCxt [* -> Constraint] ((:) (* -> Constraint) x xs) a = (x a, FunCxt [* -> Constraint] xs a) | |
| type FunCxt (* -> Constraint) cxt a = cxt a |
Simple useful instances of Apply
print. An alternative implementation could be:
>>>let hPrint = Fun print :: Fun Show (IO ())
This produces:
>>>:t applyAB hPrintapplyAB hPrint :: Show a => a -> IO ()
Constructors
| HPrint |
read
>>>applyAB HRead "5.0" :: Double5.0
Constructors
| HRead |
show
Constructors
| HShow |
Compose two instances of ApplyAB
>>>applyAB (HComp HRead HShow) (5::Double) :: Double5.0
Constructors
| HComp g f | g . f |
app Comp (f,g) = g . f. Works like:
>>>applyAB Comp (succ, pred) 'a''a'
>>>applyAB Comp (toEnum :: Int -> Char, fromEnum) 1010
Note that defaulting will sometimes give you the wrong thing
used to work (with associated types calculating result/argument types) >>> applyAB Comp (fromEnum, toEnum) 'a' *** Exception: Prelude.Enum.().toEnum: bad argument
Constructors
| Comp |
((a,b) -> f a >> b)
Constructors
| HSeq x |
Constructors
| HFlip |
Proxy
see Data.HList.Proxy
A special Proxy for record labels, polykinded
Constructors
| Label |
Instances
| (~) k x x' => ToSym * k (Label k x) x' | for Data.HList.Label6 labels |
| IsKeyFN * (Label Symbol s -> a -> b) True | labels that impose no restriction on the type of the (single) argument which follows
|
| Show desc => Show (Label * (Lbl k * x ns desc)) | |
| type LabelsOf ((:) * (Label * l) r) = (:) * l (LabelsOf r) |
labelToProxy :: Label l -> Proxy l Source
Booleans
GHC already lifts booleans, defined as
data Bool = True | False
to types: Bool becomes kind and True and False (also denoted by 'True and 'False) become nullary type constructors.
The above line is equivalent to
data HTrue data HFalse
class HBool x instance HBool HTrue instance HBool HFalse
Value-level proxies
Conjunction
Disjunction
Compare with the original code based on functional dependencies:
class (HBool t, HBool t', HBool t'') => HOr t t' t'' | t t' -> t'' where hOr :: t -> t' -> t''
instance HOr HFalse HFalse HFalse where hOr _ _ = hFalse
instance HOr HTrue HFalse HTrue where hOr _ _ = hTrue
instance HOr HFalse HTrue HTrue where hOr _ _ = hTrue
instance HOr HTrue HTrue HTrue where hOr _ _ = hTrue
Boolean equivalence
Naturals
The data type to be lifted to the type level
Instances
| HEq HNat HZero HZero True | |
| HEq HNat HZero (HSucc n) False | |
| HEq HNat (HSucc n) HZero False | |
| HEq HNat n n' b => HEq HNat (HSucc n) (HSucc n') b | |
| HTypes2HNats [*] [*] ([] *) l ([] HNat) | And lift to the list of types |
| (HType2HNat k [*] e l n, HTypes2HNats [k] [*] es l ns) => HTypes2HNats [k] [*] ((:) k e es) l ((:) HNat n ns) | |
| HLookupByHNat n l => Apply (FHLookupByHNat l) (Proxy HNat n) | |
| HNat2Integral n => Show (Proxy HNat n) | |
| Apply (FHUProj sel ns) (HList l, Proxy HNat (HSucc n)) => Apply (Proxy Bool False, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| Apply (Proxy Bool True, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| ((~) * ch (Proxy Bool (HBoolEQ sel (KMember n ns))), Apply (ch, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n)) => Apply (FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| type KMember n ([] HNat) = False | |
| type KMember n ((:) HNat n1 l) = HOr (HNatEq n n1) (KMember n l) | |
| type ApplyR (FHLookupByHNat l) (Proxy HNat n) = HLookupByHNatR n l | |
| type ApplyR (Proxy Bool False, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = ApplyR (FHUProj sel ns) (HList l, Proxy HNat (HSucc n)) | |
| type ApplyR (Proxy Bool True, FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = HJust (e, (HList l, Proxy HNat (HSucc n))) | |
| type ApplyR (FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) = ApplyR (Proxy Bool (HBoolEQ sel (KMember n ns)), FHUProj sel ns) (HList ((:) * e l), Proxy HNat n) | |
| type HNats ((:) * (Proxy HNat n) l) = (:) HNat n (HNats l) |
class HNat2Integral n where Source
Methods
hNat2Integral :: Integral i => Proxy n -> i Source
Instances
| HNat2Integral HZero | |
| HNat2Integral n => HNat2Integral (HSucc n) |
type family HNatEq t1 t2 :: Bool Source
Equality on natural numbers (eventually to be subsumed by the universal polykinded HEq)
Maybies
We cannot use lifted Maybe since the latter are not populated
Constructors
| HNothing |
Constructors
| HJust x |
Instances
| (Apply p s, HUnfold' p (ApplyR p s)) => HUnfold' p (HJust (e, s)) | |
| Show x => Show (HJust x) | |
| (~) * hJustA (HJust a) => ApplyAB (HJust t) a hJustA |
|
| FromHJust l => FromHJust ((:) * (HJust e) l) | |
| ToHJust l l' => ToHJust ((:) * e l) ((:) * (HJust e) l') | |
| type HUnfoldR p (HJust (e, s)) = (:) * e (HUnfold p s) | |
| type FromHJustR ((:) * (HJust e) l) = (:) * e (FromHJustR l) |
Polykinded Equality for types
class HEq x y b | x y -> b Source
We have to use Functional dependencies for now, for the sake of the generic equality.
Instances
| (~) Bool False b => HEq k x y b | |
| HEq k x x True | |
| HEq HNat HZero HZero True | |
| HEq HNat HZero (HSucc n) False | |
| HEq HNat (HSucc n) HZero False | |
| HEq HNat n n' b => HEq HNat (HSucc n) (HSucc n') b | |
| HEq [k] ([] k) ([] k) True | |
| HEq [k] ([] k) ((:) k e l) False | |
| HEq [k] ((:) k e l) ([] k) False | |
| (HEq k e1 e2 b1, HEq [k] l1 l2 b2, (~) Bool br (HAnd b1 b2)) => HEq [k] ((:) k e1 l1) ((:) k e2 l2) br |
Staged equality
Type-safe cast -- no longer need. We use a a ~ b
Error messages
module Data.Proxy