I'm building code for querying a REST API using servant. For some of the endpoints, I want the return value to be dependent on some type t. When I'm trying to combine multiple such APIs using :<|>, I get a compile error.
I've tried to build a boiled-down example of the problem. Sadly, the shortest version is still not very short. Here is my code:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module Sana.ArangoDB.RTest where
import Servant.API
import Servant.Client
import Data.Aeson.Types (typeMismatch)
import Data.Proxy (Proxy(..))
import Data.Yaml (FromJSON(..), (.:))
import qualified Data.Yaml as Y
data SomeStructure t = SomeStructure { _a :: Bool , _b :: t }
instance FromJSON t => FromJSON (SomeStructure t) where
parseJSON (Y.Object v) = SomeStructure <$> v .: "a" <*> v .: "b"
parseJSON i = typeMismatch "SomeStructure" i
type MyAPI t = "api" :> Header "SomeHeader" String :> Get '[JSON] (SomeStructure t)
type MyOtherAPI t = "api2" :> Header "SomeHeader" String :> Get '[JSON] (SomeStructure t)
type TheAPI t = MyAPI t :<|> MyOtherAPI t
theAPI :: Proxy (TheAPI t)
theAPI = Proxy
queryFunction1 :: (FromJSON t ) => Maybe String -> ClientM (SomeStructure t)
queryFunction2 :: (FromJSON t ) => Maybe String -> ClientM (SomeStructure t)
queryFunction1 :<|> queryFunction2 = client theAPI
This does not compile, however. The error I'm getting is this:
• Couldn't match type ‘t0’ with ‘t’
because type variable ‘t’ would escape its scope
This (rigid, skolem) type variable is bound by
the inferred type for ‘queryFunction1’:
FromJSON t => Maybe String -> ClientM (SomeStructure t)
at /Users/david/devel/sana/src/Sana/ArangoDB/RTest.hs:31:1-50
Expected type: Maybe String -> ClientM (SomeStructure t)
Actual type: Maybe [Char] -> ClientM (SomeStructure t0)
Does anybody have any advice how I could avoid this error and make my code work?
I wasn't able to reproduce the errors you saw using GHC 8.2.2. Instead I got something along the lines of “Overloaded signature conflicts with monomorphism restriction”.
I reproduced error messages you saw using an online GHC 7.10 compiler, so it seems GHC's errors have improved. In both GHC 8.2 and 7.10, the error goes away if you enable NoMonomorphismRestriction, so that fixes the problem.
My best guess is that when monomorphism restriction is enabled, the type of queryFunction1 :<|> queryFunction2 is assumed to be fully monomorphic (no type parameters at all), because you did not declare its type signature (not that you could in this case!). This conflicts with your type signatures for queryFunction1 and queryFunction2, which both contain type parameters.
Here is a minimal test that reproduces the exact issue:
h :: Show z => (Maybe z, Maybe z)
h = (Nothing, Nothing)
f :: Show x => Maybe x
g :: Show y => Maybe y
(f, g) = h
To reproduce this kind of error, you need some kind of constraint (e.g. Show) and some kind of type constructor around the variable (e.g. Maybe).
The errors you get in GHC 7.10 are:
source_file.hs:6:1:
Couldn't match type ‘z0’ with ‘x’
because type variable ‘x’ would escape its scope
This (rigid, skolem) type variable is bound by
the inferred type for ‘f’: Show x => Maybe x
at source_file.hs:6:1-10
Expected type: forall x. Show x => Maybe x
Actual type: Maybe z0
When checking that ‘f’ has the specified type
f :: forall x. Show x => Maybe x
Probable cause: the inferred type is ambiguous
source_file.hs:6:1:
Couldn't match type ‘z0’ with ‘y’
because type variable ‘y’ would escape its scope
This (rigid, skolem) type variable is bound by
the inferred type for ‘g’: Show y => Maybe y
at source_file.hs:6:1-10
Expected type: forall y. Show y => Maybe y
Actual type: Maybe z0
When checking that ‘g’ has the specified type
g :: forall y. Show y => Maybe y
Probable cause: the inferred type is ambiguous
source_file.hs:6:10:
No instance for (Show z0) arising from a use of ‘h’
The type variable ‘z0’ is ambiguous
Relevant bindings include
f :: Maybe z0
(bound at source_file.hs:6:2)
g :: Maybe z0
(bound at source_file.hs:6:5)
Note: there are several potential instances:
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
...plus 22 others
In the expression: h
In a pattern binding: (f, g) = h
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With