Question

We can define function f and g like this:

f :: [a] -> [a] -> [a]
f = (++)

g :: [a] -> [a] -> [a]
g = zipWith (+)

Both f and g take two lists as parameters and return a new list, but they are different: f returns a longer list whose length is the sum of the inputs', meanwhile g processes lists with the same length. How to figure this out to Haskell?

Was it helpful?

Solution

What you want is to encode the length of lists into the type system. In other words, encode natural numbers in the type system and operations on them. This is possible, although it involves some type trickery. There are libraries to achieve this, one of them tagged-list. TaggedList is tagged with its length as a type-level natural number. Then the types of your functions would look like

import Data.List.Tagged as T
import TypeLevel.NaturalNumber.Operations (Plus)

f :: TaggedList n a -> TaggedList m a -> TaggedList (Plus n m) a
f = T.append

g :: (Num a) => TaggedList n a -> TaggedList n a -> TaggedList n a
g x = T.zipf (T.map (+) x) -- apparently the Tagged library lacks zipWith
                           -- so we implement it ourselves

which gives clear distinction of what happens with the length of the lists.

See also Type arithmetic.

OTHER TIPS

From what I gather, you want your g to check that the lists are the same length first before performing the operation. That's relatively easy:

-- Correct type signature as @chris mentioned
g :: Num a => [a] -> [a] -> [a]
g xs ys = if length xs == length ys then zipWith (+) xs ys else error "Incompatible lists"

However, this is a really bad way to throw errors, you'd be better off using the Maybe monad:

g :: Num a => [a] -> [a] -> Maybe [a]
g xs ys = if length xs == length ys then Just $ zipWith (+) xs ys else Nothing
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top