0
votes

I'm a bit at a loss as to why the last pattern in prodV in the following doesn't work:

 {-# LANGUAGE GADTs #-}

data V a where
  V0 :: Float -> V Float
  Vn :: [Float] -> V [Float]

prodV :: (Num a) => V a -> V a -> a
prodV (V0 x) (V0 y) =  x * y
-- prodV (Vn x) (Vn y) = zipWith (*) x y -- this one works
prodV (Vn xs) (Vn ys) = [ sum $ zipWith (*) xs ys ]
prodV (V0 n) (Vn ys) = map (* n) ys 

GHCi 7.8.3 complains:

Couldn't match type ‘Float’ with ‘[Float]’ 
Inaccessible code in
  a pattern with constructor
    Vn :: [Float] -> V [Float],
  in an equation for ‘prodV’
In the pattern: Vn ys`

Any pointers? thanks in advance

3
For one thing, you expect your function prodV to work for all Num types, but the V0 and Vn constructors specifically contain Float. Secondly, you're saying that the a in V a has to be a Num, but Num a => [a] is not an instance of Num. Looks like you're approaching this problem the wrong way.bheklilr
You could certainly write a function like unV :: V a -> a that simply unwraps the constructors, but the premise of your function is not correct it seems. Maybe you should consider Vn :: [Float] -> V Float, but that would make unV :: V a -> a impossible unless you convert your list of Floats to a single Float in some fashion first.bheklilr
the error remains even if I remove the Num constraint; I don't quite understand why the V0 and Vn constructors can't be 'mixed'..ocramz
@ocramz V0 n has type V Float while Vn ns has type V [Float]. They're not the same types, but you specified that they must be the same, in the type for prodV. Pattern matching on GADT constructors refine the types.András Kovács
Your function needs to use Num functions like * and sum, so you specified in your type signature that you want Num a => V a. But both constructors to V are existential, they can't just hold any type of value. The values contained in V0 and Vn aren't just any Num a => a, they have to have a specific type. You can't construct a value using V0 with anything other than a Float. Similarly, Vn must be constructed with a value of type [Float]. You're trying to tell the compiler that for V0 x and Vn y, x and y have the same type, but by definition they can't.bheklilr

3 Answers

2
votes

The signature

prodV :: (Num a) => V a -> V a -> a

mandates that both arguments have the same type parameter, but if you match on V0 and Vn the arguments would have to have the types V Float and V [Float] in which the type parameter does not match.

I'm not quite sure what you want the semantics to be but I'm guessing that you want to define the GADT as something like

data V n where
  V0 :: n -> V n
  Vn :: [n] -> V n

and your function as either

prodV :: (Num a) => V a -> V a -> V a
prodV (V0 x) (V0 y) =  V0 $ x * y
prodV (Vn xs) (Vn ys) = Vn $ zipWith (*) xs ys
prodV (V0 n) (Vn ys) = Vn $ map (* n) ys

or possibly

prodV' :: (Num a) => V a -> V a -> a
prodV' (V0 x) (V0 y) =  x * y
prodV' (Vn xs) (Vn ys) = sum $ zipWith (*) xs ys
prodV' (V0 n) (Vn ys) = sum $ map (* n) ys

Can you describe what you would like the function prodV to actually do? The types in your original code don't really make sense to me as they are.

2
votes

Well, GHC tells you exactly what the problem is. From your definition, V0 something always has the type V Float (and something should be of type Float — but that's not relevant here), while Vn anything always has the type V [Float]. But the type signature for prodV specifies that both have the same type V a. So, in this case a should be Float and [Float] at the same time, which is impossible.

0
votes

I was just trying to understand how to overload functions in order to mimic algebraic shorthand, but I guess this isn't the right avenue. What would the idiomatic Haskell way to do this? E.g. from Numeric.Matrix:

adaptScalar f1 f2 f3 x y
    | dim x == 1 = f1   (x@>0) y
    | dim y == 1 = f3 x (y@>0)
    | otherwise = f2 x y

instance Num (Vector Float) where
    (+) = adaptScalar addConstant add (flip addConstant)
    negate = scale (-1)
    (*) = adaptScalar scale mul (flip scale)
    signum = vectorMapF Sign
    abs = vectorMapF Abs
    fromInteger = fromList . return . fromInteger

picks the right implementation of the operator (e.g. + or *) according to the dimension of the operands.