Question

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...

Was it helpful?

Solution

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?

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top