@@ -1,8 +1,11 @@
1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE KindSignatures #-}
3{-# LANGUAGE KindSignatures #-}
3{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeOperators #-}
7+{-# LANGUAGE ConstraintKinds #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
7module Servant.API.TypeLevel where
10module Servant.API.TypeLevel where
@@ -14,6 +17,9 @@ import Servant.API.Header ( Header )
14import Servant.API.Verbs ( Verb )
17import Servant.API.Verbs ( Verb )
15import Servant.API.Sub ( type (:>) )
18import Servant.API.Sub ( type (:>) )
16import Servant.API.Alternative ( type (:<|>) )
19import Servant.API.Alternative ( type (:<|>) )
20+#if MIN_VERSION_base(4,9,0)
21+import GHC.TypeLits (TypeError, ErrorMessage(..))
18-- * API predicates
24-- * API predicates
@@ -104,9 +110,24 @@ type family IsSubList a b :: Constraint where
104 IsSubList '[] b = ()
110 IsSubList '[] b = ()
105 IsSubList (x ': xs) y = Elem x y `And` IsSubList xs y
111 IsSubList (x ': xs) y = Elem x y `And` IsSubList xs y
113+-- | Check that an eleme is an element of a list:
115+-- >>> ok (Proxy :: Proxy (Elem Bool '[Int, Bool]))
118+-- >>> ok (Proxy :: Proxy (Elem String '[Int, Bool]))
120+-- No instance for (ElemNotFoundIn [Char] '[Int, Bool])
121+-- arising from a use of ‘ok’
123+type Elem e es = ElemGo e es es
125+-- 'orig' is used to store original list for better error messages
107−type family Elem e es :: Constraint where
126+type family ElemGo e es orig :: Constraint where
108− Elem x (x ': xs) = ()
127+ ElemGo x (x ': xs) orig = ()
109− Elem y (x ': xs) = Elem y xs
128+ ElemGo y (x ': xs) orig = ElemGo y xs orig
129+ ElemGo x '[] orig = ElemNotFoundIn x orig
111-- ** Logic
132-- ** Logic
@@ -121,3 +142,33 @@ type family Or (a :: Constraint) (b :: Constraint) :: Constraint where
121type family And (a :: Constraint) (b :: Constraint) :: Constraint where
142type family And (a :: Constraint) (b :: Constraint) :: Constraint where
122 And () () = ()
143 And () () = ()
145+-- * Custom type errors
147+#if MIN_VERSION_base(4,9,0)
151+type ElemNotFoundIn val list = TypeError
152+ (ShowType val :<>: Text " expected in list " :<>: ShowList list)
157+type family ShowListGo ls :: ErrorMessage where
158+ ShowListGo '[] = Text ""
159+ ShowListGo (x ': xs) = ShowType x :<>: Text ", " :<>: ShowListGo xs
161+type ShowList ls = Text "[" :<>: ShowListGo ls :<>: Text "]"
167+class ElemNotFoundIn val list
172+-- >>> import Data.Proxy
173+-- >>> data OK = OK deriving (Show)
174+-- >>> let ok :: ctx => Proxy ctx -> OK; ok _ = OK