This problem came up when I was trying to zip through lists of different length and I didn't want them to be cut by the shortest one. It was in a context where the list had integers that I wanted to add or multiply. My problem is how to set up the type variables so that it can be more general.
zipWithPadding :: a -> (a -> a -> a) -> [a] -> [a] -> [a]
zipWithPadding n f = go n f
where
go n f [] a = [f n x | x <- a]
go n f a [] = [f n x | x <- a]
go n f (x:xs) (y:ys) = f x y : go n f xs ys
Currently, because the element to pad the lists with has to be speficied, I have to write them this way, but it begs the question of whether it needs to be custom made for each context, or simply repurposed with higher order functions.
Edit: I would like to thank Chris in the comments for the help. It made the example at the bottom possible by changing the above solution into this:
zipPadWith :: a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]
zipPadWith n m f = go n m f
where
go n _ f [] a = [f n x | x <- a]
go _ m f a [] = [f x m | x <- a]
go n m f (x:xs) (y:ys) = f x y : go n m f xs ys
The example being:
transposeWith :: a -> [[a]] -> [[a]]
transposeWith n [] = []
transposeWith n (ms:mss) = zipPadWith n [] (:) ms (transposeWith n mss)
It transposes the list of lists without cutting content.
CodePudding user response:
If we implement zipPadWith
in the following manner, the application of the default value as both arguments to f
constrains the signature to a -> (a -> a -> a) -> [a] -> [a] -> [a]
.
zipPadWith _ _ [] [] = []
zipPadWith n f (x:xs) (y:ys) = f x y : zipPadWith n f xs ys
zipPadWith n f (x:xs) [] = f x n : zipPadWith n f xs []
zipPadWith n f [] (y:ys) = f n y : zipPadWith n f [] ys
However, if the function takes two default arguments, one to use if the left list falls short, and one to use if the right list falls short, the type is not constrained, and can be a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]
.
zipPadWith _ _ _ [] [] = []
zipPadWith n1 n2 f (x:xs) (y:ys) = f x y : zipPadWith n1 n2 f xs ys
zipPadWith n1 n2 f (x:xs) [] = f x n2 : zipPadWith n1 n2 f xs []
zipPadWith n1 n2 f [] (y:ys) = f n1 y : zipPadWith n1 n2 f [] ys