Hola,
Esencialmente, lo que va haciendo tip f (que está definida de forma tail-recursiva) es ir recorriendo las listas xs e ys hasta que una (o ambas se agoten) y va acumulando en zs el resultado de aplicar f sobre la cabeza de ys e xs. Eso se expresa como (f (head ys) x : zs). Podriamos haber definido la segunda lista por pattern-matching de la forma (y : ys) y entonces habriamos escrito (f y x : zs).
Sabemos que al llegar a esa ecuación la 2da lista es no vacia porque ya pasamos por la ecuación anterior en que testeamos si la segunda lista es vacía. Por lo tanto, head ys y tail ys están definidas.
Al llegar a que una de las listas es vacía retorna el reverse del acumulador (la lista zs).
Veamos un ejemplo,
tip (+) [1,2,3] [4,5,6,7] [8,9]
= tip (+) [2,3] [5,6,7] [4+1,8,9]
= tip (+) [3] [6,7] [5+2,4+1,8,9]
= tip (+) [] [7] [6+3,5+2,4+1,8,9]
= [9,8,4+1,5+2,6+3]
que equivale a hacer reverse [8,9] ++ zipWith (+) [4,5,6,7] [1,2,3], que es una de las opciones que damos en el ejercicio. Como la función se va calculando en forma tail-recursiva el acumulador va dejando las sucesivas sumas en orden inverso. Al hacer reverse al final (cuando una de las listas se agota) las sumas pasan a estar en el orden en que se hicieron, lo que corresponde a la expresión zipWith (+) [4,5,6,7] [1,2,3]. A esto hay que sumarle que el valor inicial del acumulador (zs) queda adelante y en orden inverso.
Espero que esto haya aclarado la duda.
Saludos,
Alberto.