The essence of the type error is this line: "The type variable `a0' is ambiguous".

(Disclaimer: I'm trying to avoid jargon in this answer.)

To understand what's going on here, I suggest floating the binding and query bindings to the top-level. They successfully typecheck if you then comment out the bind binding. GHC infers the following types.

*Orexio.Radix> :i query query :: Data a => JSValue -> Result a *Orexio.Radix> :i binding binding :: (Data (Representation a), Endpoint a, Resource a) => Result a -> Either String JSValue

Your error is essentially caused by the expression \x -> binding (query x) in the definition of bind . Notice that the type variable a appears only in the domain of binding and the range of query . Thus, when you compose them, two things happen.

The type variable is not determined; its "value" remains unknown. The type variable is not reachable from the type of the composition. For our informal purposes, that type is JSValue -> Either String JSValue .

GHC raises the error because the type variable was not determined during the composition (ie 1) and it can never be determined in the future (a consequence of 2).

The general shape of this problem in Haskell is more commonly known as the " show - read problem"; search for "ambiguous" on Chapter 6 of Real World Haskell. (Some might also call it "too much polymorphism".)

As you and Sjoerd have determined (and the RWH chapter explains), you can fix this type error by ascribing a type to the result of query before applying binding . Without knowing your semantics, I assume that you intend this "hidden" type variable a to be the same as the argument to the Binding type constructor. So the following would work.

bind :: forall b. (Data b, Resource b, Endpoint b, Data (Representation b)) => Binding b bind = Binding (\x -> binding $ (query x :: Result b))

This ascription eliminates the type variable a by replacing it entirely with Result b . Note that unlike a it is acceptable for b to remain undetermined, since it's reachable in the top-level type; uses of bind may each determine b .

That first solution requires giving bind an explicit signature — which can sometimes be quite onerous. In this case, due to the monomorphism restriction, you probably already need that type signature. However, if bind took an argument (but still exhibited this ambiguous type variable error), you could still rely on type inference by using a solution like the following.

dummy_ResultType :: Binding a -> Result a dummy_ResultType = error "dummy_ResultType" bind () = result where result = Binding (\x -> binding $ (query x `asTypeOf` dummy)) dummy = dummy_ResultType result

(If the use of error worries you, cf the Proxy type.)

Or trade some idiomaticness for directness:

withArgTypeOf :: f x -> g x -> f x withArgTypeOf x _ = x bind () = result where result = Binding (\x -> binding (query x `withArgTypeOf` result))

Now inference works.

*Orexio.Radix> :i bind bind :: (Data (Representation a), Data a, Endpoint a, Resource a) => () -> Binding a

Rest assured that GHC quickly determines after typechecking that the definition is not actually recursive.

HTH.