summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlyxia <>2018-12-30 15:57:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2018-12-30 15:57:00 (GMT)
commitd111b9aef3dce45ca39e4c151508040920891a64 (patch)
treeb128be76147ec9aad00264498065a6c76da4ed11
parent65c8184f7a419fa1a4190adc9d9e0087e03612b9 (diff)
version 0.4.0.00.4.0.0
-rw-r--r--CHANGELOG.md12
-rw-r--r--README.md41
-rw-r--r--first-class-families.cabal11
-rw-r--r--src/Fcf.hs47
-rw-r--r--test/test.hs28
5 files changed, 134 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0b2a60..95bdbc7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,22 @@
+# 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, thanks to isovector
+- More new functions, (isovector)
# 0.2.0.0
-- A whole bunch of basic functions, thanks to isovector
+- A whole bunch of basic functions (isovector)
- Remove `Traverse` (now `Map`), `BimapPair`, `BimapEither` (now `Bimap`)
# 0.1.0.0
diff --git a/README.md b/README.md
index 3b38dcf..d05a434 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,45 @@
# 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)
-[Haskell with only one type family](http://blog.poisson.chat/posts/2018-08-06-one-type-family.html) (blogpost)
+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.
-See `src/Fcf`.
+```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)
---
diff --git a/first-class-families.cabal b/first-class-families.cabal
index 36426fe..1d2f370 100644
--- a/first-class-families.cabal
+++ b/first-class-families.cabal
@@ -1,5 +1,5 @@
name: first-class-families
-version: 0.3.0.1
+version: 0.4.0.0
synopsis:
First class type families
description:
@@ -29,6 +29,15 @@ library
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
diff --git a/src/Fcf.hs b/src/Fcf.hs
index 3e62cb5..f09a312 100644
--- a/src/Fcf.hs
+++ b/src/Fcf.hs
@@ -76,6 +76,14 @@ 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)
@@ -294,6 +302,33 @@ 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
+
-- ** Nat
data (+) :: Nat -> Nat -> Exp Nat
@@ -308,6 +343,18 @@ type instance Eval ((Fcf.*) 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))
+
-- ** Other
data Error :: Symbol -> Exp a
diff --git a/test/test.hs b/test/test.hs
new file mode 100644
index 0000000..eb9690b
--- /dev/null
+++ b/test/test.hs
@@ -0,0 +1,28 @@
+{-# 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"
+ ])
+
+-- Compile-time tests
+
+_ = Refl :: UnitPrefix 0 :~: ""
+_ = Refl :: UnitPrefix 9 :~: "giga"
+
+-- Dummy
+
+main :: IO ()
+main = pure ()