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.