type-spec (empty) → 0.1.0.0
raw patch · 14 files changed
+985/−0 lines, 14 filesdep +basedep +prettydep +show-typesetup-changed
Dependencies added: base, pretty, show-type, type-spec
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- examples/Main.hs +157/−0
- src/Test/TypeSpec.hs +66/−0
- src/Test/TypeSpec/Core.hs +106/−0
- src/Test/TypeSpec/Group.hs +42/−0
- src/Test/TypeSpec/Internal/Apply.hs +116/−0
- src/Test/TypeSpec/Internal/Either.hs +16/−0
- src/Test/TypeSpec/Internal/Equality.hs +8/−0
- src/Test/TypeSpec/Internal/Result.hs +50/−0
- src/Test/TypeSpec/Label.hs +29/−0
- src/Test/TypeSpec/ShouldBe.hs +123/−0
- src/Test/TypeSpecCrazy.hs +140/−0
- type-spec.cabal +100/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sven Heyll (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Sven Heyll nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/Main.hs view
@@ -0,0 +1,157 @@+-- | Some examples for 'Test.TypeSpec'+module Main where++import Data.Kind+import GHC.TypeLits+import Test.TypeSpecCrazy++main :: IO ()+main = do+ print specHelloWorld+ print specGrouped+ print specAliases+ print specTuple+ print specFamilyInstancesSuperCrazy+ print spec1+ print specCrazy+ print specInvalidCrazy++-- * TypeSpec Examples++-- | Let's start off simple:+specHelloWorld :: Expect (Int `Isn't` Bool)+specHelloWorld = Valid++-- | We can also expect a bit more using lists and tuples:+specGrouped+ :: Expect '[ Int `Isn't` Bool+ , Int `Is` Int+ , Bool `Is` Bool `ButNot` String+ ]+specGrouped = Valid++specTuple ::+ Explain "Type level tuples can also be used to group tests."+ (They "accept only two elments"+ '( (5 - 4) `Is` 1, (3 + 3) `Is` 6 `ButNot` (3 - 3) ) )+specTuple = Valid++-- * Some nice aliases++type ALot = 1000++specAliases ::+ (Explain "There are a variety aliases for the basic combinators."+ (Context "Basic Combinators"+ (Describe "Context"+ (It "labels expectations using 'It'"+ (Describe "Describe"+ (It's "an alias for It, just like They"+ (It's "time for the first assertion"+ (1000 `Is` ALot))))))))+specAliases = Valid+++-- * More complex example++type family Swap a+type instance Swap (a,b) = (b,a)++type family SwapT (a :: (k1, k2)) :: (k2, k1)+type instance SwapT '(a,b) = '(b,a)++type family Fst a where+ Fst (a,b) = a++type family Count a :: Nat where+ Count (a,b) = 2+ Count (a,b,c) = 3+ Count (a,b,c,d) = 4++-- A failing test case example:+-- specFailing ::+-- TypeSpec+-- (It "counts the number of elements in a tuple"+-- (Count ((),(),()) `ShouldBe` 4))+-- specFailing = Valid++specFamilyInstancesSuperCrazy ::++ "Only one title like this"+ ###########################++ "Describe the following expectations"++ --* Swap (Int, Bool) `Isn't` (Int, Int)+ -* Swap (Int, Bool) `Is` (Bool, Int)+ -* Swap (Int, Bool) `Isn't` Swap (Swap (Int, Bool))+ -* (Int, Bool) `Is` Swap (Swap (Int, Bool))+ -*+ ("... now there is maybe a nested block"++ --* Count (Int,Int,Int,Int) `ShouldBe` 4+ -* SwapT '(0,1) `ShouldBe` '(1,0)+ -* And (Count (Int,Int,Int) <=? 4))++ -/-++ "Here there is another top-level block"+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++ [ Count (Int,Int,Int,Int) `Is` 4+ , SwapT '(0,1) `Is` '(1,0)+ , And (Count (Int,Int,Int) <=? 4)+ ]+++specFamilyInstancesSuperCrazy = Valid++-- * More examples++spec1 ::+ Explain "TypeSpec"+ (It "Allows explanations of types" (ShouldBe Int Int))+spec1 = Valid++specCrazy ::++ "Higher kinded assertions"+ ###########################++ "ShouldBe accepts types of kind * -> *"+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++ ShouldBe Maybe Maybe+ -* ShouldBe [] []+ -* ShouldBe (->) (->)++specCrazy = Valid++-- --------------------------------------------------------++type SpecInvalidCrazy =++ "One of the following specs is not OK"+ #######################################++ "This should be ok"+ ~~~~~~~~~~~~~~~~~~~~~++ TheseAreEqual Bool Bool++ -/-++ "This should also be ok"+ ~~~~~~~~~~~~~~~~~~~~~~~~++ TheseAreNotEqual (Maybe Int) (Maybe Int)++ -/-++ "But this looks bad:"+ ~~~~~~~~~~~~~~~~~~~~~++ TheseAreEqual Maybe (Either Int)++specInvalidCrazy :: SpecInvalidCrazy+specInvalidCrazy = Invalid
+ src/Test/TypeSpec.hs view
@@ -0,0 +1,66 @@+-- | A tiny EDSL to write type-level-unit tests.+module Test.TypeSpec+ ( type Expect+ , type Explain+ , type It's+ , type IsTheSameAs+ , type Is+ , type TheseAreEqual+ , type IsNot+ , type Isn't+ , type IsNotTheSameAs+ , type IsDifferentFrom+ , type TheseAreNotEqual+ , type IsTrue+ , type Therefore+ , type And+ , type IsFalse+ , type They+ , type Describe+ , type Context+ , module ReExport)+ where++import Test.TypeSpec.Core as ReExport+import Test.TypeSpec.Group as ReExport+import Test.TypeSpec.Label as ReExport+import Test.TypeSpec.ShouldBe as ReExport++-- * 'TypeSpec' Aliases++-- | An alias for 'TypeSpec'.+type Expect = TypeSpec++-- | Another alias for 'TypeSpec' (and also 'It')+type Explain does this = TypeSpec (It does this)++-- * 'ShouldBe' aliases++type Is = ShouldBe+type IsTheSameAs = ShouldBe+type TheseAreEqual = ShouldBe++-- * 'ShouldNotBe' aliases++type IsNot = ShouldNotBe+type Isn't = ShouldNotBe+type IsNotTheSameAs = ShouldNotBe+type IsDifferentFrom = ShouldNotBe+type TheseAreNotEqual = ShouldNotBe++-- * 'ShouldBeTrue' aliases++type IsTrue = ShouldBeTrue+type And = ShouldBeTrue+type Therefore = ShouldBeTrue++-- * 'ShouldBeFalse' aliases++type IsFalse = ShouldBeFalse++-- * Labelling Aliases++type They message expectations = It message expectations+type Describe = It+type Context = It+type It's = It
+ src/Test/TypeSpec/Core.hs view
@@ -0,0 +1,106 @@+-- | Core of the TypeSpec abstractions. Import to add custom instances.+module Test.TypeSpec.Core+ ( TypeSpec (..)+ , type EvalExpectation+ , PrettyTypeSpec(..)+ , prettyIndentation+ , nest'+ , sentence+ , module ReExport+ )+ where++import Data.Proxy+import Test.TypeSpec.Internal.Either ()+import Test.TypeSpec.Internal.Apply+import Test.TypeSpec.Internal.Result as ReExport+import Text.PrettyPrint++-- * Type Level Specifcations++-- | A type specification.+data TypeSpec expectation where+ -- | Expect the given expectations to hold. If the compiler does not reject it -+ -- the expectation seem plausible.+ Valid :: (Try (EvalExpectation expectation) ~ expectation)+ => TypeSpec expectation+ -- | Expect the given expectations to **NOT** hold. If the compiler does not+ -- reject it - the expectation seem indeed implausible.+ Invalid :: (DontTry (EvalExpectation expectation))+ => TypeSpec expectation++-- * Expectations++-- | An open family of type level expectation evaluators, that return either @()@+-- or an @ErrorMessage@.+type family EvalExpectation (expectation :: k) :: Result k++-- ** Combining/Grouping collections++-- | Given a pair @(expectation1, expectation2)@ try to evaluate the first then,+-- if no error was returned, the second.+type instance EvalExpectation '(a, b) =+ Pair'' <$> EvalExpectation a <*> EvalExpectation b++-- | Given a list @(expectation : rest)@ try to evaluate the @expectation@ then,+-- if no error was returned, the @rest@.+type instance EvalExpectation '[] = OK '[]+type instance EvalExpectation (expectation ': rest) =+ Cons'' <$> EvalExpectation expectation <*> EvalExpectation rest++-- * Pretty Printing++-- | A class for pretty printing via the 'Show' instance of 'TypeSpec'.+class PrettyTypeSpec (t :: k) where+ prettyTypeSpec :: proxy t -> Doc++-- * Printing/Showing++instance PrettyTypeSpec t => Show (TypeSpec t) where+ show px@Valid =+ render+ $ hang (text "Valid:") 5 (prettyTypeSpec px)+ show px@Invalid =+ render+ $ hang (text "Invalid:") 5 (prettyTypeSpec px)++-- | The default indention to use when 'nest'ing 'Doc'uments.+prettyIndentation :: Int+prettyIndentation = 2++-- | Nest using the default indention `prettyIndentation`.+nest' :: Doc -> Doc+nest' = nest prettyIndentation++-- | Print a /sentence/ with the second part 'hang'ing from the first.+-- Generate: @predicate: object@+sentence :: String -> Doc -> Doc+sentence predicate object =+ hang (text predicate <> colon <> space) 5 object++-- * Default instances++instance+ ( PrettyTypeSpec expectation1+ , PrettyTypeSpec expectation2 )+ => PrettyTypeSpec '(expectation1, expectation2)+ where+ prettyTypeSpec _ =+ prettyTypeSpec pe1 $+$ prettyTypeSpec pe2 + where pe1 = Proxy :: Proxy expectation1+ pe2 = Proxy :: Proxy expectation2++instance+ PrettyTypeSpec '[]+ where+ prettyTypeSpec _ = empty++instance+ ( PrettyTypeSpec expectation+ , PrettyTypeSpec rest )+ => PrettyTypeSpec (expectation ': rest)+ where+ prettyTypeSpec _ =+ (prettyTypeSpec pe1) $+$ (prettyTypeSpec pe2)+ where pe1 = Proxy :: Proxy expectation+ pe2 = Proxy :: Proxy rest
+ src/Test/TypeSpec/Group.hs view
@@ -0,0 +1,42 @@+-- | Group expectations+module Test.TypeSpec.Group+ ( type (-/-), type (-*) )+ where++import Data.Proxy+import Test.TypeSpec.Core+import Test.TypeSpec.Internal.Apply+import Test.TypeSpec.Internal.Either ()+import Text.PrettyPrint++-- * Composed Expectations++-- | A @cons@ like operator.+-- Make a list of expectations. Use this to chain together any expectations+-- especially those using '(~~~)' or '(-*)', since it has a lower precedence+-- than both.+data expectation1 -/- expectation2+infixr 1 -/-++type instance+ EvalExpectation (expectation -/- expectations) =+ TyCon2 ((-/-)) <$> EvalExpectation expectation <*> EvalExpectation expectations++-- | A @cons@ like operator.+-- Make a list of expectations. Use this to chain together anything /below/ or+-- inside '(-/-)' and '(~~~)' parts. The precedence of this operator is higher+-- that that of '(-/-)'.+type expectation1 -* expectation2 = expectation1 -/- expectation2+infixr 3 -*++-- * Pretty Printing Instances++instance+ ( PrettyTypeSpec expectation1+ , PrettyTypeSpec expectation2 )+ => PrettyTypeSpec (expectation1 -/- expectation2)+ where+ prettyTypeSpec _ =+ (prettyTypeSpec pe1) $+$ (prettyTypeSpec pe2)+ where pe1 = Proxy :: Proxy expectation1+ pe2 = Proxy :: Proxy expectation2
+ src/Test/TypeSpec/Internal/Apply.hs view
@@ -0,0 +1,116 @@+-- | Useful abstractions for type level programming using. This reimplements+-- parts of the singletons library, which is just too heavy of a dependency to+-- carry around, when only three small types are used of it.+module Test.TypeSpec.Internal.Apply+ ( type (>>=)+ , type (>>)+ , type (<*>)+ , type (<$>$$)+ , type (<$>$)+ , type (<$>)+ , type TyCon1+ , type TyCon2+ , type Apply+ , TyFun, type TyFunData+ , type (~>)+ , type Cons''+ , type Cons'+ , type Pair''+ , type Pair'+ , type Const+ , type Const'+ , type Const''+ , Flip'+ , Flip+ , Flip_+ , type Flip__+ , Compose''+ , Compose'+ , Compose+ , type Compose_+ )+ where++import Data.Kind++-- | Bind to actions.+type family+ (>>=) (ma :: monad a)+ (f :: TyFunData (a :: Type) ((monad b) :: Type))+ :: monad b++-- | Execute one action and then the next, ignore the result of the first.+type (>>) ma mb = ma >>= Const' mb++-- | Execute an action that returns a function than map function over the result+-- of the next action.+type family+ (f :: m (a ~> b)) <*> (ma :: m a) :: m b where+ mf <*> mx = mf >>= Apply (Flip (<$>$$)) mx++-- | Tuple construction+data Pair'' :: a ~> b ~> (a, b)+data Pair' :: a -> b ~> (a, b)+type instance Apply Pair'' x = Pair' x+type instance Apply (Pair' x) y = '(x, y)++-- | List construction+data Cons'' :: a ~> [a] ~> [a]+data Cons' :: a -> [a] ~> [a]+type instance Apply Cons'' x = Cons' x+type instance Apply (Cons' x) xs = x ': xs++-- | Convert data types to Partially applicable type functions+data TyCon1 :: (a -> b) -> a ~> b+data TyCon2 :: (a -> b -> c) -> a ~> b ~> c+type instance Apply (TyCon1 f) x = f x+type instance Apply (TyCon2 f) x = (TyCon1 (f x))++-- | Execute an action and map a pure function over the result.+data (<$>$$) :: (a ~> b) ~> m a ~> m b+data (<$>$) :: (a ~> b) -> m a ~> m b+type instance Apply (<$>$$) f = (<$>$) f+type instance Apply ((<$>$) f) x = f <$> x+type family+ (f :: (a ~> b)) <$> (ma :: m a) :: m b++-- * Flip Type Functions++data Flip' :: (a ~> b ~> c) ~> b ~> a ~> c+data Flip :: (a ~> b ~> c) -> b ~> a ~> c+data Flip_ :: (a ~> b ~> c) -> b -> a ~> c+type instance Apply Flip' f = Flip f+type instance Apply (Flip f) y = Flip_ f y+type instance Apply (Flip_ f y) x = Flip__ f y x+type family+ Flip__ (f :: (a ~> b ~> c)) (y :: b) (x :: a) :: c where+ Flip__ f y x = Apply (Apply f x) y++-- * Type Function composition++data Compose'' :: (b ~> c) ~> (a ~> b) ~> (a ~> c)+data Compose' :: (b ~> c) -> (a ~> b) ~> (a ~> c)+data Compose :: (b ~> c) -> (a ~> b) -> (a ~> c)+type instance Apply Compose'' f = Compose' f+type instance Apply (Compose' f) g = (Compose f g)+type instance Apply (Compose f g) x = Compose_ f g x+type family+ Compose_ (f :: b ~> c) (g :: a ~> b) (x :: a) :: c where+ Compose_ f g x = Apply f (Apply g x)+++-- * Type-Level 'const'++type family Const (a :: t) (b :: t') :: t where Const a b = a+data Const' :: a -> (TyFunData b a)+data Const'' :: TyFunData a (TyFunData b a)+type instance Apply Const'' a = Const' a+type instance Apply (Const' a) b = Const a b++-- * Defunctionalization++data TyFun :: Type -> Type -> Type+type TyFunData a b = TyFun a b -> Type+type a ~> b = TyFun a b -> Type+infixr 0 ~>+type family Apply (f :: a ~> b) (x :: a) :: b
+ src/Test/TypeSpec/Internal/Either.hs view
@@ -0,0 +1,16 @@+-- | Useful abstractions for type level programming using 'Either'.+module Test.TypeSpec.Internal.Either (type FromLeft) where++import Test.TypeSpec.Internal.Apply ++-- * Either instances++type instance (>>=) ('Right a) f = Apply f a+type instance (>>=) ('Left b) f = 'Left b++type instance (<$>) f ('Right a) = 'Right (Apply f a)+type instance (<$>) f ('Left a) = 'Left a++type family+ FromLeft (e :: Either a b) :: a where+ FromLeft ('Left a) = a
+ src/Test/TypeSpec/Internal/Equality.hs view
@@ -0,0 +1,8 @@+-- | Type Equality+module Test.TypeSpec.Internal.Equality (type PolyKindEq) where++ -- | Operator 'Data.Equality.(==)' expects both arguments to have the+ -- same kind.+ type family PolyKindEq (a :: ak) (b :: bk) :: Bool where+ PolyKindEq a a = 'True+ PolyKindEq a b = 'False
+ src/Test/TypeSpec/Internal/Result.hs view
@@ -0,0 +1,50 @@+-- | Result type used in constraints inside 'TypeSpec' to propagate type errors.+module Test.TypeSpec.Internal.Result+ ( type Result+ , type Try+ , type DontTry+ , type FAILED+ , type OK+ , type PrependToError+ ) where++import GHC.TypeLits+import Data.Kind+import Test.TypeSpec.Internal.Apply ()+import Test.TypeSpec.Internal.Either ()++-- * Type error propagation++-- | When a type level expectation is tested, it might be that compound+-- expectations fail. In order to have a small, precise error message, the type+-- level assertion results are made to have kind 'Result'.+type Result = Either ErrorMessage++type family+ Try (e :: Result k) :: k where+ Try (OK (d :: k)) = d+ Try (FAILED m) = TypeError m++type family+ DontTry (e :: Result r) :: Constraint where+ DontTry (FAILED e) = ()+ DontTry (OK okIsNotOk) =+ TypeError ('Text "You specified this wasn't okay: "+ ':$$:+ 'Text " " ':<>: 'ShowType okIsNotOk+ ':$$:+ 'Text "... turns out it actually is!")++-- | A nice name than 'Left'+type OK = 'Right+type FAILED = 'Left++type family+ PrependToError+ (message :: ErrorMessage)+ (result :: Result a)+ :: Result a+ where+ PrependToError message (OK x) = OK x+ PrependToError message (FAILED otherMessage) =+ FAILED (message ':<>: otherMessage)
+ src/Test/TypeSpec/Label.hs view
@@ -0,0 +1,29 @@+-- | Label expectations+module Test.TypeSpec.Label+ ( It )+ where++import Data.Kind+import Data.Typeable+import GHC.TypeLits+import Test.TypeSpec.Core+import Test.TypeSpec.Internal.Apply+import Test.TypeSpec.Internal.Either ()+import Text.PrettyPrint++-- | Add a type level string as label or longer descripton around expectations.+-- This is analog to the @it@ function in the @hspec@ package.+data It :: Symbol -> expectation -> Type++type instance+ EvalExpectation (It message expectation) =+ PrependToError+ ('Text message ':$$: 'Text " ")+ (EvalExpectation expectation)+ >> (OK (It message expectation))+++instance (KnownSymbol msg, PrettyTypeSpec x) => PrettyTypeSpec (It msg x) where+ prettyTypeSpec _ =+ (text (symbolVal (Proxy :: Proxy msg)))+ $+$ nest prettyIndentation (prettyTypeSpec (Proxy :: Proxy x))
+ src/Test/TypeSpec/ShouldBe.hs view
@@ -0,0 +1,123 @@+-- | Type level assertions on type equality.+module Test.TypeSpec.ShouldBe+ ( ShouldBe+ , ShouldNotBe+ , ShouldBeTrue+ , ShouldBeFalse+ , ButNot+ )+ where++import Data.Kind+import Data.Type.Bool+import Data.Type.Equality+import Data.Typeable+import GHC.TypeLits+import Test.TypeSpec.Core+import Test.TypeSpec.Internal.Apply ()+import Test.TypeSpec.Internal.Either ()+import Test.TypeSpec.Internal.Equality+import Text.PrettyPrint+import Type.Showtype++-- | State that a type is equal to the type level @True@.+data ShouldBeTrue :: expectation -> Type++type instance EvalExpectation (ShouldBeTrue t) =+ If (PolyKindEq t 'True)+ (OK (ShouldBeTrue t))+ (FAILED+ ('Text "Should have been 'True: " ':<>: 'ShowType t))++-- | State that a type is equal to the type level @False@.+data ShouldBeFalse :: expectation -> Type++type instance EvalExpectation (ShouldBeFalse t) =+ If (PolyKindEq t 'False)+ (OK (ShouldBeFalse t))+ (FAILED+ ('Text "Should have been 'False: " ':<>: 'ShowType t))++-- | State that one type is different to two other types. This must always be+-- used right next to a 'ShouldBe' pair, otherwise this will not work.+data ButNot :: shouldBe -> actual -> Type++type instance+ EvalExpectation (ButNot (ShouldBe expected actual) other) =+ If (expected == actual)+ (If (expected == other)+ (FAILED+ ('Text "Expected type: "+ ':$$: 'Text " " ':<>: 'ShowType expected+ ':$$: 'Text "to be different from: "+ ':$$: 'Text " " ':<>: 'ShowType other))+ (OK (ButNot (ShouldBe expected actual) other)))+ (FAILED+ ('Text "Expected type: " ':<>: 'ShowType expected+ ':$$: 'Text "Actual type: " ':<>: 'ShowType actual))++-- | State that two types or type constructs are boiled down to the same type.+data ShouldBe :: expected -> actual -> Type++type instance+ EvalExpectation (ShouldBe expected actual) =+ If (PolyKindEq expected actual)+ (OK (ShouldBe expected actual))+ (FAILED+ ('Text "Expected type: " ':<>: 'ShowType expected+ ':$$: 'Text "Actual type: " ':<>: 'ShowType actual))++-- | State that two types or type constructs are NOT the same type.+data ShouldNotBe :: expected -> actual -> Type++type instance+ EvalExpectation (ShouldNotBe expected actual) =+ If (PolyKindEq expected actual)+ (FAILED+ ('Text "Expected type: "+ ':$$: 'Text " " ':<>: 'ShowType expected+ ':$$: 'Text "to be different from: "+ ':$$: 'Text " " ':<>: 'ShowType actual))+ (OK (ShouldNotBe expected actual))++-- * PrettyTypeSpec instances++instance PrettyTypeSpec (ShouldBeTrue a) where+ prettyTypeSpec _px =+ prettyBulletPoint $ text "Type == True"++instance PrettyTypeSpec (ShouldBeFalse a) where+ prettyTypeSpec _px =+ prettyBulletPoint $ text "Type == False"++instance PrettyTypeSpec (ShouldBe a b) where+ prettyTypeSpec _px =+ prettyBulletPoint $ text "Types are equal"++instance (Showtype a, Showtype b) => PrettyTypeSpec (ShouldNotBe a b) where+ prettyTypeSpec _px =+ prettyBulletPoint+ $ sentence "Type" (text expected)+ $$ sentence "differs from" (text actual)+ where+ expected = showtype (Proxy :: Proxy a)+ actual = showtype (Proxy :: Proxy b)++instance+ (a ~ (ShouldBe a0 a1)+ , Showtype a0+ , Showtype a1+ , Showtype b )+ => PrettyTypeSpec (ButNot a b) where+ prettyTypeSpec _ =+ prettyBulletPoint+ $ (sentence "Type"+ (text (showtype (Proxy :: Proxy a0))))+ $$ (sentence "is equal to"+ (text (showtype (Proxy :: Proxy a1))))+ $$ (sentence "but not to"+ (text (showtype (Proxy :: Proxy b))))++-- | Pretty print a test prefix by a bullet-point.+prettyBulletPoint :: Doc -> Doc+prettyBulletPoint doc = text "•" <+> doc
+ src/Test/TypeSpecCrazy.hs view
@@ -0,0 +1,140 @@+-- | Funny operators that are mere type aliases for the constructs in+-- 'TypeSpec'+module Test.TypeSpecCrazy+ ( module Test.TypeSpecCrazy+ , module ReExport)+ where++import Test.TypeSpec as ReExport++-- * Crazy Type operators++-- | Create a 'TypeSpec' with an initial description or title followed by some+-- expectations. Note that the number of @#@s is alway a multiple of 3.+type (###) title expr = Explain title expr+type (######) title expr = Explain title expr+type (#########) title expr = Explain title expr+type (############) title expr = Explain title expr+type (###############) title expr = Explain title expr+type (##################) title expr = Explain title expr+type (#####################) title expr = Explain title expr+type (########################) title expr = Explain title expr+type (###########################) title expr = Explain title expr+type (##############################) title expr = Explain title expr+type (#################################) title expr = Explain title expr+type (####################################) title expr = Explain title expr+type (#######################################) title expr = Explain title expr+type (##########################################) title expr =+ Explain title expr+type (#############################################) title expr =+ Explain title expr+type (################################################) title expr =+ Explain title expr+type (###################################################) title expr =+ Explain title expr+type (######################################################) title expr =+ Explain title expr+type (#########################################################) title expr =+ Explain title expr+type (############################################################) title expr =+ Explain title expr+type (###############################################################) title expr =+ Explain title expr+type (##################################################################) title expr =+ Explain title expr+type (#####################################################################) title expr =+ Explain title expr+type (########################################################################) title expr =+ Explain title expr++infixr 0 ###+infixr 0 ######+infixr 0 #########+infixr 0 ############+infixr 0 ###############+infixr 0 ##################+infixr 0 #####################+infixr 0 ########################+infixr 0 ###########################+infixr 0 ##############################+infixr 0 #################################+infixr 0 ####################################+infixr 0 #######################################+infixr 0 ##########################################+infixr 0 #############################################+infixr 0 ################################################+infixr 0 ###################################################+infixr 0 ######################################################+infixr 0 #########################################################+infixr 0 ############################################################+infixr 0 ###############################################################+infixr 0 ##################################################################+infixr 0 #####################################################################+infixr 0 ########################################################################++-- | Specify an expectation using an underlined title using 'It'.+type (--*) title expr = It title expr+type (~~~) title expr = It title expr+type (~~~~~~) title expr = It title expr+type (~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr = It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr+type (~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~) title expr =+ It title expr++infixl 2 --*+infixl 2 ~~~+infixr 2 ~~~~~~+infixr 2 ~~~~~~~~~+infixr 2 ~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+infixr 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++type expectation1 -*- expectation2 = expectation1 -/- expectation2+infixl 2 -*-
+ type-spec.cabal view
@@ -0,0 +1,100 @@+name: type-spec+version: 0.1.0.0+synopsis: Type Level Specification by Example+description: Please see README.md+homepage: https://github.com/sheyll/type-spec#readme+license: BSD3+license-file: LICENSE+author: Sven Heyll+maintainer: sven.heyll@gmail.com+copyright: 2016 Sven Heyll+category: expring+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Test.TypeSpec+ , Test.TypeSpecCrazy+ , Test.TypeSpec.Core+ , Test.TypeSpec.Group+ , Test.TypeSpec.Label+ , Test.TypeSpec.ShouldBe+ , Test.TypeSpec.Internal.Apply+ , Test.TypeSpec.Internal.Either+ , Test.TypeSpec.Internal.Equality+ , Test.TypeSpec.Internal.Result+ build-depends: base >= 4.9 && < 5+ , pretty >= 1.1.3 && < 1.2+ , show-type >= 0.1.1+ default-language: Haskell2010+ default-extensions: ConstraintKinds+ , CPP+ , DataKinds+ , DefaultSignatures+ , DeriveDataTypeable+ , DeriveFunctor+ , DeriveGeneric+ , FlexibleInstances+ , FlexibleContexts+ , FunctionalDependencies+ , GADTs+ , GeneralizedNewtypeDeriving+ , KindSignatures+ , MultiParamTypeClasses+ , OverloadedStrings+ , PolyKinds+ , QuasiQuotes+ , RecordWildCards+ , RankNTypes+ , ScopedTypeVariables+ , StandaloneDeriving+ , TemplateHaskell+ , TupleSections+ , TypeFamilies+ , TypeInType+ , TypeOperators+ , TypeSynonymInstances+ , UndecidableInstances+ ghc-options: -Wall++test-suite examples+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ hs-source-dirs: examples+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.9 && < 5+ , type-spec+ default-language: Haskell2010+ default-extensions: ConstraintKinds+ , CPP+ , DataKinds+ , DefaultSignatures+ , DeriveDataTypeable+ , DeriveFunctor+ , DeriveGeneric+ , FlexibleInstances+ , FlexibleContexts+ , FunctionalDependencies+ , GADTs+ , GeneralizedNewtypeDeriving+ , KindSignatures+ , MultiParamTypeClasses+ , OverloadedStrings+ , QuasiQuotes+ , RecordWildCards+ , RankNTypes+ , ScopedTypeVariables+ , StandaloneDeriving+ , TemplateHaskell+ , TupleSections+ , TypeFamilies+ , TypeInType+ , TypeOperators+ , TypeSynonymInstances++source-repository head+ type: git+ location: https://github.com/sheyll/type-spec