Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing zipWith for type-safe vectors

I am trying to write a vector library that has type-safe lengths, meaning adding two vectors of different lengths will not fly.

My current implementation is roughly:

data Natural where
  Zero :: Natural
  Succ :: Natural -> Natural

type One   = Succ Zero
type Two   = Succ One
type Three = Succ Two
type Four  = Succ Three

data Vector n e where
  Nil  :: Vector Zero e
  (:|) :: e -> Vector n e -> Vector (Succ n) e

infixr :|

I am now trying to implement zipWith because it will help with implementing things like dot product and the Num typeclass.

I have gotten this far

zipWith :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c
zipWith f (a :| as) (b :| bs) = f a b :| zipWith f as bs
zipWith _ _ _ = Nil

but I receive the error

Couldn't match type `n' with 'Zero
      `n' is a rigid type variable bound by
          the type signature for
            zipWith :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c
          at LinearAlgebra.hs:51:12
    Expected type: Vector n c
      Actual type: Vector 'Zero c
    In the expression: Nil
    In an equation for `zipWith': zipWith _ _ _ = Nil

I believe the reason is that the variable n can only take on one value - either Succ or Zero?

How can I remedy this?

like image 558
sdasdadas Avatar asked May 03 '26 01:05

sdasdadas


1 Answers

Take the following pattern:

zipWith _ as bs = Nil

Here GHC only knows that the type of as is Vector n a and the type of bs is Vector n b, from the signature of the function. Also, the signature says that the right hand side must have type Vector n c, where n is the same as the other n-s in the left hand side. Nil has type Vector Zero x, for an arbitrary x.

The types don't match because the length parameter on the left hand side is an unknown arbitrary n, while the the right hand side has length Zero. Pattern matching on the arguments brings in the necessary additional information:

zipWith _ Nil Nil = Nil 
-- "zipWith _ as Nil" also works, because now "as" is constrained to also have Zero length

If you have GHC 7.8.x, you can write the following:

zipWith f as bs = _

and GHC will provide you highly useful messages about the expected type of the hole and the the types of the variables in scope (to GHC's best knowledge, given the constructors you've pattern matched on).

like image 112
András Kovács Avatar answered May 05 '26 15:05

András Kovács



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!