For the sake of simplicity, I'd say you probably want this :
fun listAdd : (int * int) list -> int list
Now, I would simply define this as an abstraction of the unzip function :
fun listAdd ls :
case ls of
[] => 0
| (x,y) :: ls' => (x + y) + (listAdd ls')
I think there is no point in taking two separate lists. Simply take a list that has a product of ints. If you have to build this, you can call the zip function :
fun zip xs ys :
case xs, ys of
[], [] => []
| xs, _ => []
| _, ys => []
| x::xs', y::ys' => (x,y) :: (zip xs' ys')
In general, if you really wanted, you can write a far more abstract function that is of the general type :
fun absProdList : ((`a * `b) -> `c) -> (`a * `b) list -> `c list
This function is simply :
fun absProdList f ls =
case l of
[] => []
| (x,y) :: ls' => (f (x,y)) :: (absProdList f ls')
This function is a supertype of the addList
function you mentioned. Simply define an anonymous function to recreate your addList
as :
fun addList' ls =
absProdList (fn (x,y) => x + y) ls
As you can see, defining the generic type-functions makes specific calls to functions that are type-substitutions to the general one far easier and much more elegant with the appropriate combination of : Currying, Higher-Order Functions and Anonymous Functions.