git.iko.soy/typeable/servant
on master help sign in
Remove QQ for now
commit5470297bdc8f8c81dbca399f492b4e198f34af2e authorJulian K. Arni <jkarni@gmail.com> dateFri 20 Feb 2015, 15:08 (12 years ago) parent99374c5
4 files changed−561
servant.cabal−1 src/Servant/API.hs−3 src/Servant/QQ.hs−344 test/Servant/QQSpec.hs−213
servant.cabal−1
@@ -41,7 +41,6 @@ library
4141 Servant.API.ReqBody
4242 Servant.API.Sub
4343 Servant.Common.Text
44− Servant.QQ
4544 Servant.Utils.Links
4645 build-depends:
4746 base >=4.7 && <5
src/Servant/API.hs−3
@@ -38,8 +38,6 @@ module Servant.API (
3838 module Servant.API.Raw,
3939
4040 -- * Utilities
41− -- | QuasiQuotes for endpoints
42− module Servant.QQ,
4341 -- | Type-safe internal URIs
4442 module Servant.Utils.Links,
4543 ) where
@@ -59,5 +57,4 @@ import Servant.API.MatrixParam ( MatrixFlag, MatrixParams, MatrixParam )
5957import Servant.API.Raw ( Raw )
6058import Servant.API.ReqBody ( ReqBody )
6159import Servant.API.Sub ( (:>)(..) )
62−import Servant.QQ ( sitemap )
6360import Servant.Utils.Links ( safeLink, URI(..), IsElem, IsElem', HasLink(..) )
src/Servant/QQ.hsdeleted−344
@@ -1,344 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RecordWildCards #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TemplateHaskell #-}
9−{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
10−-- | QuasiQuoting utilities for API types.
11−--
12−-- 'sitemap' allows you to write your type in a very natural way:
13−--
14−-- @
15−-- [sitemap|
16−-- PUT hello String -> ()
17−-- POST hello/p:Int String -> ()
18−-- GET hello/?name:String Int
19−-- |]
20−-- @
21−--
22−-- Will generate:
23−--
24−-- @
25−-- "hello" :> ReqBody String :> Put ()
26−-- :\<|> "hello" :> Capture "p" Int :> ReqBody String :> Post ()
27−-- :\<|> "hello" :> QueryParam "name" String :> Get Int
28−-- @
29−--
30−-- Note the @/@ before a @QueryParam@!
31−module Servant.QQ (sitemap) where
32−
33−import Control.Applicative ( (<$>) )
34−import Control.Monad ( void )
35−import Data.Monoid ( Monoid(..), (<>) )
36−import Data.Maybe ( mapMaybe )
37−import Language.Haskell.TH.Quote ( QuasiQuoter(..) )
38−import Language.Haskell.TH
39− ( mkName, Type(AppT, ConT, LitT), TyLit(StrTyLit) )
40−import Text.ParserCombinators.Parsec
41− ( try,
42− Parser,
43− manyTill,
44− endBy,
45− sepBy1,
46− optional,
47− optionMaybe,
48− string,
49− anyChar,
50− char,
51− spaces,
52− noneOf,
53− parse,
54− skipMany,
55− many,
56− lookAhead,
57− (<|>),
58− (<?>) )
59−import Servant.API.Capture ( Capture )
60−import Servant.API.Get ( Get )
61−import Servant.API.Post ( Post )
62−import Servant.API.Put ( Put )
63−import Servant.API.Delete ( Delete )
64−import Servant.API.QueryParam ( QueryParam )
65−import Servant.API.MatrixParam ( MatrixParam )
66−import Servant.API.ReqBody ( ReqBody )
67−import Servant.API.Sub ( (:>) )
68−import Servant.API.Alternative ( (:<|>) )
69−
70−data MethodE
71−data PathElemE
72−data OptsE
73−data SitemapE
74−
75−sitemap = undefined
76−{-
77−data Validation e a = Failure e
78− | Success a
79−
80−instance Functor (Validation e) where
81− fmap _ (Failure e) = Failure e
82− fmap f (Success a) = Success (f a)
83−
84−data Exp a where
85− Method :: String -> Exp MethodE
86− Slash :: Exp PathElemE -> Exp PathElemE -> Exp PathElemE
87− PathElem :: String -> Exp PathElemE
88− Opts :: String -> Exp OptsE
89− JoinOpts :: Exp OptsE -> Exp OptsE -> Exp OptsE
90− AddOpts :: Exp SitemapE -> Exp OptsE -> Exp SitemapE
91− Line :: Exp MethodE -> Exp PathElemE -> Exp SitemapE
92− Sitemap :: Exp SitemapE -> Exp SitemapE -> Exp SitemapE
93−
94−data ParseError = ParseError Int String
95−
96−parseEverything :: String -> Exp SitemapE
97−parseEverything str = undefined {-removeComments <$> lines str
98− where removeComments = takeWhile (/= '#')
99− parseLines _ [] = []
100− parseLines lineno (x:xs) = case opts of
101− [] -> (parseUrlLine x):parseLines rest
102− xs -> undefined
103− where (opts, rest) = span (startsWith ' ') xs -}
104−
105−
106−
107−parsePath :: String -> Exp PathElemE
108−parsePath line = foldr1 Slash $ PathElem <$> wordsBy '/' line
109−
110−parseOpts :: Int -> [String] -> Either ParseError (Exp OptsE)
111−parseOpts lineno lines = foldr1 JoinOpts $ Opts <$> lines
112−
113−parseUrlLine :: Int -> String -> Either ParseError (Exp SitemapE)
114−parseUrlLine lineno line = case span '>' line of
115− ([], x) -> Left $ ParseError lineno "Expected method and url before '>'"
116− (xs, ">") -> Left $ ParseError lineno "Expected type after '>'"
117− (xs, '>':ys) | length (words xs) /= 2 -> Left $ ParseError lineno "Expected method and url before '>'"
118− | otherwise -> Line (Method met) (parsePath url)
119− where met:url = words xs
120−
121−wordsBy :: Char -> String -> [String]
122−wordsBy c s = case dropWhile (== c) s of
123− "" -> []
124− s' -> w : words s''
125− where (w, s'') = break (== c) s'
126−
127−
128−data SitemapParser = SitemapParser
129− { methodParsers :: [String -> Maybe Type]
130− , pathParsers :: [String -> Maybe Type]
131− , optsParsers :: [String -> Maybe Type]
132− }
133−
134−
135−pFirstWE :: [a -> Maybe b] -> a -> c -> Validation c b
136−pFirstWE fn a c = maybe (Failure c) Success $ pFirst fn a
137− where pFirst :: [a -> Maybe b] -> a -> Maybe b
138− pFirst fns s = case mapMaybe ($ s) fns of
139− [] -> Nothing
140− x:_ -> Just x
141−
142−
143−joinWith :: Monoid err => (typ -> typ -> typ)
144− -> Validation err typ
145− -> Validation err typ
146− -> Validation err typ
147−joinWith mult (Success s1) (Success s2) = Success (s1 `mult` s2)
148−joinWith _ (Failure e1) (Failure e2) = Failure (e1 <> e2)
149−joinWith _ (Failure e1) _ = Failure e1
150−joinWith _ _ (Failure e2) = Failure e2
151−
152−pathUnion :: Type -> Type -> Type
153−pathUnion a = AppT (AppT (ConT ''(:>)) a)
154−
155−optsUnion :: Type -> Type -> Type
156−optsUnion a = AppT (AppT (ConT ''(:<|>)) a)
157−
158−data UndefinedError = UndefinedPath String
159− | UndefinedMethod String
160− | UndefinedOpts String
161− deriving (Eq, Show)
162−
163−evalPath :: SitemapParser -> Exp PathElemE -> Validation [UndefinedError] Type
164−evalPath SitemapParser{..} (PathElem path) = pFirstWE pathParsers path [UndefinedPath path]
165−evalPath sp (p1 `Slash` p2) = joinWith pathUnion (evalPath sp p1) (evalPath sp p2)
166−
167−evalMethod :: SitemapParser -> Exp MethodE -> Validation [UndefinedError] Type
168−evalMethod SitemapParser{..} (Method met) = pFirstWE methodParsers met [UndefinedMethod met]
169−
170−evalOpts :: SitemapParser -> Exp OptsE -> Validation [UndefinedError] Type
171−evalOpts SitemapParser{..} (Opts opt) = pFirstWE optsParsers opt [UndefinedOpts opt]
172−evalOpts sp (o1 `JoinOpts` o2) = joinWith optsUnion (evalOpts sp o1) (evalOpts sp o2)
173−
174−
175−addMethodParser, addPathParser, addOptsParser :: (String -> Maybe Type) -> SitemapParser -> SitemapParser
176−addMethodParser a x@SitemapParser{..} = x{ methodParsers = methodParsers ++ [a] }
177−addOptsParser a x@SitemapParser{..} = x{ optsParsers = optsParsers ++ [a] }
178−addPathParser a x@SitemapParser{..} = x{ pathParsers = pathParsers ++ [a] }
179−
180−-- | Finally-tagless encoding for our DSL.
181−-- Keeping 'repr'' and 'repr' distinct when writing functions with an
182−-- @ExpSYM@ context ensures certain invariants (for instance, that there is
183−-- only one of 'get', 'post', 'put', and 'delete' in a value), but
184−-- sometimes requires a little more work.
185−class ExpSYM repr' repr | repr -> repr', repr' -> repr where
186− lit :: String -> repr' -> repr
187− capture :: String -> String -> repr -> repr
188− reqBody :: String -> repr -> repr
189− queryParam :: String -> String -> repr -> repr
190− matrixParam :: String -> String -> repr -> repr
191− conj :: repr' -> repr -> repr
192− get :: String -> repr
193− post :: String -> repr
194− put :: String -> repr
195− delete :: String -> repr
196−
197−
198−infixr 6 >:
199−
200−(>:) :: Type -> Type -> Type
201−(>:) = conj
202−
203−
204−instance ExpSYM Type Type where
205− lit name r = LitT (StrTyLit name) >: r
206− capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
207− (ConT $ mkName typ) >: r
208− reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
209− queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
210− (ConT $ mkName typ) >: r
211− matrixParam name typ r = AppT (AppT (ConT ''MatrixParam) (LitT (StrTyLit name)))
212− (ConT $ mkName typ) >: r
213− conj x = AppT (AppT (ConT ''(:>)) x)
214− get typ = AppT (ConT ''Get) (ConT $ mkName typ)
215− post typ = AppT (ConT ''Post) (ConT $ mkName typ)
216− put typ = AppT (ConT ''Put) (ConT $ mkName typ)
217− delete "()" = ConT ''Delete
218− delete _ = error "Delete does not return a request body"
219−
220−parseMethod :: ExpSYM repr' repr => Parser (String -> repr)
221−parseMethod = try (string "GET" >> return get)
222− <|> try (string "POST" >> return post)
223− <|> try (string "PUT" >> return put)
224− <|> try (string "DELETE" >> return delete)
225−
226−parseUrlSegment :: ExpSYM repr repr => Parser (repr -> repr)
227−parseUrlSegment = try parseCapture
228− <|> try parseQueryParam
229− <|> try parseLit
230− where
231− parseCapture = do
232− cname <- many (noneOf " ?/:;")
233− char ':'
234− ctyp <- many (noneOf " ?/:;")
235− mx <- many parseMatrixParam
236− return $ capture cname ctyp . foldr (.) id mx
237− parseQueryParam = do
238− char '?'
239− cname <- many (noneOf " ?/:;")
240− char ':'
241− ctyp <- many (noneOf " ?/:;")
242− return $ queryParam cname ctyp
243− parseLit = do
244− lt <- many (noneOf " ?/:;")
245− mx <- many parseMatrixParam
246− return $ lit lt . foldr (.) id mx
247− parseMatrixParam = do
248− char ';'
249− cname <- many (noneOf " ?/:;")
250− char ':'
251− ctyp <- many (noneOf " ?/:;")
252− return $ matrixParam cname ctyp
253−
254−parseUrl :: ExpSYM repr repr => Parser (repr -> repr)
255−parseUrl = do
256− optional $ char '/'
257− url <- parseUrlSegment `sepBy1` char '/'
258− return $ foldr1 (.) url
259−
260−data Typ = Val String
261− | ReqArgVal String String
262−
263−parseTyp :: Parser Typ
264−parseTyp = do
265− f <- many (noneOf "-{\n\r")
266− spaces
267− s <- optionMaybe (try parseRet)
268− try $ optional inlineComment
269− try $ optional blockComment
270− case s of
271− Nothing -> return $ Val (stripTr f)
272− Just s' -> return $ ReqArgVal (stripTr f) (stripTr s')
273− where
274− parseRet :: Parser String
275− parseRet = do
276− string "->"
277− spaces
278− many (noneOf "-{\n\r")
279− stripTr = reverse . dropWhile (== ' ') . reverse
280−
281−
282−parseEntry :: ExpSYM repr repr => Parser repr
283−parseEntry = do
284− met <- parseMethod
285− spaces
286− url <- parseUrl
287− spaces
288− typ <- parseTyp
289− case typ of
290− Val s -> return $ url (met s)
291− ReqArgVal i o -> return $ url $ reqBody i (met o)
292−
293−blockComment :: Parser ()
294−blockComment = do
295− string "{-"
296− manyTill anyChar (try $ string "-}")
297− return ()
298−
299−inlineComment :: Parser ()
300−inlineComment = do
301− string "--"
302− manyTill anyChar (try $ lookAhead eol)
303− return ()
304−
305−eol :: Parser String
306−eol = try (string "\n\r")
307− <|> try (string "\r\n")
308− <|> string "\n"
309− <|> string "\r"
310− <?> "end of line"
311−
312−eols :: Parser ()
313−eols = skipMany $ void eol <|> blockComment <|> inlineComment
314−
315−parseAll :: Parser Type
316−parseAll = do
317− eols
318− entries <- parseEntry `endBy` eols
319− return $ foldr1 union entries
320− where union :: Type -> Type -> Type
321− union a = AppT (AppT (ConT ''(:<|>)) a)
322−
323−-- | The sitemap QuasiQuoter.
324−--
325−-- * @.../<var>:<type>/...@ becomes a capture
326−-- * @.../?<var>:<type>@ becomes a query parameter
327−-- * @<method> ... <typ>@ becomes a method returning @<typ>@
328−-- * @<method> ... <typ1> -> <typ2>@ becomes a method with request
329−-- body of @<typ1>@ and returning @<typ2>@
330−--
331−-- Comments are allowed, and have the standard Haskell format
332−--
333−-- * @--@ for inline
334−-- * @{- ... -}@ for block
335−--
336−sitemap :: QuasiQuoter
337−sitemap = QuasiQuoter { quoteExp = undefined
338− , quotePat = undefined
339− , quoteType = \x -> case parse parseAll "" x of
340− Left err -> error $ show err
341− Right st -> return st
342− , quoteDec = undefined
343− }
344−-}
test/Servant/QQSpec.hsdeleted−213
@@ -1,213 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE OverlappingInstances #-}
6−{-# LANGUAGE QuasiQuotes #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−module Servant.QQSpec where
11−
12−import Test.Hspec ( Expectation, Spec, shouldBe, it, describe, pendingWith )
13−
14−spec = describe "this" $ it "is" $ pendingWith "playing around"
15−{-
16−import Servant.API
17− ( (:<|>),
18− ReqBody,
19− QueryParam,
20− MatrixParam,
21− Put,
22− Get,
23− Post,
24− Capture,
25− (:>),
26− JSON,
27− sitemap )
28−
29−--------------------------------------------------------------------------
30−-- Types for testing
31−--------------------------------------------------------------------------
32−
33−-- Methods ---------------------------------------------------------------
34−type SimpleGet = [sitemap|
35−GET hello ()
36−|]
37−type SimpleGet' = "hello" :> Get '[JSON] ()
38−type SimpleGet'' = "hello" :> Get '[JSON] Bool
39−
40−type SimpleGet2 = [sitemap|
41−GET hello Bool
42−|]
43−type SimpleGet2' = "hello" :> Get '[JSON] Bool
44−type SimpleGet2'' = "hello" :> Get '[JSON] Int
45−
46−type SimplePost = [sitemap|
47−POST hello ()
48−|]
49−type SimplePost' = "hello" :> Post ()
50−type SimplePost'' = "hello" :> Post Bool
51−
52−type SimplePost2 = [sitemap|
53−POST hello Bool
54−|]
55−type SimplePost2' = "hello" :> Post Bool
56−type SimplePost2'' = "hello" :> Post ()
57−
58−type SimplePut = [sitemap|
59−PUT hello ()
60−|]
61−type SimplePut' = "hello" :> Put ()
62−type SimplePut'' = "hello" :> Put Bool
63−
64−type SimplePut2 = [sitemap|
65−PUT hello Bool
66−|]
67−type SimplePut2' = "hello" :> Put Bool
68−type SimplePut2'' = "hello" :> Put ()
69−
70−-- Parameters ------------------------------------------------------------
71−
72−type SimpleReqBody = [sitemap|
73−POST hello () -> Bool
74−|]
75−type SimpleReqBody' = "hello" :> ReqBody () :> Post Bool
76−type SimpleReqBody'' = "hello" :> ReqBody Bool :> Post ()
77−
78−type SimpleCapture = [sitemap|
79−POST hello/p:Int Bool
80−|]
81−type SimpleCapture' = "hello" :> Capture "p" Int :> Post Bool
82−type SimpleCapture'' = "hello" :> Capture "r" Int :> Post Bool
83−type SimpleCapture''' = "hello" :> Capture "p" Bool :> Post Bool
84−
85−type SimpleQueryParam = [sitemap|
86−POST hello/?p:Int Bool
87−|]
88−type SimpleQueryParam' = "hello" :> QueryParam "p" Int :> Post Bool
89−type SimpleQueryParam'' = "hello" :> QueryParam "r" Int :> Post Bool
90−type SimpleQueryParam''' = "hello" :> QueryParam "p" Bool :> Post Bool
91−
92−type SimpleMatrixParam = [sitemap|
93−POST hello;p:Int Bool
94−|]
95−type SimpleMatrixParam' = "hello" :> MatrixParam "p" Int :> Post Bool
96−type SimpleMatrixParam'' = "hello" :> MatrixParam "r" Int :> Post Bool
97−type SimpleMatrixParam''' = "hello" :> MatrixParam "p" Bool :> Post Bool
98−
99−type ComplexMatrixParam = [sitemap|
100−POST hello;p:Int;q:String/world;r:Int Bool
101−|]
102−type ComplexMatrixParam' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Int :> Post Bool
103−type ComplexMatrixParam'' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "s" Int :> Post Bool
104−type ComplexMatrixParam''' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Bool :> Post Bool
105−
106−-- Combinations ----------------------------------------------------------
107−
108−type TwoPaths = [sitemap|
109−POST hello Bool
110−GET hello Bool
111−|]
112−type TwoPaths' = ("hello" :> Post Bool) :<|> ("hello" :> Get '[JSON] Bool)
113−
114−type WithInlineComments = [sitemap|
115−GET hello Bool -- This is a comment
116−|]
117−type WithInlineComments' = "hello" :> Get '[JSON] Bool
118−
119−type WithInlineComments2 = [sitemap|
120−GET hello Bool
121−-- This is a comment
122−|]
123−type WithInlineComments2' = "hello" :> Get '[JSON] Bool
124−
125−
126−type WithBlockComments = [sitemap|
127−GET hello Bool {-
128−POST hello Bool
129−-}
130−|]
131−type WithBlockComments' = "hello" :> Get '[JSON] Bool
132−
133−type WithBlockComments2 = [sitemap|
134−GET hello Bool {-
135−POST hello Bool
136−-}
137−POST hello Bool
138−|]
139−type WithBlockComments2' = ("hello" :> Get '[JSON] Bool) :<|> ("hello" :> Post Bool)
140−
141−--------------------------------------------------------------------------
142−-- Spec
143−--------------------------------------------------------------------------
144−
145−spec :: Spec
146−spec = do
147− describe "'sitemap' QuasiQuoter" $ do
148− it "Handles simple GET types" $ do
149− (u::SimpleGet) ~= (u::SimpleGet' ) ~> True
150− (u::SimpleGet) ~= (u::SimpleGet'' ) ~> False
151− (u::SimpleGet2) ~= (u::SimpleGet2' ) ~> True
152− (u::SimpleGet2) ~= (u::SimpleGet2'') ~> False
153− it "Handles simple POST types" $ do
154− (u::SimplePost) ~= (u::SimplePost' ) ~> True
155− (u::SimplePost) ~= (u::SimplePost'' ) ~> False
156− (u::SimplePost2) ~= (u::SimplePost2' ) ~> True
157− (u::SimplePost2) ~= (u::SimplePost2'') ~> False
158− it "Handles simple PUT types" $ do
159− (u::SimplePut) ~= (u::SimplePut' ) ~> True
160− (u::SimplePut) ~= (u::SimplePut'' ) ~> False
161− (u::SimplePut2) ~= (u::SimplePut2' ) ~> True
162− (u::SimplePut2) ~= (u::SimplePut2'') ~> False
163− it "Handles simple request body types" $ do
164− (u::SimpleReqBody) ~= (u::SimpleReqBody' ) ~> True
165− (u::SimpleReqBody) ~= (u::SimpleReqBody'') ~> False
166− it "Handles simple captures" $ do
167− (u::SimpleCapture) ~= (u::SimpleCapture' ) ~> True
168− (u::SimpleCapture) ~= (u::SimpleCapture'') ~> False
169− (u::SimpleCapture) ~= (u::SimpleCapture''') ~> False
170− it "Handles simple querystring parameters" $ do
171− (u::SimpleQueryParam) ~= (u::SimpleQueryParam' ) ~> True
172− (u::SimpleQueryParam) ~= (u::SimpleQueryParam'') ~> False
173− (u::SimpleQueryParam) ~= (u::SimpleQueryParam''') ~> False
174− it "Handles simple matrix parameters" $ do
175− (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam' ) ~> True
176− (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam'') ~> False
177− (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam''') ~> False
178− it "Handles more complex matrix parameters" $ do
179− (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam' ) ~> True
180− (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam'') ~> False
181− (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam''') ~> False
182− it "Handles multiples paths" $ do
183− (u::TwoPaths) ~= (u::TwoPaths') ~> True
184− it "Ignores inline comments" $ do
185− (u::WithInlineComments) ~= (u::WithInlineComments') ~> True
186− (u::WithInlineComments2) ~= (u::WithInlineComments2') ~> True
187− it "Ignores inline comments" $ do
188− (u::WithBlockComments) ~= (u::WithBlockComments') ~> True
189− (u::WithBlockComments2) ~= (u::WithBlockComments2') ~> True
190−
191−-}
192−
193−--------------------------------------------------------------------------
194−-- Utilities
195−--------------------------------------------------------------------------
196−data HTrue
197−data HFalse
198−
199−-- Kiselyov's Type Equality predicate
200−class TypeEq x y b | x y -> b where { areEq :: x -> y -> Bool }
201−instance TypeEq x x HTrue where { areEq _ _ = True }
202−instance b ~ HFalse => TypeEq x y b where { areEq _ _ = False}
203−
204−infix 4 ~=
205−(~=) :: TypeEq x y b => x -> y -> Bool
206−(~=) = areEq
207−
208−u :: a
209−u = undefined
210−
211−infix 3 ~>
212−(~>) :: (Show a, Eq a) => a -> a -> Expectation
213−(~>) = shouldBe