Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: "...because type variable ‘t’ would escape its scope" using package "servant" in Haskell

Tags:

haskell

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?

like image 590
Sh4pe Avatar asked Jul 25 '26 07:07

Sh4pe


1 Answers

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
like image 180
Rufflewind Avatar answered Jul 28 '26 15:07

Rufflewind



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!