Generic: provide a suffix alternative to `:-`
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
This is a very interesting step forward. I don't see anything shocking at first sight.
I just confused my colleague by trying to explain what :- meant; pointing him at this issue seemed to help. +1 from me to move away from an operator toward a named combinator.
How about Using? It seems to scan passably with things like AsServerT m:
Capture "id" Int :> Get '[JSON] String `Using` AsServerT m ==> ServerT m (Capture "Id" Int :> Get '[JSON] String)
Alternatives: WithMode, InMode, ...?
I too like this. I quite often make the mistake that I accidentally type :> instead of :- which leads to very obscure and hard to debug error messages. Especially if the type parameter is route My brain more quickly jumps to :>.
I also misunderstood it until finding this issue.