Reputation: 4680
class Foo t where
foo :: t
bar :: Binary t => t -> ()
bar = undefined
repro :: (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
bar (foo :: t)
The compiler complains:
Could not deduce (Binary t0) arising from a use of ‘bar’ from the context: (Binary t, Foo t) bound by the type signature for: repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
Could not deduce (Foo t2) arising from a use of ‘foo’ from the context: (Binary t, Foo t) bound by the type signature for: repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
Specifically, I am surprised it does not see I am passing t
to bar
, and creates a t0
type var. t2
is even more mysterious, because foo
is explicitly annotated with type t
.
Upvotes: 2
Views: 98
Reputation: 152707
For completeness, this can also be handled without extensions. The key trick here is to write a function whose type is more restricted than it needs to be, to connect the Proxy
's type argument with the Foo
instance. So:
-- most general possible type is Foo b => a -> b
fooForProxy :: Foo t => proxy t -> t
fooForProxy _proxy = foo
-- I've changed Proxy to proxy here because that's good practice, but everything
-- works fine with your original signature.
repro :: (Binary t, Foo t) => proxy t -> ()
repro = bar . fooForProxy
Of course, the modern way is to use even more extensions to eliminate proxies entirely:
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
repro :: forall t. (Binary t, Foo t) => ()
repro = bar @t foo
Invoking repro
will again require a type application, as in repro @Int
or whatever.
Upvotes: 2
Reputation: 68640
Type variables, by default, are not scoped that way. The t
in the function signature and the t
in the function body are not the same. Your code is equivalent to this:
repro :: (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
bar (foo :: a)
You need to enable the ScopedTypeVariables extension, and add an explicit forall t
.
{-# LANGUAGE ScopedTypeVariables #-}
repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
bar (foo :: t)
Upvotes: 4
Reputation: 116139
You probably need to turn on the ScopedTypeVariables
extension and then use
repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
repro _proxy = bar (foo :: t)
Otherwise, the t
in foo :: t
is not related to the other t
in the repro
signature. Essentially foo :: t
becomes equivalent to foo :: forall a. a
.
This is arguably one the most disliked features of the Haskell definition, and ScopedTypeVariables
is very popular since it allows to work around that. (In my opinion, it should be on by default.)
Upvotes: 2