Haskell beginner here, trying to wrap a HTTP REST API in a safe way and with automatic Aeson decoding of return values. I started with a Haskell function for every API call. It was a bit boilerplateish, but ok.
Looking to improve things I wanted to turn each API call into a data type of its own. For example for logging in, I would model that as Login
type that specifies the method, Credentials
type for method parameters and LoginReponse
for the result of the API call. Parameters and response types of course have corresponding FromJSON and ToJSON instances.
For two API calls it looks something like this (using GADTs):
data Void = Void
data Credentials = Credentials {...}
data LoginResponse = LoginResponse {...}
data LogoutResponse = LogoutResponse {...}
data Command a where
Login :: Credentials -> Command LoginResponse
Logout :: Void -> Command LogoutResponse
execute :: FromJSON a => Command a -> IO a
execute cmd = do
manager <- newManager tlsManagerSettings
let request = buildHttpRequest cmd
result <- httpLbs request manager
let body = responseBody result
let parsed = fromJust $ decode body
return parsed
This works great for my use case - I can introspect Commands before executing them, I can't construct invalid API calls and Aeson knows how to decode the return values!
Only problem with this approach is that I have to keep all of my Commands in a single file under single data declaration.
I'd like to move method definitions (in my example Login
and Logout
) to separate modules, but to keep the execute
function similar, and of course keep the type safety and Aeson decoding.
I've tried to make something using type classes, but got nowhere.
Any tips how to do that are welcome!
buildHttpRequest
request function, theCommand
data family, and anything else you need. As a side note, the type you have named "Void" is usually just called()
in Haskell (Void is usually reserved for the empty type). – user2407038