Multiparameter declarations of typeclass instances

I'm trying to understand multiparameter types, but I just don't get instance declarations. I'm starting to try to create a class like InnerProductSpace for type Vector so that I can run a point product on two vectors. For starters, I just wanted to know if I can multiply the first element of each vector. Here is my code

class InnerProductSpace abc where dot :: a -> b -> c data Vector = Vector [Double] deriving (Show) instance InnerProductSpace Vector Vector Double where dot (Vector a) (Vector b) = (head a * head b) 

and error after trying to use the point function

 No instance for (InnerProductSpace Vector Vector c0) arising from a use of `dot' The type variable `c0' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there is a potential instance available: instance InnerProductSpace Vector Vector Double -- Defined at Vector.hs:8:10 Possible fix: add an instance declaration for (InnerProductSpace Vector Vector c0) In the expression: dot ab In an equation for `it': it = dot ab 

What I did wrong? Thanks!

+6
source share
2 answers

The problem is that the compiler does not know how to select the correct instance, given what it knows. If you try something like

 vectorA `dot` vectorA 

the compiler is looking for the correct dot , knowing that its type must be dot :: Vector -> Vector -> c0 . Unfortunately, this is simply not enough information in itself - c0 can be anything, and the compiler never assumes that just because it has only one instance, it must be correct (this is due to the assumption of an open world --- there may be another instance that the compiler has not yet seen, so he prefers to simply throw an error). You can get away from it by explicitly telling the compiler what the result should be

 vectorA `dot` vectorB :: Double 

but this is tedious and most likely fails badly with numeric types, since often these types are also common. For example, which Num instance is used here?

 (vectorA `dot` vectorB) + 3 

We know this Double , but the compiler cannot prove it.

One solution is to use type families, as @AndrewC suggests in a comment. I really highly recommend this. Another solution that you will see in the wild is functional dependencies. They are written like this:

 class InnerProductSpace abc | ab -> c where dot :: a -> b -> c 

and translate to promise the compiler: "Know a and b enough information to uniquely identify c ." The compiler keeps you informed, and this will not allow you to write instances that contradict this promise.

 instance InnerProductSpace Vector Vector Double where dot (Vector a) (Vector b) = (head a * head b) instance InnerProductSpace Vector Vector Float where dot (Vector a) (Vector b) = (head a * head b) --- /Users/tel/tmp/foo.hs:10:10: Functional dependencies conflict between instance declarations: instance InnerProductSpace Vector Vector Double -- Defined at /Users/tel/tmp/foo.hs:10:10 instance InnerProductSpace Vector Vector Float -- Defined at /Users/tel/tmp/foo.hs:13:10 

But the promise gives the compiler enough information to resolve Double in the previous example.

 Main*> (Vector [1,2,3]) `dot` (Vector [2,3,4]) + 3.0 5 
+7
source

or use TypeFamilies

 class (D ab ~ c) => InnerProductSpace abc where type D ab dot :: a -> b -> c 

or

 class InnerProductSpace ab where type D ab :: * dot :: a -> b -> D ab instance InnerProductSpace Vector Vector where type D Vector Vector = Double dot (Vector a) (Vector b) = (head a * head b) 
+3
source

Source: https://habr.com/ru/post/951172/


All Articles