Ambiguous type of functional dependency

In the haskell functonal dependency wiki :

Given these definitions:

data Vector = Vector Int Int deriving (Eq, Show)
data Matrix = Matrix Vector Vector deriving (Eq, Show)
instance Num Vector where
  Vector a1 b1 + Vector a2 b2 = Vector (a1+a2) (b1+b2)
  Vector a1 b1 - Vector a2 b2 = Vector (a1-a2) (b1-b2)
  {- ... and so on ... -}

instance Num Matrix where
  Matrix a1 b1 + Matrix a2 b2 = Matrix (a1+a2) (b1+b2)
  Matrix a1 b1 - Matrix a2 b2 = Matrix (a1-a2) (b1-b2)
  {- ... and so on ... -}
class Mult a b c where
  (*) :: a -> b -> c

instance Mult Matrix Matrix Matrix where
  {- ... -}

instance Mult Matrix Vector Vector where
  {- ... -}

      

I can't figure out why there is an ambiguous type for:

m1, m2, m3 :: Matrix
(m1 * m2) * m3              -- type error; type of (m1*m2) is ambiguous

      

Obviously, when m1 and m2 are matrices, the only possible return type is matrix, that is, application instance Mult Matrix Matrix Matrix

.

+3


source to share


2 answers


The problem is in the class declaration like

class Mult a b c where
  (*) :: a -> b -> c

      

Applying (*)

on two arguments, you have no way to determine the type of the result. Let's say you have two instances:

instance Mult Int Int Int where ...
instance Mult Int Int Integer where ...

      

Then 2 * 4

can have a type Int

as well Integer

.



Now you can argue that you only have one instance, so the compiler shouldn't complain. But classes like Haskell live in the open world. You can always add more instances and it shouldn't interrupt your code elsewhere. So even if you only had one instance, someone from the other library could add another one. And you will have two libraries, each working, but not working together. It would be perfectly obvious. See Open World Living in the Real World of Haskell.

Thus, as a general rule, the return types of a function in a type class should be inferred from its arguments. This is exactly what functional dependencies are for. If you declare

class Mult a b c | a b -> c where

      

then the compiler can always tell what the return type is (*)

.

+6


source


Because you forgot all other possibilities, for example

 instance Mult Matrix Matrix Vector where
 instance Mult Vector Matrix Vector where
 instance Mult Float Matrix Float where
 instance Mult Matrix Matrix Float where  -- etc.

 a :: Vector
 a = (m1 >< m2:: Vector) >< m3

 b :: Float
 b = (m1 >< m2:: Float) >< m3

      

Adding functional dependency to class definition:

instance Mult a b c | a b -> c

      

would mean that

instance Mult Matrix Matrix Matrix

      

solves the question for two matrices and that



instance Mult Matrix Matrix Vector
instance Mult Matrix Matrix Float

      

are excluded because they give a different way of looking at the "product of a Matrix

and a Matrix

". Thus, the state of affairs you find intuitive is the one you get with functional dependency.

If the functional dependence was formulated as follows:

 instance Mult a b c | b -> a c

      

this will also resolve two instances that you specify,

 instance Mult Matrix Matrix Matrix where
 instance Mult Matrix Vector Vector where

      

but exclude others that I imagined, and they all have Matrix

in position b

(as necessary, since in an ambiguous example the product of matrices m3

was explained as Matrix

and not as a result.

+1


source







All Articles