Let's assume that slist-iter does what you explained as list-iter behaviour, which is "'used' when you'd like to see something done with each element of the list"...
Now, I'm going to present you another way to do the cartesian product using Clojure core functions, so I can point you the key element that could be confusing you...
(defn slist-cartesian [lst]
(doseq [x lst
y lst]
(println x y)))
That is what doseq does. You can read it something like for each x in lst, and for each y in lst, print x and y.
Now, the key part here is that you are repeating lst both for x and y. That way we get the Cartesian product of lst with lst.
That is exactly what the code you show is doing. It is using lst as the list to be processed by both of the slist-iter calls.
Understand that it is needed to process the list each time from the beginning, to get what you are asking: The Cartesian product.
And now understand that you can do it both with the doseq expression and within the slist-iter functions.
Probably you need to understand why you can do it in the slist-iter form.
It is possible because fn creates a function and also that function is a Closure "which means that 'it' can access all the variables that were in scope when the function was created".
That means that that function slist-cartesian is creating with fn when calling the first slist-iter, has access to the parameter lst. Let's refer to that function as the anonymous one.
That anonymous function will be called again each time the first slist-iter begins to process a new element of lst, but lst is going to be the same slist-cartesian received and used when the later created the former.
I hope this answered your question.
If you still want to think and dig more in the subject, there is another way to think about this is by constructing the function one step at a time.
So in your own words ...
(defn slist-cartesian [lst]
(slist-iter
lst ;; The list with the elements
,,, ;; The thing to be done to the elements to the list
))
The thing to be done is a function that receive 1 parameter. That function will be called passing as an argument an element of the list, one element at a time for all the elements in the list.
If we make println the function, it will call it many times, one per element of the list, with such element as argument. Which means that it would print the whole list, one item at a time:
(defn slist-cartesian [lst]
(slist-iter
lst
println
))
(slist-cartesian '(1 2 3))
;; 1
;; 2
;; 3
What happens when you need to add more information to what is printed? You need a more complex function than simply println.
Let's create a function that will prefix a text to each item on the list and print them. Let's prefix the text "number: ". Because we have slist-iter we just only need to solve the problem of prefixing the text to a single number, with a function with only one parameter so it can be used by slist-iter and so solving the problem for all elements in the list. Let's make that solution a function:
(defn prefix-text-number-and-print [x]
(println "number: " x))
And now let's use it to build a function that prefix "number: " to all elements of a list and print them. Let's call that function prefix-text-number-to-each:
(defn prefix-text-number-to-each [lst]
(slist-iter
lst
prefix-text-number-and-print))
(prefix-text-number-to-each '(1 2 3))
;; number: 1
;; number: 2
;; number: 3
Cool. But what happen if we need to change the text of the prefix? That means that the function prefix-text-number-and-print needs to be generalized by adding a parameter for such prefix. But that will make that function useless for slist-iter. What then?
What we will do is to create the generalized function, and then somehow derive from it an specialized function we need on the precise moment.
(defn prefix-text-and-print [prefix, x] ;; Generalized function
(println prefix x))
So for the soon to be function prefix-text-to-each, we would do something like:
(defn prefix-text-to-each [prefix, x]
(slist-iter
lst
(somehow-derive-a-function prefix-text-and-print prefix))) ;; ATTENTION
There are many ways to create functions on the spot. You already used one tool for that in the code you show. You are using the function fn to build a function in place. Another way to do it is with the function partial. Let's explore the partial option first.
For that, I will do to prefix-text-and-print the opposite of what I did to prefix-text-number-and-print. I will specialize it. I will define prefix-text-number-and-print in terms of prefix-text-and-print. First by hand, and then by using partial:
;; By hand
(defn prefix-text-number-and-print [x]
(prefix-text-and-print "number: " x))
;; Using partial
(def prefix-text-number-and-print
(partial prefix-text-and-print "number: "))
Both definitions produce the same result: a function. Note the last one uses def instead of defn.
The definition using partial is simple a def. So it is simply defining prefix-text-number-and-print with whatever value is created by the call to partial.
partial creates a function. That new function will simply call the first parameter received by partial (in this case prefix-text-and-print) with the rest of the arguments partial received as arguments (in this case only "number: ") followed by the arguments the function itself receives.
Try it, it doesn't matter what version you used, the result will be the same:
(prefix-text-number-and-print 123)
;; number: 123
You could have done it in place of the call, with the same result
((partial prefix-text-and-print "number: ") 123)
;; number: 123
Now that you now how to use partial, let's use it to build a generalized function prefix-text-to-each
(defn prefix-text-to-each [prefix, lst]
(slist-iter
lst
(partial prefix-text-and-print prefix))) ;; ATTENTION
Let's try it:
(prefix-text-to-each "hola: " '(1 2 3)
;; hola: 1
;; hola: 2
;; hola: 3
Now we could think on making a function that combines not a text, but a list, with each element of another list. Again, we begin with combining the thing (a list) to a single element:
(def sufix-list-and-print [a-lst x] ;; Yeap, for practical reasons, let's suffix the thing (the list) instead of prefixing.
(slist-iter
a-lst
(partial prefix-text-and-print x ;; we are reusing this function, why not?
)))
(sufix-list-and-print '(1 2 3) 88)
;; 88 1
;; 88 2
;; 88 3
With that function, we can now create the function that combines 2 lists:
(def print-2-lsts-combined [a-lst b-lst]
(slist-iter
b-lst
(partial prefix-list-and-print a-lst)))
Let's try that:
(print-2-lsts-combined '(1 2 3) '(10 20 30))
;; 1 10
;; 1 20
;; 1 30
;; 2 10
;; 2 20
;; 2 30
;; 3 10
;; 3 20
;; 3 30
It happens that if the 2 lists are the same, you will have the Cartesian product. So you can define the Cartesian product in terms of this function:
(defn slist-cartesian [lst]
(print-2-lst-combined lst lst))
Or by itself in a similar way to the print-2-lst-combined function, which resembles the code you show originally:
(def slist-cartesian [lst]
(slist-iter
lst
(partial prefix-list-and-print lst) ;; ATENTION
))
Now, the original code uses fn instead of partial. fn and partial have the same job: to create a function.
Do you see it?
The problem in the list commented with ATTENTION was to give somehow the list lst to a function that receives only 1 parameter and you couldn't use that parameter for such purpose.
So the solution with partial was to derive such function.
The solution with fn was to express that function in place, and "say" the word lst when needed, because it could due to it being a Closure, as explained before. That is an anonymous function and I invite you to read more about it.