first-class-families-0.6.0.0/src/0000755000000000000000000000000013537433331014656 5ustar0000000000000000first-class-families-0.6.0.0/src/Fcf/0000755000000000000000000000000013537433514015357 5ustar0000000000000000first-class-families-0.6.0.0/src/Fcf/Data/0000755000000000000000000000000013537433331016225 5ustar0000000000000000first-class-families-0.6.0.0/test/0000755000000000000000000000000013537433331015046 5ustar0000000000000000first-class-families-0.6.0.0/src/Fcf.hs0000644000000000000000000000417613537433331015720 0ustar0000000000000000{-# LANGUAGE TypeOperators #-} -- | First-class type families -- -- For example, here is a regular type family: -- -- @ -- type family FromMaybe (a :: k) (m :: Maybe k) :: k -- type instance FromMaybe a 'Nothing = a -- type instance FromMaybe a ('Just b) = b -- @ -- -- With @Fcf@, it translates to a @data@ declaration: -- -- @ -- data FromMaybe :: k -> Maybe k -> 'Exp' k -- type instance 'Eval' (FromMaybe a 'Nothing) = a -- type instance 'Eval' (FromMaybe a ('Just b)) = b -- @ -- -- - Fcfs can be higher-order. -- - The kind constructor 'Exp' is a monad: there's @('=<<')@ and 'Pure'. -- -- Essential language extensions for "Fcf": -- -- > {-# LANGUAGE -- > DataKinds, -- > PolyKinds, -- > TypeFamilies, -- > TypeInType, -- > TypeOperators, -- > UndecidableInstances #-} module Fcf ( -- * First-class type families Exp , Eval , type (@@) -- ** Functional combinators , Pure , Pure1 , Pure2 , Pure3 , type (=<<) , type (<=<) , LiftM , LiftM2 , LiftM3 , Join , type (<$>) , type (<*>) , Flip , ConstFn , type ($) -- * Operations on common types -- ** Pairs , Uncurry , Fst , Snd , type (***) -- ** Either , UnEither , IsLeft , IsRight -- ** Maybe , UnMaybe , FromMaybe , IsNothing , IsJust -- ** Lists , Foldr , UnList , type (++) , Filter , Head , Tail , Null , Length , Find , FindIndex , Lookup , SetIndex , ZipWith , Zip , Unzip , Cons2 -- ** Bool , UnBool , type (||) , type (&&) , Not -- *** Multi-way if , Guarded , Guard((:=)) , Otherwise -- ** Case splitting , Case , Match() , type (-->) , Is , Any , Else -- ** Nat , type (+) , type (-) , type (Fcf.Data.Nat.*) , type (^) , type (<=) , type (>=) , type (<) , type (>) -- * Overloaded operations , Map , Bimap -- * Miscellaneous , Error , Constraints , TyEq , Stuck , IsBool(_If) , If ) where 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 first-class-families-0.6.0.0/src/Fcf/Core.hs0000644000000000000000000000074713416124623016605 0ustar0000000000000000{-# 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) first-class-families-0.6.0.0/src/Fcf/Combinators.hs0000644000000000000000000000371413537433514020200 0ustar0000000000000000{-# 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 infixl 1 >>= 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 (>>=) :: Exp a -> (a -> Exp b) -> Exp b type instance Eval (e >>= k) = 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 $ first-class-families-0.6.0.0/src/Fcf/Data/Bool.hs0000644000000000000000000000421213537433331017453 0ustar0000000000000000{-# 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)) {-# DEPRECATED Guarded "Use 'Case' instead" #-} -- | 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 first-class-families-0.6.0.0/src/Fcf/Data/Common.hs0000644000000000000000000000354313423202524020006 0ustar0000000000000000{-# 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 first-class-families-0.6.0.0/src/Fcf/Data/List.hs0000644000000000000000000000751513537433327017511 0ustar0000000000000000{-# 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 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) first-class-families-0.6.0.0/src/Fcf/Data/Nat.hs0000644000000000000000000000250213537433327017307 0ustar0000000000000000{-# LANGUAGE CPP, DataKinds, PolyKinds, TypeFamilies, TypeInType, TypeOperators, UndecidableInstances #-} #if __GLASGOW_HASKELL__ >= 806 {-# LANGUAGE NoStarIsType #-} #endif -- | 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)) first-class-families-0.6.0.0/src/Fcf/Classes.hs0000644000000000000000000000231413423325417017304 0ustar0000000000000000{-# 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)) first-class-families-0.6.0.0/src/Fcf/Utils.hs0000644000000000000000000000560613537433331017017 0ustar0000000000000000{-# LANGUAGE AllowAmbiguousTypes, ConstraintKinds, DataKinds, PolyKinds, RankNTypes, TypeFamilies, TypeInType, TypeOperators, UndecidableInstances #-} -- | Miscellaneous families. module Fcf.Utils ( Error , TError , Constraints , TyEq , Stuck , IsBool(_If) , Case , Match() , type (-->) , Is , Any , Else -- * 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 -- * Case splitting infix 0 --> data Match j k = Match_ j k | Is_ (j -> Exp Bool) k | Any_ k | Else_ (j -> Exp k) -- | (Limited) equivalent of @\\case { .. }@ syntax. Supports matching of exact -- values ('-->') and final matches for any value ('Any') or for passing value -- to subcomputation ('Else'). Examples: -- -- @ -- type BoolToNat = Case -- [ 'True --> 0 -- , 'False --> 1 -- ] -- -- type NatToBool = Case -- [ 0 --> 'False -- , Any 'True -- ] -- -- type ZeroOneOrSucc = Case -- [ 0 --> 0 -- , 1 --> 1 -- , Else ((+) 1) -- ] -- @ data Case :: [Match j k] -> j -> Exp k type instance Eval (Case ms a) = Case_ ms a type family Case_ (ms :: [Match j k]) (a :: j) :: k where Case_ ('Match_ a b : _ ) a = b Case_ ('Match_ _ _ : ms) a = Case_ ms a Case_ ('Is_ p b : ms) a = Case_ [ 'True --> b , 'False --> Case_ ms a ] (p @@ a) Case_ ('Any_ b : _ ) _ = b Case_ ('Else_ f : _ ) a = f @@ a -- | Match concrete type in 'Case'. type (-->) = ('Match_ :: j -> k -> Match j k) -- | Match on predicate being successful with type in 'Case'. type Is = ('Is_ :: (j -> Exp Bool) -> k -> Match j k) -- | Match any type in 'Case'. Should be used as a final branch. type Any = ('Any_ :: k -> Match j k) -- | Pass type being matched in 'Case' to subcomputation. Should be used as a -- final branch. type Else = ('Else_ :: (j -> Exp k) -> Match j k)first-class-families-0.6.0.0/test/test.hs0000644000000000000000000000141613537433331016363 0ustar0000000000000000{-# LANGUAGE DataKinds, KindSignatures, TypeOperators #-} import Data.Type.Equality ((:~:)(Refl)) import GHC.TypeLits (Nat) import Fcf type UnitPrefix (n :: Nat) = 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" ]) type UnitPrefix' = Case [ 0 --> "" , 1 --> "deci" , 2 --> "hecto" , 3 --> "kilo" , 6 --> "mega" , 9 --> "giga" , Any (Error @@ "Something Else") ] -- Compile-time tests _ = Refl :: UnitPrefix 0 :~: "" _ = Refl :: UnitPrefix 9 :~: "giga" _ = Refl :: Eval (UnitPrefix' 0) :~: "" _ = Refl :: Eval (UnitPrefix' 3) :~: "kilo" -- Dummy main :: IO () main = pure () first-class-families-0.6.0.0/LICENSE0000644000000000000000000000204513320552756015100 0ustar0000000000000000Copyright Li-yao Xia (c) 2018 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.first-class-families-0.6.0.0/Setup.hs0000644000000000000000000000005613320552756015527 0ustar0000000000000000import Distribution.Simple main = defaultMain first-class-families-0.6.0.0/first-class-families.cabal0000644000000000000000000000243013537433751021101 0ustar0000000000000000name: first-class-families version: 0.6.0.0 synopsis: First class type families description: First class type families, eval-style defunctionalization . See "Fcf". homepage: https://github.com/Lysxia/first-class-families#readme license: MIT license-file: LICENSE author: Li-yao Xia maintainer: lysxia@gmail.com copyright: 2018 Li-yao Xia category: Other build-type: Simple extra-source-files: README.md, CHANGELOG.md cabal-version: >=1.10 tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.1 library 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: -- This upper bound is conservative. base >= 4.9 && < 4.14 ghc-options: -Wall default-language: Haskell2010 test-suite fcf-test type: exitcode-stdio-1.0 hs-source-dirs: test main-is: test.hs default-language: Haskell2010 build-depends: base, first-class-families source-repository head type: git location: https://github.com/Lysxia/first-class-families first-class-families-0.6.0.0/README.md0000644000000000000000000000247713405753611015360 0ustar0000000000000000# First-class type families [![Hackage](https://img.shields.io/hackage/v/first-class-families.svg)](https://hackage.haskell.org/package/first-class-families) [![Build Status](https://travis-ci.org/Lysxia/first-class-families.svg)](https://travis-ci.org/Lysxia/first-class-families) For example, consider this simple type family: ```haskell type family FromMaybe (a :: k) (m :: Maybe k) :: k type instance FromMaybe a 'Nothing = a type instance FromMaybe a ('Just b) = b ``` With first-class-families, it translates to a `data` declaration and instances for a single `Eval` family: ```haskell import Fcf data FromMaybe :: k -> Maybe k -> Exp k type instance Eval (FromMaybe a 'Nothing) = a type instance Eval (FromMaybe a ('Just b)) = b ``` That way, the `FromMaybe` constructor can be passed to higher-order fcfs. ```haskell Eval (Map (FromMaybe 0) '[ 'Just 1, 'Nothing ]) = '[ 1, 0 ] :: [Nat] ``` Essential language extensions: ```haskell {-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeInType, TypeOperators, UndecidableInstances #-} ``` ## See also [Haskell with only one type family](http://blog.poisson.chat/posts/2018-08-06-one-type-family.html) (blogpost) --- Contributions are welcome. Feel free to open an issue or make a PR on [Github](https://github.com/Lysxia/first-class-families)! first-class-families-0.6.0.0/CHANGELOG.md0000644000000000000000000000124713537433622015707 0ustar0000000000000000# 0.6.0.0 - Add `Fcf.Utils.Case` and `(Fcf.Combinators.>>=)` - Deprecate `Fcf.Bool.Guarded` - GHC 8.8 compatibility # 0.5.0.0 - Modularized library - `Fcf.Utils`: + Add `TError` + Rename `Collapse` to `Constraints` - `Fcf.Data.List`: Added `Cons`, `Last`, `Init`, `Elem` # 0.4.0.0 - New functions (blmage) + `LiftM`, `LiftM2`, `LiftM3` + `(<=)`, `(>=)`, `(<)`, `(>)` + `Guarded`, `Guard((:=))`, `Otherwise` # 0.3.0.1 - GHC 8.6 compatibility # 0.3.0.0 - More new functions, (isovector) # 0.2.0.0 - A whole bunch of basic functions (isovector) - Remove `Traverse` (now `Map`), `BimapPair`, `BimapEither` (now `Bimap`) # 0.1.0.0 Initial version