简体   繁体   中英

How to use predefined function (e.g. ==, tail) on my custom data type in Haskell?

I'm new to haskell and nothing I've read online really helped me with this. My assignment is to implement a typeclass VEC that has a function factor which either calculates the multiplication of two Integers or performs the dot product of two matrices if the user input wasn't two numbers but instead two lists of integers of type vector. My code looks as follows:

data Vector = VECTOR [Int] deriving (Show, Eq)
class VEC a where
    factor :: a -> a -> a
instance VEC Int where
    factor a b = a*b
instance VEC Vector where
    factor xs [] = 0
    factor xs ys = (head xs) * (head ys) + factor (tail xs) (tail ys)

I assumed the Vectors were of type [Int] but now I'm not so sure, since I get the following error message when trying to compile the code using Hugs:

Hugs> :l kdp  
ERROR file:kdp.hs:7 - Type error in instance member binding  
*** Term           : []  
*** Type           : [a]  
*** Does not match : Vector

So my questions would be: What does the first line actually mean? It was given along with the assignment and I've seen many similar definitions of data types but none with this particular pattern. And then how do I fix the problem of not being able to use predefined functions such as tail or in the above error case comparing a list of Integers with my custom data type? My guess is I have to define the operations on my own but I couldn't figure out how to do that.

When you write an instance for a class like

class C a where
  method :: a -> a

you replace all of the parameter appearances ( a ) with the type you are writing instance for. For example in your case:

{-# LANGUAGE InstanceSigs #-}
instance VEC Vector where
    factor :: Vector -> Vector -> Vector
    factor _ _ = undefined

So you cannot match arguments of type Vector with pattern [] nor use head nor tail functions on it as they are working on lists. However, your Vector consists of lists, so you can simply unpack it:

instance VEC Vector where
    factor _ (Vector []) = Vector [0] -- you need to return Vector as well
    factor (Vector xs) (Vector ys) =
      let Vector [x] = factor (Vector $ tail xs) (Vector $ tail ys)
      in Vector [(head xs) * (head ys) + x]

This is very ugly and partial tho, you can use some builtin Data.List machinery to make it more sexy:

instance VEC Vector where
  factor (Vector xs) (Vector ys) =
    Vector [sum (zipWith (*) xs ys)]

As you are using deriving (Show, Eq) , the == operator should just work.

You can generalize your type class to take two type variables to accommodate the two different operations of integer multiplication and vector dot products. This requires two GHC-specific extensions, though, precluding your use of Hugs.

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

-- a -> b means that the second type is uniquely determined by the
-- first. Without it, you would need explicitly type an expression
-- involving factor.
class VEC a b | a -> b  where
    factor :: a -> a -> b

instance VEC Int Int where
    -- a ~ Int, b ~ Int
    factor x y = x * y

-- newtype instead of data, for efficiency if nothing else
newtype Vector = VECTOR [Int] deriving (Show, Eq)

instance VEC Vector Int where
    -- a ~ Vector, b ~ Int
    factor (VECTOR xs) (VECTOR ys) = sum $ zipWith (*) xs ys


main = do
  print $ factor (3 :: Int) 3
  print $ factor (VECTOR [1,2,3]) (VECTOR [4,5,6])

Without the functional dependency, you would have to write

main = do
    print (factor (3 :: Int) 3 :: Int)
    print (factor (VECTOR [1,2,3]) (VECTOR [4,5,6]) :: Int)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM