@@ -1,3 +1,6 @@
1+{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE TypeOperators #-}
3+{-# LANGUAGE UndecidableInstances #-}
1module Servant.API (
4module Servant.API (
3 -- * Combinators
6 -- * Combinators
@@ -43,11 +46,16 @@ module Servant.API (
43 module Servant.Common.Text,
46 module Servant.Common.Text,
44 -- | Classes and instances for types that can be converted to and from @Text@
47 -- | Classes and instances for types that can be converted to and from @Text@
49+ -- * Canonicalizing (flattening) API types
46 -- * Utilities
53 -- * Utilities
47 module Servant.Utils.Links,
54 module Servant.Utils.Links,
48 -- | Type-safe internal URIs
55 -- | Type-safe internal URIs
58+import Data.Proxy (Proxy(..))
51import Servant.Common.Text (FromText(..), ToText(..))
59import Servant.Common.Text (FromText(..), ToText(..))
52import Servant.API.Alternative ((:<|>) (..))
60import Servant.API.Alternative ((:<|>) (..))
53import Servant.API.Capture (Capture)
61import Servant.API.Capture (Capture)
@@ -69,3 +77,34 @@ import Servant.API.ReqBody (ReqBody)
69import Servant.API.Sub ((:>))
77import Servant.API.Sub ((:>))
70import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
78import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
71 URI (..), safeLink)
79 URI (..), safeLink)
81+-- | Turn an API type into its canonical form.
83+-- The canonical form of an API type is basically the all-flattened form
84+-- of the original type. More formally, it takes a type as input and hands you
85+-- back an /equivalent/ type formed of toplevel `:<|>`-separated chains of `:>`s,
86+-- i.e with all `:>`s distributed inside the `:<|>`s.
88+-- It basically turns:
90+-- > "hello" :> (Get Hello :<|> ReqBody Hello :> Put Hello)
94+-- > ("hello" :> Get Hello) :<|> ("hello" :> ReqBody Hello :> Put Hello)
96+-- i.e distributing all ':>'-separated bits into the subsequent ':<|>'s.
97+type family Canonicalize api :: * where
98+ -- requires UndecidableInstances
99+ Canonicalize (a :> (b :<|> c)) = a :> Canonicalize b :<|> a :> Canonicalize c
100+ Canonicalize ((a :<|> b) :> c) = a :> Canonicalize c :<|> b :> Canonicalize c
101+ Canonicalize (a :> b) = Redex b (Canonicalize b) a
102+ Canonicalize (a :<|> b) = Canonicalize a :<|> Canonicalize b
105+type family Redex a b c :: * where
106+ Redex a a first = Canonicalize first :> a
107+ Redex a b first = Canonicalize (first :> b)
109+canonicalize :: Proxy layout -> Proxy (Canonicalize layout)
110+canonicalize Proxy = Proxy