on master help sign in
Merge pull request #23 from haskell-servant/canonical-types

Add the `Canonicalize` type family to reduce API types to their canonical forms before proceeding in the other packages

commit3311cbd9015e94b628336718f85cbc2edc1ea9d3 authorAlp Mestanogullari <alpmestan@gmail.com> dateWed 11 Mar 2015, 12:17 (12 years ago) parent13dcf087a83952
3 files changed+42
CHANGELOG.md+1 src/Servant/API.hs+39 src/Servant/API/ContentTypes.hs+2
CHANGELOG.md+1
@@ -1,5 +1,6 @@
110.3
22---
3+* Add a `Canonicalize` type family that distributes all `:>`s inside `:<|>`s to get to the canonical type of an API -- which is then used in all other packages to avoid weird handler types in *servant-server*.
34* Multiple content-type/accept support for all the relevant combinators
45* Provide *JSON*, *PlainText*, *OctetStream* and *FormUrlEncoded* content types out of the box
56* Type-safe link generation to API endpoints
src/Servant/API.hs+39
@@ -1,3 +1,6 @@
1+{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE TypeOperators #-}
3+{-# LANGUAGE UndecidableInstances #-}
14module Servant.API (
25
36 -- * Combinators
@@ -43,11 +46,16 @@ module Servant.API (
4346 module Servant.Common.Text,
4447 -- | Classes and instances for types that can be converted to and from @Text@
4548
49+ -- * Canonicalizing (flattening) API types
50+ Canonicalize,
51+ canonicalize,
52+
4653 -- * Utilities
4754 module Servant.Utils.Links,
4855 -- | Type-safe internal URIs
4956 ) where
5057
58+import Data.Proxy (Proxy(..))
5159import Servant.Common.Text (FromText(..), ToText(..))
5260import Servant.API.Alternative ((:<|>) (..))
5361import Servant.API.Capture (Capture)
@@ -69,3 +77,34 @@ import Servant.API.ReqBody (ReqBody)
6977import Servant.API.Sub ((:>))
7078import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
7179 URI (..), safeLink)
80+
81+-- | Turn an API type into its canonical form.
82+--
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.
87+--
88+-- It basically turns:
89+--
90+-- > "hello" :> (Get Hello :<|> ReqBody Hello :> Put Hello)
91+--
92+-- into
93+--
94+-- > ("hello" :> Get Hello) :<|> ("hello" :> ReqBody Hello :> Put Hello)
95+--
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
103+ Canonicalize a = a
104+
105+type family Redex a b c :: * where
106+ Redex a a first = Canonicalize first :> a
107+ Redex a b first = Canonicalize (first :> b)
108+
109+canonicalize :: Proxy layout -> Proxy (Canonicalize layout)
110+canonicalize Proxy = Proxy
src/Servant/API/ContentTypes.hs+2
@@ -55,6 +55,8 @@ module Servant.API.ContentTypes
5555 , AcceptHeader(..)
5656 , AllCTRender(..)
5757 , AllCTUnrender(..)
58+ , AllMimeRender(..)
59+ , AllMimeUnrender(..)
5860 , FromFormUrlEncoded(..)
5961 , ToFormUrlEncoded(..)
6062 , IsNonEmpty