Sándor Rakonczai
Sándor Rakonczai

Reputation: 213

Not deducible relationship

Why the relationship between a and b is not deducible?

class Vector a where
    (<.>) :: Num b => a -> a -> b

data Vec2 a
    = Vec2 (a, a)

    deriving Show

instance Num a => Vector (Vec2 a) where
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d

I would like to have a Vec2 algebric data structure where components can be any numbers.

Upvotes: 1

Views: 67

Answers (2)

chi
chi

Reputation: 116139

(<.>) :: Num b => a -> a -> b

The above means that (<.>) is able to produce any type of number the caller of that function might want.

For instance, if x,y are Vec2 Double, then x <.> y can be called to return Integer. The compiler then complains that its implementation in the posted instance is not general enough, because it returns Doubles instead of any type the caller might choose.

I think this is not what that code was intended to model.

You might want to switch to a multiparameter class (you'll need to enable a few extensions, GHC will tell you which ones):

class Vector a b where
    (<.>) :: a -> a -> b

instance Num a => Vector (Vec2 a) a where
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d

Since now the compiler can not determine the type of x <.> y from the type of x,y, you might want to add a functional dependency using

class Vector a b | a -> b where
    (<.>) :: a -> a -> b

or alternatively to use a type family

class Vector a where
    type Scalar a
    (<.>) :: a -> a -> Scalar a

instance Num a => Vector (Vec2 a) where
    type Scalar (Vec a) = a
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d

Upvotes: 7

&#216;rjan Johansen
&#216;rjan Johansen

Reputation: 18189

As @chi's answer said, your code doesn't make b depend on a. To get what I think you want, you can use an associated type family:

{-# LANGUAGE TypeFamilies #-}

class Vector a where
    type Element a
    (<.>) :: a -> a -> Element a

data Vec2 a
    = Vec2 (a, a)

    deriving Show

instance Num a => Vector (Vec2 a) where
    type Element (Vec2 a) = a
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d

Another option is to use a multiparameter typeclass with a functional dependency, but I think that's more complicated here.

Upvotes: 2

Related Questions