haskell-servant / haskell-servant/servant

Specialized `fromServant` for clients

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

Description

When dealing with a generic and nested API, like this - admittedly simplified - one:

```hs
data API route = API
{
foo :: route :- "foo" :> ToServantApi FooAPI
} deriving (Generic)

data FooAPI route = FooAPI
{
bar :: route :- "bar" :> ToServantApi BarAPI
} deriving (Generic)

data BarAPI route = BarAPI
{
baz :: route :- "baz" :> Get '[JSON] String
} deriving (Generic)
```

Composition for client callers is tricky. One can of course write separated functions, like this:

```hs
apiClient :: API (AsClientT ClientM)
apiClient = genericClient

fooClient :: FooAPI (AsClientT ClientM)
fooClient = fromServant $ foo apiClient

barClient :: BarAPI (AsClientT ClientM)
barClient = fromServant $ bar apiClient
```

Of course, in this silly example, it's acceptable boiler-plate, but picture a much bigger and deeper API. We quickly realized it was not possible to do something that would have felt natural, like:

```hs
quickBarClient :: BarAPI (AsClientT ClientM)
quickBarClient = fromServant . bar . fromServant . foo $ genericClient
```

I'm not quite sure I understand why exactly, but since the compiler kept getting lost with ambiguous types over the `route` parameter, we've tried this simple enough function:

```
fromServant' :: (m ~ AsClientT n) => GenericServant routes m
=> ToServant routes m -> routes m
fromServant' = fromServant
```

And lo and behold, it lets us compose thus:

```hs
quickBarClient :: BarAPI (AsClientT ClientM)
quickBarClient = fromServant' . bar . fromServant' . foo $ genericClient
```

This leads me to two questions:

1°) Is there such a function already somewhere in the codebase ? Because I feel like I'm missing something and not using your excellent library properly.
2°) If the answer to the previous question was no, what about adding such a specialized function (maybe with a shorter name) to the Servant.Client.Generic module to guide users towards a workable composition ?

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.