Search code examples
listclojurearguments

Cannot pass list as argument to function


I have a substitute function that will take in an expression such as (or false x y) and a binding map such as '{x false, y true} and will return the list with the appropriate substitutions. The result is in l and displayed. How might I pass l into another function to do some other processing? The definition for the function I'm passing to looks like this:

(defn f [expression]
    )

I have tried passing l as a parameter when calling f, but I get the following error:

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:550)

Here is my substitute function:

    (defn deep-substitute [m l]
  (map (fn [i]
         (if (seq? i)
           (deep-substitute m i)
           (m i i)))
       l
       (f 'l)))

My new function fully works the way I expect it to when calling it with an unevaluated list, but not when I pass it an unevaluated list.


Solution

  • The job of deep-substitute is to fill in a template form and return it. The problem gets harder if you try to make deep-substitute also responsible for doing the next step in the transformations as well. Perhaps let it return the value, then pass that value on to another transform function after it's returned.

    Here's an annotated working example:

    user>  (defn deep-substitute [m l]      
             (map (fn [i]          
                    (if (seq? i)             ;; if this is a sequence, 
                      (deep-substitute m i)  ;; finish the sequence and include the result here
                      (get m i i)))          ;; otherwise get the new value from the map, if found
                  l))                        ;; if it's not found, us the value unchanged.
    #'user/deep-substitute
    

    then test it on your example:

    user> (deep-substitute '{x false, y true} '(or false x y))
    (or false false true)
    

    and a more nested example:

    user> (deep-substitute '{x false, y true} '(or false (and x (or y x)) y))
    (or false (and false (or true false)) true)
    

    a common next step is to wrap that in a macro so it can modify forms before they are evaluated, then evaluate that newly created from to find out what value it becomes:

    user> (defmacro dsub [m l]
            (deep-substitute m l))
    #'user/dsub
    

    and test it

    user> (dsub {x false, y true} (or false (and x (or y x)) y))
    true
    
    user> (dsub {x false, y true} (or false  x))
    false
    

    or pass the resulting form (unevaluated) into another function by nesting it:

    user> (defn f [expression]
            (reverse expression))
    #'user/f
    
    user> (f (deep-substitute '{x false, y true} '(or false (and x (or y x)) y)))
    (true (and false (or true false)) false or)
    

    A common pattern is to use the threading macros -> ->> etc. to pass the result from transform to transform until the final result is made by composing many simple operations:

    user> (->> '(or false (and x (or y x)) y)
               (deep-substitute '{x false, y true})
               f
               (deep-substitute '{false, 41 true 42}))
    (42 (and 41 (or 42 41)) 41 or)