|
| 1 | +synopsis: Display capture hints in router layout |
| 2 | +prs: #1556 |
| 3 | + |
| 4 | +description: { |
| 5 | + |
| 6 | +This PR enhances the `Servant.Server.layout` function, which produces a textual description of the routing layout of an API. More precisely, it changes `<capture>` blocks, so that they display the name and type of the variable being captured instead. |
| 7 | + |
| 8 | +Example: |
| 9 | + |
| 10 | +For the following API |
| 11 | +```haskell |
| 12 | +type API = |
| 13 | + "a" :> "d" :> Get '[JSON] NoContent |
| 14 | + :<|> "b" :> Capture "x" Int :> Get '[JSON] Bool |
| 15 | + :<|> "a" :> "e" :> Get '[JSON] Int |
| 16 | +``` |
| 17 | + |
| 18 | +we previously got the following output: |
| 19 | + |
| 20 | +``` |
| 21 | +/ |
| 22 | +├─ a/ |
| 23 | +│ ├─ d/ |
| 24 | +│ │ └─• |
| 25 | +│ └─ e/ |
| 26 | +│ └─• |
| 27 | +└─ b/ |
| 28 | + └─ <capture>/ |
| 29 | + ├─• |
| 30 | + ┆ |
| 31 | + └─• |
| 32 | +``` |
| 33 | + |
| 34 | +now we get: |
| 35 | + |
| 36 | +``` |
| 37 | +/ |
| 38 | +├─ a/ |
| 39 | +│ ├─ d/ |
| 40 | +│ │ └─• |
| 41 | +│ └─ e/ |
| 42 | +│ └─• |
| 43 | +└─ b/ |
| 44 | + └─ <x::Int>/ |
| 45 | + ├─• |
| 46 | + ┆ |
| 47 | + └─• |
| 48 | +``` |
| 49 | + |
| 50 | +This change is achieved by the introduction of a CaptureHint type, which is passed as an extra argument to the CaptureRouter and CaptureAllRouter constructors for the Router' type. |
| 51 | +CaptureHint values are then used in routerLayout, to display the name and type of captured values, instead of just `<capture>` previously. |
| 52 | + |
| 53 | +N.B.: |
| 54 | +Because the choice smart constructor for routers can aggregate Capture combinators with different capture hints, the Capture*Router constructors actually take a list of CaptureHint, instead of a single one. |
| 55 | + |
| 56 | +This PR also introduces Spec tests for the routerLayout function. |
| 57 | + |
| 58 | +Warning: |
| 59 | +This change is potentially breaking, because it adds the constraint `Typeable a` to all types that are to be captured. Because all types are typeable since GHC 7.10, this is not as bad as it sounds ; it only break expressions where `a` is quantified in an expression with `Capture a`. |
| 60 | +In those cases, the fix is easy: it suffices to add `Typeable a` to the left-hand side of the quantification constraint. |
| 61 | + |
| 62 | +For instance, the following code will no longer compile: |
| 63 | +```haskell |
| 64 | +type MyAPI a = Capture "foo" a :> Get '[JSON] () |
| 65 | + |
| 66 | +myServer :: forall a. Server (MyAPI a) |
| 67 | +myServer = const $ return () |
| 68 | + |
| 69 | +myApi :: forall a. Proxy (MyAPI a) |
| 70 | +myApi = Proxy |
| 71 | + |
| 72 | +app :: forall a. (FromHttpApiData a) => Application |
| 73 | +app = serve (myApi @a) (myServer @a) |
| 74 | +``` |
| 75 | + |
| 76 | +Indeed, `app` should be replaced with: |
| 77 | +```haskell |
| 78 | +app :: forall a. (FromHttpApiData a, Typeable a) => Application |
| 79 | +app = serve (myApi @a) (myServer @a) |
| 80 | +``` |
| 81 | +} |
0 commit comments