Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

(:<|>) is not Associative #1643

Open
ChrisPenner opened this issue Jan 26, 2023 · 1 comment
Open

(:<|>) is not Associative #1643

ChrisPenner opened this issue Jan 26, 2023 · 1 comment

Comments

@ChrisPenner
Copy link

Hello wonderful servant maintainers,

I guess whether this is actually a bug depends on your expected behaviour, but I wasn't easily able to find the expected behaviour written down anywhere (maybe I just missed it);
Given that :<|> is defined in Servant.API.Alternative I expected it to be associative, so I was quite surprised when I learned it isn't!

Here's a small reproduction which demonstrates this:

type FlatAPI =
  ("a" :> "b" :> Post '[JSON] Int)
    :<|> ("a" :> "b" :> Delete '[JSON] String)
    :<|> ("c" :> "d" :> Post '[JSON] Int)
    :<|> ("c" :> "d" :> Delete '[JSON] String)

type NestedAPI =
  ("a" :> "b" :> (Post '[JSON] Int :<|> Delete '[JSON] String))
    :<|> ("c" :> "d" :> (Post '[JSON] Int :<|> Delete '[JSON] String))

handlePost :: Handler Int
handlePost = pure 1

handleDelete :: Handler String
handleDelete = pure "hello"

server :: Server FlatAPI
server =
  handlePost
    :<|> handleDelete
    :<|> handlePost
    :<|> handleDelete

nestedServer :: Server NestedAPI
nestedServer =
  (handlePost :<|> handleDelete) -- < these brackets are required.
    :<|> handlePost
    :<|> handleDelete

I had assumed that FlatAPI and NestedAPI were equivalent, but it turns out that NestedAPI requires you to manually group your server implementations to match the bracketing on your API. Remove the brackets causes it to fail to compile.

If this is expected, that's fine, just letting you know that I got tripped up here 😄

@alpmestan
Copy link
Contributor

This is sadly a consequence of "just" representing endpoints as an agglomerate of :<|> separated stuffs, instead of having this "reduce" to some internal representation that will "quotient out" the kind of difference you are talking about (e.g generating a [Any] or Vector Any or something underneath).

I wrote a tiny package for "flattening" things in case that's useful: https://hackage.haskell.org/package/servant-flatten-0.2/docs/Servant-API-Flatten.html

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants