views:

83

answers:

1

Hi. Hopefully some of you can help me clear up this confusion I have. I'm sorry for the bad title - if anyone has suggestions I'll change it.

Let's set up some context first. I've reduced everything down to the essentials, so bear with me if the example code below is somewhat contrived. Let's say we have

class Foo a where
    foo :: a

data Type a = Type a

instance (Foo a) => Foo (Type a) where
    foo = Type foo

Now, suppose I want to make Type a an instance of, say, Show whenever a is an instance of both Foo and Show (Show was chosen to avoid defining another typeclass). So how do we want Type a to be an instance of Show? Well, unless we're crazy, we'd of course want it to be something like

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = show x

or maybe

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = "Blabla " ++ (show x)

That's all great and works fine. But say that for some inexplicable reason, we'd like show to output whatever foo :: a looks/shows like! In our contrived setting I cannot imagine why we'd want that, but let's say we do. Shouldn't

instance (Foo a, Show a) => Show (Type a) where
    show _ = show foo

do the trick?

Alas, GHC says

Ambiguous type variable 'a' in the constraints: 'Foo a' [...] 'Show a'

Hmm, I thought, so maybe GHC can't figure out which foo I'm talking about. Do I mean foo :: Type a or foo :: a? Changing the previous snippet to

instance (Foo a, Show a) => Show (Type a) where
    show _ = show (foo :: a)

gives me

Could not deduce (Foo a1) from the context () arising from a use of 'foo' at [...] Possible fix: add (Foo a1) to the context of an expression type signature In the first argument of 'show', namely '(foo :: a)' In the expression: show (foo :: a)

At this point I'm starting to think I've misunderstood something basic. Yet, I have the strange feeling that similar constructions have worked for me in the past.

+6  A: 

I think the problem is that type variables aren't scoped to definitions. That is, in

instance (Foo a, Show a) => Show (Type a) where
    show _ = show (foo :: a)

a in the second line is different from a in the first line, which is why it's shown as a1 in the error message. See http://www.haskell.org/haskellwiki/Scoped_type_variables. If this is the problem, this should work (I don't have GHC on this machine):

asTypeOf :: a -> a -> a
asTypeOf a b = a

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = show (foo `asTypeOf` x)
Alexey Romanov
Good answer except for a minor typo which leads to non-termination: the last line should be `show (Type x) = show (foo \`asTypeOf\` x)`. You can also use `{-# LANGUAGE ScopedTypeVariables #-}` along with `show _ = show (foo :: a)`
pelotom
Great! Thanks, this really clarified things. Thanks also to pelotom for pointing out the GHC extension.
gspr
The reason why I didn't mention the extension is that it requires that `forall` be written explicitly: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/other-type-extensions.html#scoped-type-variables, but I wasn't sure where it should go: `instance forall a. (Foo a, Show a) => Show (Type a)`? Somewhere else? Unnecessary for instances?
Alexey Romanov
Alexey: I didn't need `forall` at least. In my example, I turned on the GHC extension and implemented `show` as `show _ = show (foo :: a)`. It's working perfectly. It's nice to know about your `asTypeOf` approach too.
gspr
"I didn't need `forall` at least." Good to know!
Alexey Romanov