From 34aed1d28989a8a91ddc64e7aafafdc807d71249 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Thu, 17 Feb 2022 16:11:26 +0100 Subject: [PATCH] removing Generic cookbook in favour of NamedRoutes --- cabal.project | 1 - doc/cookbook/generic/Generic.lhs | 141 ----------------------------- doc/cookbook/generic/generic.cabal | 25 ----- 3 files changed, 167 deletions(-) delete mode 100644 doc/cookbook/generic/Generic.lhs delete mode 100644 doc/cookbook/generic/generic.cabal diff --git a/cabal.project b/cabal.project index ce32726d..092e12a4 100644 --- a/cabal.project +++ b/cabal.project @@ -34,7 +34,6 @@ packages: doc/cookbook/db-postgres-pool doc/cookbook/db-sqlite-simple doc/cookbook/file-upload - doc/cookbook/generic doc/cookbook/hoist-server-with-context doc/cookbook/https doc/cookbook/jwt-and-basic-auth diff --git a/doc/cookbook/generic/Generic.lhs b/doc/cookbook/generic/Generic.lhs deleted file mode 100644 index 45180230..00000000 --- a/doc/cookbook/generic/Generic.lhs +++ /dev/null @@ -1,141 +0,0 @@ -# Using generics - -```haskell -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeOperators #-} -module Main (main, api, getLink, routesLinks, cliGet) where - -import Control.Exception (throwIO) -import Control.Monad.Trans.Reader (ReaderT, runReaderT) -import Data.Proxy (Proxy (..)) -import Network.Wai.Handler.Warp (run) -import System.Environment (getArgs) - -import Servant -import Servant.Client - -import Servant.API.Generic -import Servant.Client.Generic -import Servant.Server.Generic -``` - -The usage is simple, if you only need a collection of routes. -First you define a record with field types prefixed by a parameter `route`: - -```haskell -data Routes route = Routes - { _get :: route :- Capture "id" Int :> Get '[JSON] String - , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool - } - deriving (Generic) -``` - -Then we'll use this data type to define API, links, server and client. - -## API - -You can get a `Proxy` of the API using `genericApi`: - -```haskell -api :: Proxy (ToServantApi Routes) -api = genericApi (Proxy :: Proxy Routes) -``` - -It's recommended to use `genericApi` function, as then you'll get -better error message, for example if you forget to `derive Generic`. - -## Links - -The clear advantage of record-based generics approach, is that -we can get safe links very conveniently. We don't need to define endpoint types, -as field accessors work as proxies: - -```haskell -getLink :: Int -> Link -getLink = fieldLink _get -``` - -We can also get all links at once, as a record: - -```haskell -routesLinks :: Routes (AsLink Link) -routesLinks = allFieldLinks -``` - -## Client - -Even more power starts to show when we generate a record of client functions. -Here we use `genericClientHoist` function, which lets us simultaneously -hoist the monad, in this case from `ClientM` to `IO`. - -```haskell -cliRoutes :: Routes (AsClientT IO) -cliRoutes = genericClientHoist - (\x -> runClientM x env >>= either throwIO return) - where - env = error "undefined environment" - -cliGet :: Int -> IO String -cliGet = _get cliRoutes -``` - -## Server - -Finally, probably the most handy usage: we can convert record of handlers into -the server implementation: - -```haskell -record :: Routes AsServer -record = Routes - { _get = return . show - , _put = return . odd - } - -app :: Application -app = genericServe record - -main :: IO () -main = do - args <- getArgs - case args of - ("run":_) -> do - putStrLn "Starting cookbook-generic at http://localhost:8000" - run 8000 app - -- see this cookbook below for custom-monad explanation - ("run-custom-monad":_) -> do - putStrLn "Starting cookbook-generic with a custom monad at http://localhost:8000" - run 8000 (appMyMonad AppCustomState) - _ -> putStrLn "To run, pass 'run' argument: cabal new-run cookbook-generic run" -``` - -## Using generics together with a custom monad - -If your app uses a custom monad, here's how you can combine it with -generics. - -```haskell -data AppCustomState = - AppCustomState - -type AppM = ReaderT AppCustomState Handler - -apiMyMonad :: Proxy (ToServantApi Routes) -apiMyMonad = genericApi (Proxy :: Proxy Routes) - -getRouteMyMonad :: Int -> AppM String -getRouteMyMonad = return . show - -putRouteMyMonad :: Int -> AppM Bool -putRouteMyMonad = return . odd - -recordMyMonad :: Routes (AsServerT AppM) -recordMyMonad = Routes {_get = getRouteMyMonad, _put = putRouteMyMonad} - --- natural transformation -nt :: AppCustomState -> AppM a -> Handler a -nt s x = runReaderT x s - -appMyMonad :: AppCustomState -> Application -appMyMonad state = genericServeT (nt state) recordMyMonad diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal deleted file mode 100644 index 725f70c9..00000000 --- a/doc/cookbook/generic/generic.cabal +++ /dev/null @@ -1,25 +0,0 @@ -cabal-version: 2.2 -name: cookbook-generic -version: 0.1 -synopsis: Using custom monad to pass a state between handlers -homepage: http://docs.servant.dev/ -license: BSD-3-Clause -license-file: ../../../servant/LICENSE -author: Servant Contributors -maintainer: haskell-servant-maintainers@googlegroups.com -build-type: Simple -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 - -executable cookbook-using-custom-monad - main-is: Generic.lhs - build-depends: base == 4.* - , servant - , servant-client - , servant-client-core - , servant-server - , base-compat - , warp >= 3.2 - , transformers >= 0.3 - default-language: Haskell2010 - ghc-options: -Wall -pgmL markdown-unlit - build-tool-depends: markdown-unlit:markdown-unlit >= 0.4