Skip to content

Question: mapping over a Handler? #119

New issue

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

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

Already on GitHub? Sign in to your account

Open
lexi-lambda opened this issue Apr 11, 2017 · 5 comments
Open

Question: mapping over a Handler? #119

lexi-lambda opened this issue Apr 11, 2017 · 5 comments
Labels

Comments

@lexi-lambda
Copy link

This is a reproduction of an open Stack Overflow question, which I think might get more visibility here.

I have created a simple API for testing purposes:

type Query = Object "Query" '[]
  '[ Argument "id" Text :> Field "test" (Maybe Foo) ]

type Foo = Object "Foo" '[]
  '[ Field "name" Text ]

I also have a Haskell type that represents the Foo resource, as well as a function for retrieving it from the database:

data ServerFoo = ServerFoo
  { name :: Text
  } deriving (Eq, Show)

lookupFoo :: Text -> IO (Maybe ServerFoo)

Now, I want to implement a Handler for Query. To start, I implemented a Handler for Foo:

viewFoo :: ServerFoo -> Handler IO Foo
viewFoo ServerFoo { name } = pure $ pure name

Then I went to implement the root handler, but I realized I am not sure how to use my viewFoo function when I end up with with a Maybe ServerFoo. I tried this:

handler :: Handler IO Query
handler = pure $ \fooId -> do
  foo <- lookupFoo fooId
  sequence $ fmap viewFoo foo

However, this does not work. It produces the following type error:

• Couldn't match type ‘IO Text’
                 with ‘Object "Foo" '[] '[Field "name" Text]’
  Expected type: ServerFoo
                 -> IO (Object "Foo" '[] '[Field "name" Text])
    Actual type: ServerFoo -> Handler IO Foo
• In the first argument of ‘fmap’, namely ‘viewFoo’
  In the second argument of ‘($)’, namely ‘fmap viewFoo foo’
  In a stmt of a 'do' block: (sequence $ fmap viewFoo foo)

This seems a bit odd to me, given that the examples I’ve read would seem to imply that Handlers are designed to be compositional. Indeed, they appear to be, since making a few tweaks to remove the Maybe makes this typecheck:

type Query = Object "Query" '[]
  '[ Argument "id" Text :> Field "test" Foo ]

handler :: Handler IO Query
handler = pure $ \fooId -> do
  Just foo <- lookupFoo fooId
  viewFoo foo

Obviously, however, this involves a partial pattern match, so it’s not an okay solution.

My conclusion is that either Handler IO is not a functor or Handler m (Maybe a) is stranger than I’m anticipating. Whatever the reason, this seems like a fairly straightforward use case, so I would imagine there has to be some solution.

@jml
Copy link
Collaborator

jml commented Apr 13, 2017 via email

@lexi-lambda
Copy link
Author

No worries, I certainly understand. I think this project is really exciting, for what it’s worth, and it seems to work pretty well considering how young it is.

@teh
Copy link
Collaborator

teh commented Apr 18, 2017

Thanks for the detailed report!

looking at the expansion:

λ  :kind! Handler IO Query
Handler IO Query :: *
= IO (Text -> IO (Maybe (Object "Foo" '[] '[Field "name" Text])))

The Object "Foo" '[] '[Field "name" Text] part should be reduced to another type but it isn't. I.e. this is a bug.

@lexi-lambda
Copy link
Author

Yes, I looked through the source about a week ago and found this line in GraphQL.Resolver:

instance forall m hg. (HasResolver m hg, Functor m, ToValue (Maybe hg)) => HasResolver m (Maybe hg) where
  type Handler m (Maybe hg) = m (Maybe hg)
  resolve handler _ =  map (ok . toValue) handler

I thought it was weird that the HasResolver m hg instance doesn’t seem to be used at all. The associated type is just m (Maybe hg) instead of m (Maybe (Handler m hg)), as I would have expected. However, I’m not familiar enough with the library to know what the right thing to do is.

@teh
Copy link
Collaborator

teh commented Apr 18, 2017

Related: #102

I'm checking but might take a while because I haven't touched this part of the code in a while :)

teh added a commit that referenced this issue Apr 18, 2017
I'm not happy with this solution because of the extra monad level
introduced.

    pure (Just (pure value))

is not a good way to return a Maybe. Not sure how to fix better ATM.
@jml jml added the question label Oct 13, 2017
teh added a commit that referenced this issue Oct 15, 2017
Correctly propagate Maybe handlers. Closes #102 and #119.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants