[Haskell-cafe] Code review - wrapping servant client to hide required parameters

Nigel Rantor wiggly at wiggly.org
Mon Jan 2 16:05:43 UTC 2017


Hello,

So, I am still learning and would like someone to cast an eye over some 
code to see if I am missing something in terms of making it cleaner/more 
concise.

I have begun wrapping the They Work For You API using the Servant Client 
library.

All code is already online for you to grab at;

https://github.com/wiggly/twfy-api-client
https://hackage.haskell.org/package/twfy-api-client

The API is not under my control so I can't change how it works or how it 
accepts parameters.

Every API call requires a Key to be passed as a query string parameter. 
For a particular session/user this will always be the same, and there is 
no point in allowing the user to not provide it.

Since it is a query parameter Servant forces the type to be within 
Maybe. For example;

     type TwfyAPI = "getConstituency" :> QueryParam "key" ApiKey :> 
QueryParam "name" T.Text :> QueryParam "postcode" T.Text :> Get 
'[JsonIso8859] Constituency

Gives the following function signature;

     getConstituency :: Maybe ApiKey -- ^ API key
                                -> Maybe T.Text -- ^ Name
                                -> Maybe T.Text -- ^ Post code
                                -> ClientM Constituency

To make the API easier to use I have created a Client module that wraps 
these so that I can hide the passing of the API key for every call, and 
also allow the user to not have to deal with http client managers or URL 
parsing if they don't want to. The upshot is that I get a signature that 
now accepts a data type I have created that contains all the information 
required to run the client action and return a result, like so (where 
A.getConstituency is the function above taking a Maybe ApiKey as first 
argument);

     getConstituency :: Client
                                -> Maybe T.Text -- ^ Name
                                -> Maybe T.Text -- ^ Post code
                                -> IO (Either ServantError Constituency)
     getConstituency c = do
         let key = Just $ clientApiKey c
              env = clientEnv c
         in (\name postCode -> (flip runClientM) env (A.getConstituency 
key name postCode))

So, this wraps up all of the calling of the servant client with an 
appropriate environment and arguments.

I have two questions;

1) What do people think of this style of wrapping the servant client calls?

2) I am now returning a lambda that accepts the remaining arguments to 
the call, is there any way I can make this cleaner? i.e. is it possible 
for me to not have to specifically re-write all of the function 
parameters using something like uncurry or other machinery? I have 
played with trying to use uncurry but got nowhere fast.

Thanks in advance,

   Nigel


More information about the Haskell-Cafe mailing list