3

I am going through the servant tutorial found here. There is a partial implementation that is left for the user to figure out what it should be.

type APIFor a i =
       Get '[JSON] [a]
  :<|> ReqBody '[JSON] a :> PostNoContent
  :<|> Capture "id" i :>
         ( Get '[JSON] a
      :<|> ReqBody '[JSON] a :> PutNoContent
      :<|> DeleteNoContent
         )

serverFor :: Handler [a]
          -> (a -> Handler NoContent)
          -> (i -> Handler a)
          -> (i -> a -> Handler NoContent)
          -> (i -> Handler NoContent)
          -> Server (APIFor a i)
serverFor = error "..." -- What should be here?

Following from the previous examples I came up with the implementation:

serverFor = listing :<|> ops

    where
        listing :: Handler [a]
        listing = error ""

        ops a i =
            creating a
            :<|> getById i
            :<|> updating i a
            :<|> deleting i

            where
                creating :: a -> Handler NoContent
                creating a = error ""

                getById :: i -> Handler a
                getById id = error ""

                updating :: i -> a -> Handler NoContent
                updating i a = error ""

                deleting :: i -> Handler NoContent
                deleting i = error ""

But am getting the error:

• Couldn't match expected type ‘Handler [a]
                                -> (a -> Handler NoContent)
                                -> (i -> Handler a)
                                -> (i -> a -> Handler NoContent)
                                -> (i -> Handler NoContent)
                                -> Server (APIFor a i)’
              with actual type ‘Handler [a0]
                                :<|> (a1
                                      -> i0
                                      -> Handler NoContent
                                         :<|> (Handler a2
                                               :<|> (Handler NoContent :<|> Handler NoContent)))’
• Possible cause: ‘(:<|>)’ is applied to too many arguments
  In the expression: listing :<|> ops

I understand what the error is saying but do not know how to implement it correctly. Is anyone able to help with what the correct implementation should be?

Sylvoo
  • 265
  • 2
  • 17
  • Does it work if you remove all type annotations for the functions in the `where` blocks? – l7r7 Sep 08 '22 at 08:01
  • I think this is essentially the same problem as this: https://stackoverflow.com/questions/22436402/how-to-add-type-annotation-in-let-binding/22436624#22436624 – l7r7 Sep 08 '22 at 08:12
  • @l7r7 Removing the type annotations does not work, nor does adding the language extension ScopedTypeVariables – Sylvoo Sep 08 '22 at 10:48
  • I missed the other part of the problem, which is now addressed in an answer – l7r7 Sep 08 '22 at 12:08

1 Answers1

3

The signature of the serverFor function says it takes handler functions and builds the Server.

serverFor list create get update delete = 
  -- combine handlers according to API type
  list :<|> create :<|> (\i -> get i :<|> update i :<|> delete i)

you can then call serverFor for your specific types.

data User = User {id :: Int}

server :: Server (APIFor User Int)
server = serverFor listing creating getById updating deleting
  where
    listing :: Handler [User]
    listing = error ""
    creating :: User -> Handler NoContent
    creating a = error ""
    getById :: Int -> Handler User
    getById id = error ""
    updating :: Int -> User -> Handler NoContent
    updating i a = error ""
    deleting :: Int -> Handler NoContent
    deleting i = error ""
l7r7
  • 1,134
  • 1
  • 7
  • 23
ekim boran
  • 1,809
  • 12
  • 22
  • This is close, but the `(\i -> get i :<|> update i :<|> delete i)` should just be `(get :<|> update :<|> delete)` - the handlers don't share that argument. – Axman6 Sep 23 '22 at 06:10