The functions in those examples have fixed numbers of arguments,
so one would use the original formulation shown by Tyr42.
> (+) <$> ZipList [1,2,3] <*> ZipList [4,5,6]
ZipList {getZipList = [5,7,9]}
> (+3) <$> ZipList [1,2,3]
ZipList {getZipList = [4,5,6]}
> let max3 x y z = max x (max y z)
> max3 <$> ZipList [1,2] <*> ZipList [3,4] <*> ZipList [5,6]
ZipList {getZipList = [5,6]}
If you want to use "functions unknown at runtime that could take
any number of arguments" then you'll have to pass the arguments
in a list. Of course these can crash at runtime, which
Haskellers wouldn't be happy with given an alternative, but
hey-ho, let's see where we get.
> let unsafePlus [x, y] = x + y
> fmap unsafePlus (sequenceA [ZipList [1,2,3], ZipList [4,5,6]])
ZipList {getZipList = [5,7,9]}
> let unsafePlus3 [x] = x + 3
> fmap unsafePlus3 (sequenceA [ZipList [1,2,3]])
ZipList {getZipList = [4,5,6]}
> unsafeMax3 [x, y, z] = x `max` y `max` z
> fmap unsafeMax3 (sequenceA [ZipList [1,2], ZipList [3,4], ZipList [5,6]])
ZipList {getZipList = [5,6]}
So the answer to your question is that
cl_map :: ([a] -> b) -> [ZipList a] -> ZipList b
cl_map f = fmap f . sequenceA
except you don't actually want all the elements of the list to be
of the same type, you want them to be of dynamic type, so let's
just make them Dynamic.
> let unwrap x = fromDyn x (error "Type error")
>
> let unsafeGreeting [name, authorized] =
> if unwrap authorized then "Welcome, " ++ unwrap name
> else "UNAUTHORIZED!"
>
> fmap unsafeGreeting (sequenceA [ZipList [toDyn "tome", toDyn "simiones", toDyn "pg"]
> , ZipList [toDyn True, toDyn True, toDyn False]])
ZipList {getZipList = ["Welcome, tome","Welcome, simiones","UNAUTHORIZED!"]}
and the type of cl_map becomes
cl_map :: ([Dynamic] -> b) -> [ZipList Dynamic] -> ZipList b
cl_map f = fmap f . sequenceA
One could polish this up a bit and make a coherent ecosystem out
of it, but Haskell programmers hardly ever use Dynamic. We just
don't come across the situations where Clojurists seem to think
it's necessary.