Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GHC is complaining type mismatch when using typeclass.

Tags:

haskell

I don't see why 'dot' part will not work.

class Vector a where
    add, minus, cross :: a -> a -> a
    dot :: Num b => a -> a -> b

data Vector2D a = Vector2D a a deriving (Read)

instance Num a => Vector (Vector2D a) where
    add (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 + x2) (y1 + y2)
    minus (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 - x2) (y1 - y2)
    dot (Vector2D x1 y1) (Vector2D x2 y2) = x1*x2 + y1*y2

The error message is:

Couldn't match expected type ‘b’ with actual type ‘a’
  ‘a’ is a rigid type variable bound by
      the instance declaration at example.hs:9:10
  ‘b’ is a rigid type variable bound by
      the type signature for
        dot :: Num b => Vector2D a -> Vector2D a -> b
      at example.hs:12:3
Relevant bindings include
  y2 :: a (bound at example.hs:12:37)
  x2 :: a (bound at example.hs:12:34)
  y1 :: a (bound at example.hs:12:20)
  x1 :: a (bound at example.hs:12:17)
  dot :: Vector2D a -> Vector2D a -> b (bound at example.hs:12:3)
In the first argument of ‘(*)’, namely ‘x1’
In the first argument of ‘(+)’, namely ‘x1 * x2’

I look at the type of dot and it is dot :: (Num b, Vector a) => a -> a -> b. How can I make it right?

like image 549
Jazzon Avatar asked Apr 07 '26 21:04

Jazzon


1 Answers

The problem is that the type signature

dot :: Num b => a -> a -> b

means that dot needs to be able to return any numeric type, regardless what the vector type is, while the expression x1*x2 + y1*y2 will only return the type that was put into the vectors.

To fix that you can use a type family to connect a single scalar type to each vector type:

{-# LANGUAGE TypeFamilies #-}

class Vector a where
    type ScalarOf a
    add, minus, cross :: a -> a -> a
    dot :: a -> a -> ScalarOf a

data Vector2D a = Vector2D a a deriving (Read)

instance Num a => Vector (Vector2D a) where
    type ScalarOf (Vector2D a) = a
    add (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 + x2) (y1 + y2)
    minus (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 - x2) (y1 - y2)
    dot (Vector2D x1 y1) (Vector2D x2 y2) = x1*x2 + y1*y2

An alternative method is to use a multiparameter type class, probably with a functional dependency.

I also want to mention that cross products essentially only make sense for 3-dimensional vectors. (I've heard that you can make them work for 7 dimensions, or trivially for 1.)

like image 89
Ørjan Johansen Avatar answered Apr 10 '26 22:04

Ørjan Johansen