packages feed

first-class-families 0.4.0.0 → 0.5.0.0

raw patch · 11 files changed

+705/−344 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Fcf: data Collapse :: [Constraint] -> Exp Constraint
- Fcf: instance Fcf.IsBool 'GHC.Types.False
- Fcf: instance Fcf.IsBool 'GHC.Types.True
+ Fcf: data Constraints :: [Constraint] -> Exp Constraint
+ Fcf.Classes: data Bimap :: (a -> Exp a') -> (b -> Exp b') -> f a b -> Exp (f a' b')
+ Fcf.Classes: data Map :: (a -> Exp b) -> f a -> Exp (f b)
+ Fcf.Combinators: data ($) :: (a -> Exp b) -> a -> Exp b
+ Fcf.Combinators: data ConstFn :: a -> b -> Exp a
+ Fcf.Combinators: data Flip :: (a -> b -> Exp c) -> b -> a -> Exp c
+ Fcf.Combinators: data Join :: Exp (Exp a) -> Exp a
+ Fcf.Combinators: data LiftM2 :: (a -> b -> Exp c) -> Exp a -> Exp b -> Exp c
+ Fcf.Combinators: data LiftM3 :: (a -> b -> c -> Exp d) -> Exp a -> Exp b -> Exp c -> Exp d
+ Fcf.Combinators: data Pure :: a -> Exp a
+ Fcf.Combinators: data Pure1 :: (a -> b) -> a -> Exp b
+ Fcf.Combinators: data Pure2 :: (a -> b -> c) -> a -> b -> Exp c
+ Fcf.Combinators: data Pure3 :: (a -> b -> c -> d) -> a -> b -> c -> Exp d
+ Fcf.Combinators: infixl 4 <*>
+ Fcf.Combinators: infixr 0 $
+ Fcf.Combinators: infixr 1 <=<
+ Fcf.Combinators: type LiftM = (=<<)
+ Fcf.Core: type Exp a = a -> Type
+ Fcf.Core: type f @@ x = Eval (f x)
+ Fcf.Core: type family Eval (e :: Exp a) :: a
+ Fcf.Data.Bool: (:=) :: a -> b -> Guard a b
+ Fcf.Data.Bool: data (&&) :: Bool -> Bool -> Exp Bool
+ Fcf.Data.Bool: data Guard a b
+ Fcf.Data.Bool: data Guarded :: a -> [Guard (a -> Exp Bool) (Exp b)] -> Exp b
+ Fcf.Data.Bool: data Not :: Bool -> Exp Bool
+ Fcf.Data.Bool: data UnBool :: Exp a -> Exp a -> Bool -> Exp a
+ Fcf.Data.Bool: infixr 0 :=
+ Fcf.Data.Bool: infixr 2 ||
+ Fcf.Data.Bool: infixr 3 &&
+ Fcf.Data.Bool: type Otherwise = ConstFn 'True
+ Fcf.Data.Common: data (***) :: (b -> Exp c) -> (b' -> Exp c') -> (b, b') -> Exp (c, c')
+ Fcf.Data.Common: data FromMaybe :: k -> Maybe k -> Exp k
+ Fcf.Data.Common: data Fst :: (a, b) -> Exp a
+ Fcf.Data.Common: data IsJust :: Maybe a -> Exp Bool
+ Fcf.Data.Common: data IsLeft :: Either a b -> Exp Bool
+ Fcf.Data.Common: data IsNothing :: Maybe a -> Exp Bool
+ Fcf.Data.Common: data IsRight :: Either a b -> Exp Bool
+ Fcf.Data.Common: data Snd :: (a, b) -> Exp b
+ Fcf.Data.Common: data UnEither :: (a -> Exp c) -> (b -> Exp c) -> Either a b -> Exp c
+ Fcf.Data.Common: data UnMaybe :: Exp b -> (a -> Exp b) -> Maybe a -> Exp b
+ Fcf.Data.Common: data Uncurry :: (a -> b -> Exp c) -> (a, b) -> Exp c
+ Fcf.Data.Common: infixr 3 ***
+ Fcf.Data.List: data (++) :: [a] -> [a] -> Exp [a]
+ Fcf.Data.List: data Cons :: a -> [a] -> Exp [a]
+ Fcf.Data.List: data Cons2 :: (a, b) -> ([a], [b]) -> Exp ([a], [b])
+ Fcf.Data.List: data Filter :: (a -> Exp Bool) -> [a] -> Exp [a]
+ Fcf.Data.List: data Find :: (a -> Exp Bool) -> [a] -> Exp (Maybe a)
+ Fcf.Data.List: data FindIndex :: (a -> Exp Bool) -> [a] -> Exp (Maybe Nat)
+ Fcf.Data.List: data Foldr :: (a -> b -> Exp b) -> b -> [a] -> Exp b
+ Fcf.Data.List: data Head :: [a] -> Exp (Maybe a)
+ Fcf.Data.List: data Init :: [a] -> Exp (Maybe [a])
+ Fcf.Data.List: data Last :: [a] -> Exp (Maybe a)
+ Fcf.Data.List: data Length :: [a] -> Exp Nat
+ Fcf.Data.List: data Null :: [a] -> Exp Bool
+ Fcf.Data.List: data SetIndex :: Nat -> a -> [a] -> Exp [a]
+ Fcf.Data.List: data Tail :: [a] -> Exp (Maybe [a])
+ Fcf.Data.List: data UnList :: b -> (a -> b -> Exp b) -> [a] -> Exp b
+ Fcf.Data.List: data Unzip :: Exp [(a, b)] -> Exp ([a], [b])
+ Fcf.Data.List: data ZipWith :: (a -> b -> Exp c) -> [a] -> [b] -> Exp [c]
+ Fcf.Data.List: type Elem a as = IsJust =<< FindIndex (TyEq a) as
+ Fcf.Data.List: type Lookup (a :: k) (as :: [(k, b)]) = (Map Snd (Eval (Find (TyEq a <=< Fst) as)) :: Exp (Maybe b))
+ Fcf.Data.List: type Zip = ZipWith (Pure2 '(,))
+ Fcf.Data.Nat: data (>) :: Nat -> Nat -> Exp Bool
+ Fcf.Data.Nat: data Nat
+ Fcf.Utils: _If :: IsBool b => (b ~ 'True => r) -> (b ~ 'False => r) -> r
+ Fcf.Utils: class IsBool (b :: Bool)
+ Fcf.Utils: data Constraints :: [Constraint] -> Exp Constraint
+ Fcf.Utils: data Error :: Symbol -> Exp a
+ Fcf.Utils: data TError :: ErrorMessage -> Exp a
+ Fcf.Utils: data TyEq :: a -> b -> Exp Bool
+ Fcf.Utils: instance Fcf.Utils.IsBool 'GHC.Types.False
+ Fcf.Utils: instance Fcf.Utils.IsBool 'GHC.Types.True
+ Fcf.Utils: type family If (cond :: Bool) (tru :: k) (fls :: k) :: k
- Fcf: data ($) :: (a -> Exp b) -> a -> Exp b
+ Fcf: data (>) :: Nat -> Nat -> Exp Bool
- Fcf: infixr 0 $
+ Fcf: infixr 0 :=
- Fcf: type family If (b :: Bool) (x :: k) (y :: k) :: k
+ Fcf: type family If (cond :: Bool) (tru :: k) (fls :: k) :: k

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+# 0.5.0.0++- Modularized library++- `Fcf.Utils`:++    + Added `TError`+    + Renamed `Collapse` to `Constraints`++- `Fcf.Data.List`: Added `Cons`, `Last`, `Init`, `Elem`+ # 0.4.0.0  - New functions (blmage)
first-class-families.cabal view
@@ -1,5 +1,5 @@ name:                first-class-families-version:             0.4.0.0+version:             0.5.0.0 synopsis:   First class type families description:@@ -24,8 +24,17 @@   hs-source-dirs:      src   exposed-modules:     Fcf+    Fcf.Core+    Fcf.Combinators+    Fcf.Data.Bool+    Fcf.Data.Common+    Fcf.Data.List+    Fcf.Data.Nat+    Fcf.Classes+    Fcf.Utils   build-depends:-    base >= 4.9 && < 5+    -- This upper bound is conservative.+    base >= 4.9 && < 4.13   ghc-options:         -Wall   default-language:    Haskell2010 
src/Fcf.hs view
@@ -1,12 +1,4 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}  -- | First-class type families --@@ -39,361 +31,117 @@ -- >     TypeOperators, -- >     UndecidableInstances #-} -module Fcf where--import Data.Kind (Type, Constraint)-import GHC.TypeLits (Symbol, Nat, TypeError, ErrorMessage(..))-import qualified GHC.TypeLits as TL---- * First-class type families---- | Kind of type-level expressions indexed by their result type.-type Exp a = a -> Type---- | Expression evaluator.-type family Eval (e :: Exp a) :: a---- ** Monadic operations--infixr 1 =<<, <=<-infixl 4 <$>, <*>--data Pure :: a -> Exp a-type instance Eval (Pure x) = x--data Pure1 :: (a -> b) -> a -> Exp b-type instance Eval (Pure1 f x) = f x--data Pure2 :: (a -> b -> c) -> a -> b -> Exp c-type instance Eval (Pure2 f x y) = f x y--data Pure3 :: (a -> b -> c -> d) -> a -> b -> c -> Exp d-type instance Eval (Pure3 f x y z) = f x y z--data (=<<) :: (a -> Exp b) -> Exp a -> Exp b-type instance Eval (k =<< e) = Eval (k (Eval e))--data (<=<) :: (b -> Exp c) -> (a -> Exp b) -> a -> Exp c-type instance Eval ((f <=< g) x) = Eval (f (Eval (g x)))--type LiftM = (=<<)--data LiftM2 :: (a -> b -> Exp c) -> Exp a -> Exp b -> Exp c-type instance Eval (LiftM2 f x y) = Eval (f (Eval x) (Eval y))--data LiftM3 :: (a -> b -> c -> Exp d) -> Exp a -> Exp b -> Exp c -> Exp d-type instance Eval (LiftM3 f x y z) = Eval (f (Eval x) (Eval y) (Eval z))--data Join :: Exp (Exp a) -> Exp a-type instance Eval (Join e) = Eval (Eval e)--data (<$>) :: (a -> b) -> Exp a -> Exp b-type instance Eval (f <$> e) = f (Eval e)--data (<*>) :: Exp (a -> b) -> Exp a -> Exp b-type instance Eval (f <*> e) = Eval f (Eval e)---- ** More combinators--data Flip :: (a -> b -> Exp c) -> b -> a -> Exp c-type instance Eval (Flip f y x) = Eval (f x y)--data Uncurry :: (a -> b -> Exp c) -> (a, b) -> Exp c-type instance Eval (Uncurry f '(x, y)) = Eval (f x y)--data UnEither :: (a -> Exp c) -> (b -> Exp c) -> Either a b -> Exp c-type instance Eval (UnEither f g ('Left  x)) = Eval (f x)-type instance Eval (UnEither f g ('Right y)) = Eval (g y)--data ConstFn :: a -> b -> Exp a-type instance Eval (ConstFn a _b) = a---- ** Tuples--data Fst :: (a, b) -> Exp a-type instance Eval (Fst '(a, _b)) = a--data Snd :: (a, b) -> Exp b-type instance Eval (Snd '(_a, b)) = b--infixr 3 ***---- | Equivalent to 'Bimap'.-data (***) :: (b -> Exp c) -> (b' -> Exp c') -> (b, b') -> Exp (c, c')-type instance Eval ((***) f f' '(b, b')) = '(Eval (f b), Eval (f' b'))---- ** Lists--data Foldr :: (a -> b -> Exp b) -> b -> [a] -> Exp b-type instance Eval (Foldr f y '[]) = y-type instance Eval (Foldr f y (x ': xs)) = Eval (f x (Eval (Foldr f y xs)))---- | N.B.: This is equivalent to a 'Foldr' flipped.-data UnList :: b -> (a -> b -> Exp b) -> [a] -> Exp b-type instance Eval (UnList y f xs) = Eval (Foldr f y xs)--data (++) :: [a] -> [a] -> Exp [a]-type instance Eval ((++) '[] ys) = ys-type instance Eval ((++) (x ': xs) ys) = x ': Eval ((++) xs ys)--data Filter :: (a -> Exp Bool) -> [a] -> Exp [a]-type instance Eval (Filter _p '[]) = '[]-type instance Eval (Filter p (a ': as)) =-  If (Eval (p a))-    (a ': Eval (Filter p as))-    (Eval (Filter p as))--data Head :: [a] -> Exp (Maybe a)-type instance Eval (Head '[]) = 'Nothing-type instance Eval (Head (a ': _as)) = 'Just a--data Tail :: [a] -> Exp (Maybe [a])-type instance Eval (Tail '[]) = 'Nothing-type instance Eval (Tail (_a ': as)) = 'Just as--data Null :: [a] -> Exp Bool-type instance Eval (Null '[]) = 'True-type instance Eval (Null (a ': as)) = 'False--data Length :: [a] -> Exp Nat-type instance Eval (Length '[]) = 0-type instance Eval (Length (a ': as)) = 1 TL.+ Eval (Length as)--data Find :: (a -> Exp Bool) -> [a] -> Exp (Maybe a)-type instance Eval (Find _p '[]) = 'Nothing-type instance Eval (Find p (a ': as)) =-  If (Eval (p a))-    ('Just a)-    (Eval (Find p as))---- | Find the index of an element satisfying the predicate.-data FindIndex :: (a -> Exp Bool) -> [a] -> Exp (Maybe Nat)-type instance Eval (FindIndex _p '[]) = 'Nothing-type instance Eval (FindIndex p (a ': as)) =-  Eval (If (Eval (p a))-    (Pure ('Just 0))-    (Map ((+) 1) =<< FindIndex p as))---- | Find an element associated with a key.--- @--- 'Lookup' :: k -> [(k, b)] -> 'Exp' ('Maybe' b)--- @-type Lookup (a :: k) (as :: [(k, b)]) =-  (Map Snd (Eval (Find (TyEq a <=< Fst) as)) :: Exp (Maybe b))---- | Modify an element at a given index.------ The list is unchanged if the index is out of bounds.-data SetIndex :: Nat -> a -> [a] -> Exp [a]-type instance Eval (SetIndex n a' as) = SetIndexImpl n a' as--type family SetIndexImpl (n :: Nat) (a' :: k) (as :: [k]) where-  SetIndexImpl _n _a' '[] = '[]-  SetIndexImpl 0 a' (_a ': as) = a' ': as-  SetIndexImpl n a' (a ': as) = a ': SetIndexImpl (n TL.- 1) a' as--data ZipWith :: (a -> b -> Exp c) -> [a] -> [b] -> Exp [c]-type instance Eval (ZipWith _f '[] _bs) = '[]-type instance Eval (ZipWith _f _as '[]) = '[]-type instance Eval (ZipWith f (a ': as) (b ': bs)) =-  Eval (f a b) ': Eval (ZipWith f as bs)---- |--- @--- 'Zip' :: [a] -> [b] -> 'Exp' [(a, b)]--- @-type Zip = ZipWith (Pure2 '(,))--data Unzip :: Exp [(a, b)] -> Exp ([a], [b])-type instance Eval (Unzip as) = Eval (Foldr Cons2 '( '[], '[]) (Eval as))--data Cons2 :: (a, b) -> ([a], [b]) -> Exp ([a], [b])-type instance Eval (Cons2 '(a, b) '(as, bs)) = '(a ': as, b ': bs)---- ** Maybe--data UnMaybe :: Exp b -> (a -> Exp b) -> Maybe a -> Exp b-type instance Eval (UnMaybe y f 'Nothing) = Eval y-type instance Eval (UnMaybe y f ('Just x)) = Eval (f x)--data FromMaybe :: k -> Maybe k -> Exp k-type instance Eval (FromMaybe a 'Nothing)   = a-type instance Eval (FromMaybe _a ('Just b)) = b--data IsJust :: Maybe a -> Exp Bool-type instance Eval (IsJust ('Just _a)) = 'True-type instance Eval (IsJust 'Nothing) = 'False--data IsNothing :: Maybe a -> Exp Bool-type instance Eval (IsNothing ('Just _a)) = 'False-type instance Eval (IsNothing 'Nothing) = 'True---- ** Either--data IsLeft :: Either a b -> Exp Bool-type instance Eval (IsLeft ('Left _a)) = 'True-type instance Eval (IsLeft ('Right _a)) = 'False--data IsRight :: Either a b -> Exp Bool-type instance Eval (IsRight ('Left _a)) = 'False-type instance Eval (IsRight ('Right _a)) = 'True---- ** Overloaded functions---- | Type-level 'fmap' for type-level functors.-data Map :: (a -> Exp b) -> f a -> Exp (f b)--type instance Eval (Map f '[]) = '[]-type instance Eval (Map f (a ': as)) = Eval (f a) ': Eval (Map f as)--type instance Eval (Map f 'Nothing) = 'Nothing-type instance Eval (Map f ('Just a)) = 'Just (Eval (f a))--type instance Eval (Map f ('Left x)) = 'Left x-type instance Eval (Map f ('Right a)) = 'Right (Eval (f a))--type instance Eval (Map f '(x, a)) =-  '(x, Eval (f a))-type instance Eval (Map f '(x, y, a)) =-  '(x, y, Eval (f a))-type instance Eval (Map f '(x, y, z, a)) =-  '(x, y, z, Eval (f a))-type instance Eval (Map f '(x, y, z, w, a)) =-  '(x, y, z, w, Eval (f a))--data Bimap :: (a -> Exp a') -> (b -> Exp b') -> f a b -> Exp (f a' b')--type instance Eval (Bimap f g '(x, y)) = '(Eval (f x), Eval (g y))--type instance Eval (Bimap f g ('Left  x)) = 'Left  (Eval (f x))-type instance Eval (Bimap f g ('Right y)) = 'Right (Eval (g y))---- ** Bool---- | N.B.: The order of the two branches is the opposite of "if":--- @UnBool ifFalse ifTrue bool@.------ This mirrors the default order of constructors:------ @--- data Bool = False | True--- ----------- False < True--- @-data UnBool :: Exp a -> Exp a -> Bool -> Exp a-type instance Eval (UnBool fal tru 'False) = Eval fal-type instance Eval (UnBool fal tru 'True ) = Eval tru--infixr 2 ||-infixr 3 &&--data (||) :: Bool -> Bool -> Exp Bool-type instance Eval ('True || b) = 'True-type instance Eval (a || 'True) = 'True-type instance Eval ('False || b) = b-type instance Eval (a || 'False) = a--data (&&) :: Bool -> Bool -> Exp Bool-type instance Eval ('False && b) = 'False-type instance Eval (a && 'False) = 'False-type instance Eval ('True && b) = b-type instance Eval (a && 'True) = a--data Not :: Bool -> Exp Bool-type instance Eval (Not 'True)  = 'False-type instance Eval (Not 'False) = 'True---- | A conditional choosing the first branch whose guard @a -> 'Exp' 'Bool'@--- accepts a given value @a@.------ === Example------ @--- type UnitPrefix n = 'Eval' ('Guarded' n---   '[ 'TyEq' 0 \'':=' 'Pure' \"\"---    , 'TyEq' 1 \'':=' 'Pure' \"deci\"---    , 'TyEq' 2 \'':=' 'Pure' \"hecto\"---    , 'TyEq' 3 \'':=' 'Pure' \"kilo\"---    , 'TyEq' 6 \'':=' 'Pure' \"mega\"---    , 'TyEq' 9 \'':=' 'Pure' \"giga\"---    , 'Otherwise' \'':=' 'Error' "Something else"---    ])--- @-data Guarded :: a -> [Guard (a -> Exp Bool) (Exp b)] -> Exp b-type instance Eval (Guarded x ((p ':= y) ': ys)) =-    Eval (If (Eval (p x)) y (Guarded x ys))---- | A fancy-looking pair type to use with 'Guarded'.-data Guard a b = a := b-infixr 0 :=+module Fcf+  ( -- * First-class type families --- | A catch-all guard for 'Guarded'.-type Otherwise = ConstFn 'True+    Exp+  , Eval+  , type (@@) --- ** Nat+    -- ** Functional combinators -data (+) :: Nat -> Nat -> Exp Nat-type instance Eval ((+) a b) = a TL.+ b+  , Pure+  , Pure1+  , Pure2+  , Pure3+  , type (=<<)+  , type (<=<)+  , LiftM+  , LiftM2+  , LiftM3+  , Join+  , type (<$>)+  , type (<*>)+  , Flip+  , ConstFn+  , type ($) -data (-) :: Nat -> Nat -> Exp Nat-type instance Eval ((-) a b) = a TL.- b+    -- * Operations on common types -data (*) :: Nat -> Nat -> Exp Nat-type instance Eval ((Fcf.*) a b) = a TL.* b+    -- ** Pairs -data (^) :: Nat -> Nat -> Exp Nat-type instance Eval ((^) a b) = a TL.^ b+  , Uncurry+  , Fst+  , Snd+  , type (***) -data (<=) :: Nat -> Nat -> Exp Bool-type instance Eval ((<=) a b) = a TL.<=? b+    -- ** Either -data (>=) :: Nat -> Nat -> Exp Bool-type instance Eval ((>=) a b) = b TL.<=? a+  , UnEither+  , IsLeft+  , IsRight -data (<) :: Nat -> Nat -> Exp Bool-type instance Eval ((<) a b) = Eval (Not =<< (a >= b))+    -- ** Maybe -data (>) :: Nat -> Nat -> Exp Bool-type instance Eval ((>) a b) = Eval (Not =<< (a <= b))+  , UnMaybe+  , FromMaybe+  , IsNothing+  , IsJust --- ** Other+    -- ** Lists -data Error :: Symbol -> Exp a-type instance Eval (Error msg) = TypeError ('Text msg)+  , Foldr+  , UnList+  , type (++)+  , Filter+  , Head+  , Tail+  , Null+  , Length+  , Find+  , FindIndex+  , Lookup+  , SetIndex+  , ZipWith+  , Zip+  , Unzip+  , Cons2 -data Collapse :: [Constraint] -> Exp Constraint-type instance Eval (Collapse '[]) = () ~ ()-type instance Eval (Collapse (a ': as)) = (a, Eval (Collapse as))+    -- ** Bool -data TyEq :: a -> b -> Exp Bool-type instance Eval (TyEq a b) = TyEqImpl a b+  , UnBool+  , type (||)+  , type (&&)+  , Not -type family TyEqImpl (a :: k) (b :: k) :: Bool where-  TyEqImpl a a = 'True-  TyEqImpl a b = 'False+    -- *** Multi-way if -infixr 0 $+  , Guarded+  , Guard((:=))+  , Otherwise --- | Note that this denotes the identity function, so @($) f@ can usually be--- replaced with @f@.-data ($) :: (a -> Exp b) -> a -> Exp b-type instance Eval (($) f a) = Eval (f a)+    -- ** Nat --- | A stuck type that can be used like a type-level 'undefined'.-type family Stuck :: a+  , type (+)+  , type (-)+  , type (Fcf.Data.Nat.*)+  , type (^)+  , type (<=)+  , type (>=)+  , type (<)+  , type (>) --- * Helpful shorthands+    -- * Overloaded operations --- | Apply and evaluate a unary type function.-type f @@ x = Eval (f x)+  , Map+  , Bimap --- * Reification+    -- * Miscellaneous -class IsBool (b :: Bool) where-  _If :: ((b ~ 'True) => r) -> ((b ~ 'False) => r) -> r+  , Error+  , Constraints+  , TyEq+  , Stuck+  , IsBool(_If)+  , If -instance IsBool 'True  where _If a _ = a-instance IsBool 'False where _If _ b = b+  ) where -type family   If (b :: Bool) (x :: k) (y :: k) :: k-type instance If 'True   x _y = x-type instance If 'False _x  y = y+import Fcf.Core+import Fcf.Combinators+import Fcf.Data.Bool+import Fcf.Data.Common+import Fcf.Data.List+import Fcf.Data.Nat+import Fcf.Classes+import Fcf.Utils
+ src/Fcf/Classes.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators #-}++-- | Overloaded functions.+module Fcf.Classes+  ( Map+  , Bimap+  ) where++import Fcf.Core++-- | Type-level 'fmap' for type-level functors.+data Map :: (a -> Exp b) -> f a -> Exp (f b)++-- []+type instance Eval (Map f '[]) = '[]+type instance Eval (Map f (a ': as)) = Eval (f a) ': Eval (Map f as)++-- Maybe+type instance Eval (Map f 'Nothing) = 'Nothing+type instance Eval (Map f ('Just a)) = 'Just (Eval (f a))++-- Either+type instance Eval (Map f ('Left x)) = 'Left x+type instance Eval (Map f ('Right a)) = 'Right (Eval (f a))++-- Tuples+type instance Eval (Map f '(x, a)) =+  '(x, Eval (f a))+type instance Eval (Map f '(x, y, a)) =+  '(x, y, Eval (f a))+type instance Eval (Map f '(x, y, z, a)) =+  '(x, y, z, Eval (f a))+type instance Eval (Map f '(x, y, z, w, a)) =+  '(x, y, z, w, Eval (f a))++-- | Type-level 'Data.Bifunctor.bimap'.+data Bimap :: (a -> Exp a') -> (b -> Exp b') -> f a b -> Exp (f a' b')++-- (,)+type instance Eval (Bimap f g '(x, y)) = '(Eval (f x), Eval (g y))++-- Either+type instance Eval (Bimap f g ('Left  x)) = 'Left  (Eval (f x))+type instance Eval (Bimap f g ('Right y)) = 'Right (Eval (g y))
+ src/Fcf/Combinators.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators,+    UndecidableInstances #-}++-- | General fcf combinators.+module Fcf.Combinators+  ( Pure+  , Pure1+  , Pure2+  , Pure3+  , type (=<<)+  , type (<=<)+  , LiftM+  , LiftM2+  , LiftM3+  , Join+  , type (<$>)+  , type (<*>)+  , Flip+  , ConstFn+  , type ($)+  ) where++import Fcf.Core++-- ** Monadic operations++infixr 1 =<<, <=<+infixl 4 <$>, <*>++data Pure :: a -> Exp a+type instance Eval (Pure x) = x++data Pure1 :: (a -> b) -> a -> Exp b+type instance Eval (Pure1 f x) = f x++data Pure2 :: (a -> b -> c) -> a -> b -> Exp c+type instance Eval (Pure2 f x y) = f x y++data Pure3 :: (a -> b -> c -> d) -> a -> b -> c -> Exp d+type instance Eval (Pure3 f x y z) = f x y z++data (=<<) :: (a -> Exp b) -> Exp a -> Exp b+type instance Eval (k =<< e) = Eval (k (Eval e))++data (<=<) :: (b -> Exp c) -> (a -> Exp b) -> a -> Exp c+type instance Eval ((f <=< g) x) = Eval (f (Eval (g x)))++type LiftM = (=<<)++data LiftM2 :: (a -> b -> Exp c) -> Exp a -> Exp b -> Exp c+type instance Eval (LiftM2 f x y) = Eval (f (Eval x) (Eval y))++data LiftM3 :: (a -> b -> c -> Exp d) -> Exp a -> Exp b -> Exp c -> Exp d+type instance Eval (LiftM3 f x y z) = Eval (f (Eval x) (Eval y) (Eval z))++data Join :: Exp (Exp a) -> Exp a+type instance Eval (Join e) = Eval (Eval e)++data (<$>) :: (a -> b) -> Exp a -> Exp b+type instance Eval (f <$> e) = f (Eval e)++data (<*>) :: Exp (a -> b) -> Exp a -> Exp b+type instance Eval (f <*> e) = Eval f (Eval e)++data Flip :: (a -> b -> Exp c) -> b -> a -> Exp c+type instance Eval (Flip f y x) = Eval (f x y)++data ConstFn :: a -> b -> Exp a+type instance Eval (ConstFn a _b) = a++-- | Note that this denotes the identity function, so @($) f@ can usually be+-- replaced with @f@.+data ($) :: (a -> Exp b) -> a -> Exp b+type instance Eval (($) f a) = Eval (f a)++infixr 0 $
+ src/Fcf/Core.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators #-}++-- | The 'Eval' family.++module Fcf.Core+  ( Exp+  , Eval+  , type (@@)+  ) where++import Data.Kind (Type)++-- * First-class type families++-- | Kind of type-level expressions indexed by their result type.+type Exp a = a -> Type++-- | Expression evaluator.+type family Eval (e :: Exp a) :: a++-- ** Miscellaneous++-- | Apply and evaluate a unary type function.+type f @@ x = Eval (f x)+
+ src/Fcf/Data/Bool.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators,+    UndecidableInstances #-}++-- | Booleans.+--+-- Note that the operations from this module conflict with+-- "Data.Type.Bool".+module Fcf.Data.Bool+  ( UnBool+  , type (||)+  , type (&&)+  , Not++    -- *** Multi-way if++  , Guarded+  , Guard((:=))+  , Otherwise+  ) where++import Fcf.Core+import Fcf.Combinators (ConstFn)+import Fcf.Utils++-- | N.B.: The order of the two branches is the opposite of "if":+-- @UnBool ifFalse ifTrue bool@.+--+-- This mirrors the default order of constructors:+--+-- @+-- data Bool = False | True+-- ----------- False < True+-- @+data UnBool :: Exp a -> Exp a -> Bool -> Exp a+type instance Eval (UnBool fal tru 'False) = Eval fal+type instance Eval (UnBool fal tru 'True ) = Eval tru++infixr 2 ||+infixr 3 &&++data (||) :: Bool -> Bool -> Exp Bool+type instance Eval ('True || b) = 'True+type instance Eval (a || 'True) = 'True+type instance Eval ('False || b) = b+type instance Eval (a || 'False) = a++data (&&) :: Bool -> Bool -> Exp Bool+type instance Eval ('False && b) = 'False+type instance Eval (a && 'False) = 'False+type instance Eval ('True && b) = b+type instance Eval (a && 'True) = a++data Not :: Bool -> Exp Bool+type instance Eval (Not 'True)  = 'False+type instance Eval (Not 'False) = 'True++-- | A conditional choosing the first branch whose guard @a -> 'Exp' 'Bool'@+-- accepts a given value @a@.+--+-- === Example+--+-- @+-- type UnitPrefix n = 'Eval' ('Guarded' n+--   '[ 'TyEq' 0 \'':=' 'Pure' \"\"+--    , 'TyEq' 1 \'':=' 'Pure' \"deci\"+--    , 'TyEq' 2 \'':=' 'Pure' \"hecto\"+--    , 'TyEq' 3 \'':=' 'Pure' \"kilo\"+--    , 'TyEq' 6 \'':=' 'Pure' \"mega\"+--    , 'TyEq' 9 \'':=' 'Pure' \"giga\"+--    , 'Otherwise' \'':=' 'Error' "Something else"+--    ])+-- @+data Guarded :: a -> [Guard (a -> Exp Bool) (Exp b)] -> Exp b+type instance Eval (Guarded x ((p ':= y) ': ys)) =+    Eval (If (Eval (p x)) y (Guarded x ys))++-- | A fancy-looking pair type to use with 'Guarded'.+data Guard a b = a := b+infixr 0 :=++-- | A catch-all guard for 'Guarded'.+type Otherwise = ConstFn 'True+
+ src/Fcf/Data/Common.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators #-}++-- | Common data types: tuples, 'Either', 'Maybe'.+module Fcf.Data.Common+  ( -- ** Pairs++    Uncurry+  , Fst+  , Snd+  , type (***)++    -- ** Either++  , UnEither+  , IsLeft+  , IsRight++    -- ** Maybe++  , UnMaybe+  , FromMaybe+  , IsNothing+  , IsJust+  ) where++import Fcf.Core++-- ** Pairs++data Uncurry :: (a -> b -> Exp c) -> (a, b) -> Exp c+type instance Eval (Uncurry f '(x, y)) = Eval (f x y)++data Fst :: (a, b) -> Exp a+type instance Eval (Fst '(a, _b)) = a++data Snd :: (a, b) -> Exp b+type instance Eval (Snd '(_a, b)) = b++infixr 3 ***++-- | Equivalent to 'Bimap' for pairs.+data (***) :: (b -> Exp c) -> (b' -> Exp c') -> (b, b') -> Exp (c, c')+type instance Eval ((***) f f' '(b, b')) = '(Eval (f b), Eval (f' b'))++-- ** Either++data UnEither :: (a -> Exp c) -> (b -> Exp c) -> Either a b -> Exp c+type instance Eval (UnEither f g ('Left  x)) = Eval (f x)+type instance Eval (UnEither f g ('Right y)) = Eval (g y)++data IsLeft :: Either a b -> Exp Bool+type instance Eval (IsLeft ('Left _a)) = 'True+type instance Eval (IsLeft ('Right _a)) = 'False++data IsRight :: Either a b -> Exp Bool+type instance Eval (IsRight ('Left _a)) = 'False+type instance Eval (IsRight ('Right _a)) = 'True++-- ** Maybe++data UnMaybe :: Exp b -> (a -> Exp b) -> Maybe a -> Exp b+type instance Eval (UnMaybe y f 'Nothing) = Eval y+type instance Eval (UnMaybe y f ('Just x)) = Eval (f x)++data FromMaybe :: k -> Maybe k -> Exp k+type instance Eval (FromMaybe a 'Nothing)   = a+type instance Eval (FromMaybe _a ('Just b)) = b++data IsNothing :: Maybe a -> Exp Bool+type instance Eval (IsNothing ('Just _a)) = 'False+type instance Eval (IsNothing 'Nothing) = 'True++data IsJust :: Maybe a -> Exp Bool+type instance Eval (IsJust ('Just _a)) = 'True+type instance Eval (IsJust 'Nothing) = 'False
+ src/Fcf/Data/List.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators,+    UndecidableInstances #-}++-- | Lists.+module Fcf.Data.List+  ( Foldr+  , UnList+  , Cons+  , type (++)+  , Filter+  , Head+  , Last+  , Tail+  , Init+  , Null+  , Length+  , Find+  , FindIndex+  , Elem+  , Lookup+  , SetIndex+  , ZipWith+  , Zip+  , Unzip+  , Cons2+  ) where++import GHC.TypeLits (Nat)+import qualified GHC.TypeLits as TL++import Fcf.Core+import Fcf.Combinators+import Fcf.Classes+import Fcf.Data.Common+import Fcf.Data.Nat+import Fcf.Utils++data Cons :: a -> [a] -> Exp [a]+type instance Eval (Cons a as) = a ': as++data Foldr :: (a -> b -> Exp b) -> b -> [a] -> Exp b+type instance Eval (Foldr f y '[]) = y+type instance Eval (Foldr f y (x ': xs)) = Eval (f x (Eval (Foldr f y xs)))++-- | N.B.: This is equivalent to a 'Foldr' flipped.+data UnList :: b -> (a -> b -> Exp b) -> [a] -> Exp b+type instance Eval (UnList y f xs) = Eval (Foldr f y xs)++data (++) :: [a] -> [a] -> Exp [a]+type instance Eval ((++) '[] ys) = ys+type instance Eval ((++) (x ': xs) ys) = x ': Eval ((++) xs ys)++data Filter :: (a -> Exp Bool) -> [a] -> Exp [a]+type instance Eval (Filter _p '[]) = '[]+type instance Eval (Filter p (a ': as)) =+  If (Eval (p a))+    (a ': Eval (Filter p as))+    (Eval (Filter p as))++data Head :: [a] -> Exp (Maybe a)+type instance Eval (Head '[]) = 'Nothing+type instance Eval (Head (a ': _as)) = 'Just a++data Last :: [a] -> Exp (Maybe a)+type instance Eval (Last '[]) = 'Nothing+type instance Eval (Last (a ': '[])) = 'Just a+type instance Eval (Last (a ': b ': as)) = Eval (Last (b ': as))++data Init :: [a] -> Exp (Maybe [a])+type instance Eval (Init '[]) = 'Nothing+type instance Eval (Init (a ': '[])) = 'Just '[]+type instance Eval (Init (a ': b ': as)) =+  Eval (Map (Cons a) =<< (Init (b ': as)))++data Tail :: [a] -> Exp (Maybe [a])+type instance Eval (Tail '[]) = 'Nothing+type instance Eval (Tail (_a ': as)) = 'Just as++data Null :: [a] -> Exp Bool+type instance Eval (Null '[]) = 'True+type instance Eval (Null (a ': as)) = 'False++data Length :: [a] -> Exp Nat+type instance Eval (Length '[]) = 0+type instance Eval (Length (a ': as)) = 1 TL.+ Eval (Length as)++data Find :: (a -> Exp Bool) -> [a] -> Exp (Maybe a)+type instance Eval (Find _p '[]) = 'Nothing+type instance Eval (Find p (a ': as)) =+  If (Eval (p a))+    ('Just a)+    (Eval (Find p as))++-- | Find the index of an element satisfying the predicate.+data FindIndex :: (a -> Exp Bool) -> [a] -> Exp (Maybe Nat)+type instance Eval (FindIndex _p '[]) = 'Nothing+type instance Eval (FindIndex p (a ': as)) =+  Eval (If (Eval (p a))+    (Pure ('Just 0))+    (Map ((+) 1) =<< FindIndex p as))++type Elem a as = IsJust =<< FindIndex (TyEq a) as++-- | Find an element associated with a key.+-- @+-- 'Lookup' :: k -> [(k, b)] -> 'Exp' ('Maybe' b)+-- @+type Lookup (a :: k) (as :: [(k, b)]) =+  (Map Snd (Eval (Find (TyEq a <=< Fst) as)) :: Exp (Maybe b))++-- | Modify an element at a given index.+--+-- The list is unchanged if the index is out of bounds.+data SetIndex :: Nat -> a -> [a] -> Exp [a]+type instance Eval (SetIndex n a' as) = SetIndexImpl n a' as++type family SetIndexImpl (n :: Nat) (a' :: k) (as :: [k]) where+  SetIndexImpl _n _a' '[] = '[]+  SetIndexImpl 0 a' (_a ': as) = a' ': as+  SetIndexImpl n a' (a ': as) = a ': SetIndexImpl (n TL.- 1) a' as++data ZipWith :: (a -> b -> Exp c) -> [a] -> [b] -> Exp [c]+type instance Eval (ZipWith _f '[] _bs) = '[]+type instance Eval (ZipWith _f _as '[]) = '[]+type instance Eval (ZipWith f (a ': as) (b ': bs)) =+  Eval (f a b) ': Eval (ZipWith f as bs)++-- |+-- @+-- 'Zip' :: [a] -> [b] -> 'Exp' [(a, b)]+-- @+type Zip = ZipWith (Pure2 '(,))++data Unzip :: Exp [(a, b)] -> Exp ([a], [b])+type instance Eval (Unzip as) = Eval (Foldr Cons2 '( '[], '[]) (Eval as))++data Cons2 :: (a, b) -> ([a], [b]) -> Exp ([a], [b])+type instance Eval (Cons2 '(a, b) '(as, bs)) = '(a ': as, b ': bs)+
+ src/Fcf/Data/Nat.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE+    DataKinds,+    PolyKinds,+    TypeFamilies,+    TypeInType,+    TypeOperators,+    UndecidableInstances #-}++-- | Natural numbers.+--+-- Note that the operators from this module conflict with "GHC.TypeLits" and+-- "GHC.TypeNats".+module Fcf.Data.Nat+  ( -- * Reexported type+    -- | From "GHC.TypeNats".++    Nat++    -- * Operations++  , type (+)+  , type (-)+  , type (Fcf.Data.Nat.*)+  , type (^)+  , type (<=)+  , type (>=)+  , type (<)+  , type (>)+  ) where++import GHC.TypeLits (Nat)+import qualified GHC.TypeLits as TL++import Fcf.Core+import Fcf.Combinators+import Fcf.Data.Bool (Not)++data (+) :: Nat -> Nat -> Exp Nat+type instance Eval ((+) a b) = a TL.+ b++data (-) :: Nat -> Nat -> Exp Nat+type instance Eval ((-) a b) = a TL.- b++data (*) :: Nat -> Nat -> Exp Nat+type instance Eval ((Fcf.Data.Nat.*) a b) = a TL.* b++data (^) :: Nat -> Nat -> Exp Nat+type instance Eval ((^) a b) = a TL.^ b++data (<=) :: Nat -> Nat -> Exp Bool+type instance Eval ((<=) a b) = a TL.<=? b++data (>=) :: Nat -> Nat -> Exp Bool+type instance Eval ((>=) a b) = b TL.<=? a++data (<) :: Nat -> Nat -> Exp Bool+type instance Eval ((<) a b) = Eval (Not =<< (a >= b))++data (>) :: Nat -> Nat -> Exp Bool+type instance Eval ((>) a b) = Eval (Not =<< (a <= b))+
+ src/Fcf/Utils.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE+    AllowAmbiguousTypes,+    ConstraintKinds,+    DataKinds,+    PolyKinds,+    RankNTypes,+    TypeFamilies,+    TypeInType,+    TypeOperators #-}++-- | Miscellaneous families.+module Fcf.Utils+  ( Error+  , TError+  , Constraints+  , TyEq+  , Stuck+  , IsBool(_If)++    -- | From "Data.Type.Bool".+  , If+  ) where++import Data.Kind (Constraint)+import Data.Type.Bool (If)+import GHC.TypeLits (Symbol, TypeError, ErrorMessage(..))++import Fcf.Core++-- | Type-level 'error'.+data Error :: Symbol -> Exp a+type instance Eval (Error msg) = TypeError ('Text msg)++-- | 'TypeError' as a fcf.+data TError :: ErrorMessage -> Exp a+type instance Eval (TError msg) = TypeError msg++-- | Conjunction of a list of constraints.+data Constraints :: [Constraint] -> Exp Constraint+type instance Eval (Constraints '[]) = (() :: Constraint)+type instance Eval (Constraints (a ': as)) = (a, Eval (Constraints as))++-- | Type equality.+data TyEq :: a -> b -> Exp Bool+type instance Eval (TyEq a b) = TyEqImpl a b++type family TyEqImpl (a :: k) (b :: k) :: Bool where+  TyEqImpl a a = 'True+  TyEqImpl a b = 'False++-- | A stuck type that can be used like a type-level 'undefined'.+type family Stuck :: a++-- * Reification++class IsBool (b :: Bool) where+  _If :: ((b ~ 'True) => r) -> ((b ~ 'False) => r) -> r++instance IsBool 'True  where _If a _ = a+instance IsBool 'False where _If _ b = b