tags:

views:

128

answers:

2

I need some help in figuring a compiler error which is really driving me nuts...

I have the following type class:

infixl 7 -->
class Selectable a s b where
  type Res a s b :: *
  (-->) :: (CNum n) => (Reference s a) -> (n,(a->b),(a->b->a)) -> Res a s b

which I instance twice. First time goes like a charm:

instance Selectable a s b where
  type Res a s b = Reference s b
  (-->) (Reference get set) (_,read,write) = 
        (Reference (\s -> 
                      let (v,s') = get s
                      in (read v,s'))
                   (\s -> \x ->
                      let (v,s') = get s
                          v' = write v x
                          (_,s'') = set s' v'
                      in (x,s'')))   

since the type checker infers

(-->) :: Reference s a -> (n,a->b,a->b->a) -> Reference s b

and this signature matches with the class signature for (-->) since

Res a s b = Reference s b

Now I add a second instance and everything breaks:

instance (Recursive a, Rec a ~ reca) => Selectable a s (Method reca b c) where
  type Res a s (Method reca b c) = b -> Reference s c
  (-->) (Reference get set) (_,read,write) =
    \(x :: b) -> 
        from_constant( Constant(\(s :: s)->
                          let (v,s') = get s :: (a,s)
                              m = read v
                              ry = m x :: Reference (reca) c
                              (y,v') = getter ry (cons v) :: (c,reca)
                              v'' = elim v'
                              (_,s'') = set s' v''
                              in (y,s''))) :: Reference s c

the compiler complains that

Couldn't match expected type `Res a s (Method reca b c)'
       against inferred type `b -> Reference s c'
The lambda expression `\ (x :: b) -> ...' has one argument,
which does not match its type
In the expression:
    \ (x :: b)
        -> from_constant (Constant (\ (s :: s) -> let ... in ...)) ::
             Reference s c
In the definition of `-->':
    --> (Reference get set) (_, read, write)
          = \ (x :: b)
                -> from_constant (Constant (\ (s :: s) -> ...)) :: Reference s c

reading carefully the compiler is telling me that it has inferred the type of (-->) thusly:

(-->) :: Reference s a -> (n,a->(Method reca b c),a->(Method reca b c)->a) -> (b -> Reference s c)

which is correct since

Res a s (Method reca b c) = b -> Reference s c

but why can't it match the two definitions?

Sorry for not offering a more succint and standalone example, but in this case I cannot figure how to do it...

+4  A: 

When you write

instance Selectable a s b where

you are saying that any combination of types is an instance of Selectable. This leaves no room for other instances.

Sure, certain shady compiler extensions will allow you to write more (necessarily conflicting) instances, but you are bound to run into trouble.

Can you make your first instance more specific so it no longer conflicts with the other instance you are trying to write?

Running into problems like these is usually a sign that type classes are not the answer. If you're writing only two instances, why not give up overloading and just write two specific functions—one for each use case?

Martijn
Well, I do have two functions now but since they have very similar semantics overloading feels like the right way...I need to specify something like "if no specific match to the second instance occurs then go with the first", but I am starting to fear that this may not be doable :(
Giuseppe Maggiore
Giuseppe, could the ideas described in [this page on the wiki](http://www.haskell.org/haskellwiki/GHC/AdvancedOverlap) work for you? It seems to describe your situation quite well!
yatima2975
would it be possible for him to just put the less generic definition first, and then the other acts as a 'fallback' case?
jakebman
I have to agree with Martijn that the two `(-->)` functions don't seem similar at all and shouldn't have the same name--I understand what the first one is, but haven't the foggiest idea about the second one!
Reid Barton
A: 

Like I said elsewhere, I have no idea what's going on in the second instance given so little context. However, maybe you can make your instances nonoverlapping by doing this instead:

class Selectable a s b r where
    (-->) :: (CNum n) => (Reference s a) -> (n,(a->b),(a->b->a)) -> r

instance Selectable a s b (Reference s b) where ...
instance (Recursive a, Rec a ~ reca) => Selectable a s (Method reca b c) (b -> Reference s c) where ...

This is likely to cause you problems at the call sites instead, though, and it's probably much better to use two functions with different names.

Reid Barton