On Feb 13, 2006, at 2:26 PM, [EMAIL PROTECTED] wrote:


Hi,

I've posted a couple messages to the Haskell Cafe in the last few months.  I'm new to Haskell.  But, I've set out to implement my own vectors, matrices, complex numbers, etc.

One goal I have, is to overload operators to work with my new types.  The pursuit of this goal, has pushed me to learn a lot about the
Haskell type system. When I get stuck from time-to-time, the kind folks on this list have pointed me in the right direction.

I'm stuck now.  One thing I want to avoid is adding new multiplication operators to handle multiplication of dissimilar types.  For instance, I'd like to be able to have an _expression_ like k * m where k is a Double and m is a Matrix.  This doesn't work with the prelude's (*) operator because the prelude's (*) has signature:

(*) :: (Num a) => a -> a -> a.

To get around this, I wrote my own versions of a Multiply class that allows dissimilar types to be multiplied.  You can see my Multiply class in the module at the end of this Email. 

[snip error message]

I don't understand how m1 * m2 can match the scalar multiplication instances.  For instance, the scalar * matrix instance has signature:

instance (Multiply a b c, Num a, Num b, Num c)
                               => Multiply a (Matrix b) (Matrix c) where

m1 in my _expression_ would correspond to the 'a' type variable.  But, 'a' is constrained to be a Num.  However, I never made my Matrix type an instance of Num.

Is there a work around for this?  In my first implementation, I did not have the Num constraints in the matrix Multiply instances.  I added the Num constraints specifically, to remove the ambiguity of the overlapping instance.  Why didn't this work?

I'm pretty sure this is due to a misfeature of the way class class instance selection works.  Essentially, the typechecker IGNORES the instance context (everything before the =>) when looking for matches, and it only checks the context after it has irrevocably selected an instance.  Thus, rather than backtracking and trying to find another instance, the typechecker just gives you errors about unsatisfied constraints or overlapping instance errors.  Often this isn't what you want (as in this case).

To be fair, doing better than this (in general) seems pretty difficult.  The typechecker sometimes needs to have more information than it can currently gather.  I think the following extension proposal might address this problem (if its ever implemented...)


As of now, the typechecker can't be absolutely certain that 'Matrix' isn't (and will never be) an instance of 'Num'.  Just because you haven't make it a member of 'Num' doesn't mean someone else couldn't!  For it to do what you want, the typechecker needs to be able to prove that, given any legal collection of instances, the instance declarations in question will not overlap.  It can't to that.


As to workarounds... that becomes more difficult.  Essentially you need to replace the bare type variable 'a' in your instance declarations with something that can guide the typechecker to select the 'correct' instance.  Two options come to mind:

1) create a 'newtype' for scalars.  Now you have to wrap and unwrap your scalars, which is a bit of a pain, but it is a fully general solution.  Judicious use of newtype deriving may eliminate some of this pain.

2) Create separate 'Multiply' instances for each type of scalar you want to use.  Eliminates the ugly wrapping/unwrapping, but limits the types of scalars you can use.




Rob Dockins

Speak softly and drive a Sherman tank.
Laugh hard; it's a long way to the bank.
          -- TMBG



_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to