I think skuro gives a good answer of what goes wrong and why, and also suggests a very useful library which you can use to solve your original goal. "So why am I reading this?" I hear you ask. I just thought it would fun to share how you could generalize applying functions to the cartesian products of things with only a few lines of code--without tresspassing into scary macro-land.
When I say generalize, I mean that the goal would be to be able to do something like:
user> (permute str ["abc" "ab"])
=> ("aa" "ab" "ba" "bb" "ca" "cb")
The nice thing, if we develop this so-far-non-existent permute function, is that we could use the same function to do something like:
user> (permute + [[1 2 3] [10 20 30]])
=> (11 21 31 12 22 32 13 23 33)
This is a toy example, but hopefully conveys the flexibility you would gain from generalizing this way.
Well, here's a very concise way I came up with:
(defn permute [f [coll & remaining]]
(if (nil? remaining)
(map f coll)
(mapcat #(permute (partial f %) remaining) coll)))
The core idea I started with was iterating with map or mapcat for as many iterations as there are different strings to combine. I started with your example, and wrote a "verbose" non-general solution:
user> (mapcat (fn [i] (map (partial str i) "ab")) "abc")
=> ("aa" "ab" "ba" "bb" "ca" "cb")
This mapcats a function down "abc". Specifically, it mapcats a function down "abc" that maps string-ing together the single element it is using from "abc" at this time (i), with each element from "ab".
In order for me to understand how to generalize this into a function, I had to go one "level deeper", and try it with a third string.
user> (mapcat (fn [i] (mapcat (fn [j] (map (partial str i j) "def")) "ab")) "abc")
=> ("aad" "aae" "aaf" "abd" "abe" "abf" "bad" "bae" "baf" "bbd" "bbe" "bbf" "cad" "cae" "caf" "cbd" "cbe" "cbf")
This mapcats a function that mapcats a function that maps string-ing together elements in a combinatorial way. Phew. Now I started to see how I could generalize. The innermost expresion would always be maping some sort of partial str function down the final string in the list of strings to re-combine. The outer expressions are just mapcats with the successively more front-ward strings, to the point where the outermost is using the first string in the list of strings to re-combine.
I guess from here I noticed that I needn't define the entire partial str function at once, but rather I could "build it up" as I recursively called permute.
Hopefully I've now given enough context to explain how the function works. The last iteration of permute happens when there are no remaining colls (i.e. (nil? remaining) returns true). It simply maps whatever function it is given down the last coll.
When there are remaining colls, it mapcats a permute variant down the current coll. This permute variant is using a partial function of f with the anonymous argument, and is permuteing down the remaining colls. By doing this, it will incrementally build up a partial function which will ultimately be invoked once it reaches the end of the list of colls. Then, in my head, I imagine it tracing backwards, calling nested mapcats until it finally unravels with the resultant re-combined colls.
I imagine this function, although concise, is probably not optimal. Frankly, I don't have much of a CS background, but from what I've picked up reading about Clojure, using loop/recur instead of self-recursive calls tends to be much more "efficient". I imagine it would be fairly straightforward to re-work the function to use loop/recur if optimization were important to you.