haskell-servant / haskell-servant/servant

Generic: provide a suffix alternative to `:-`

Open
#1,466 5 comments 6 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
2k
Forks
427
Avg merge
2d 23h
Merged PRs (30d)
5

Description

This is purely a cosmetic consideration, but one that has the potential of improving understand-ability of generic capabilities in servant.

Here's how the cookbook showcases generic support in servant:

> 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:

```
data Routes route = Routes
{ _get :: route :- Capture "id" Int :> Get '[JSON] String
, _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
}
deriving (Generic)
```

In my experience with servant at work, this has sent a lot of people (myself included) down the wrong path: this gives the impression that the API types are appended to some `route` (based on the `:-` operator that looks like `:>`, and on the `route` parameter name).

Reading the code told me this not the case at all, actually. `route` is instead called `mode` in the lib internals, and the `:-` operator is not appending an API type to an existing route; rather it _projects_ an API type according to a _mode_ argument (`AsClient`, `AsServer`, …). This may not sound like a big issue, but this generated a lot of frustration when we migrated to record-based routes definitions, especially when it came to nested records.

# A modest suggestion

An alternate definition I've tried internally with good reception is to see the `:-` operator as some kind of annotation. In that case, a suffix definition works better (to align with type annotations):

```
infix 0 `As`
type As api mode = mode :- api

data Routes mode = Routes
{ _get :: Capture "id" Int :> Get '[JSON] String `As` mode
, _put :: ReqBody '[JSON] Int :> Put '[JSON] Bool `As` mode
}
deriving (Generic)
```

It's highly subjective, of course, but I think it helps reader "get" what's going on more clearly than `:-`.

I'm not sure where the definition should go, in the lib or in the cookbook, and if `As` is the best name (since `mode` will be instantiated to something like `AsServer` or `AsClient`, the `As` would be redundant).

I'd gladly open a PR to add the type alias and modify the relevant tests / cookbook examples if some kind of consensus is reached

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.