To access the next element, map on tails
instead:
mapWithNext op xs = map g (tails xs)
where
g (x:y:_) = Just (op x y)
g _ = Nothing
since when there's no next element, your op
can't be applied, though it will only happen with the finite lists. You might know that the input lists will be infinite, but your type doesn't, so it's better to be on the safe side.
You might want to add a post-processing step to get rid of the final Nothing
(s) and extract the Just
values. catMaybes :: [Maybe a] -> [a]
seems applicable.
Of course using zipWith
(*) mentioned in the comments is cleaner and more to the point, here; but this way with tails
is more versatile in general, i.e. it is easier to amend to be used with e.g. ternary op
, etc.. With zipWith
we'd have to use a chain of nested zipWith
applications and build artificial tuples just to have access to the elements; with tails
we don't need to do anything like that at all.
A combination of map
(generally, foldr
) with tails
is actually emulating paramorphism. It has its merits so as e.g. in Common Lisp it is actually present as a library function maplist
.
(*) you could say, zipWith
is not map, but e.g. in Scheme it is so. There, map
accepts an arbitrary number of argument lists, and a combining operation of the corresponding arity. But of course Haskell is not Scheme.