Clojure keyword argument default depending on other keyword argument? - clojure

I am trying to get an optional keyword parameter that depends on another optional keyword parameter for its value.
Example function:
(defn printab [& {:keys [a b] :or {:a 5 :b 200}}]
(println "a is" a "b is" b))
I want the default value of :b to be the value of :a. Is there a way to do this within the defn macro, or do I just need to create a let binding to handle this behavior?
The below function does not work the way I hoped:
(defn printab [& {:keys [a b] :or {:a 5 :b a}]
(println "a is" a "b is" b))

It's possible to achieve this without using a let. Part of the problem is the colon prefixes in the map of defaults, which should be omitted. I think you'll also need a reference to the whole map of arguments for it to work. The following appears to do it:
(defn printab [& {:keys [a b] :as m :or {a 5 b (:a m 5)}}]
(println "a is" a "b is" b))

Related

Clojure: First Function and Tick Marks

(ns main.core)
(defn andexp [& resty]
(println "here is resty:" resty)
(first (resty))
)
I am very new to Clojure coming from a Java and C background
In the repl, the input has to be in this format:
(andexp '(John is a beginner so have mercy))
I need to include the tick mark ('). I want my program to print out "John."
There are two things happening here:
You are using & resty to destructure for a list of all the arguments; that is why you see the output of ((John ...)) (note the double (()).
Next you are calling resty, when you write (resty), which results in an error (remember, parens in clojure are always meaningful and not just for groupting things togehter etc. like in curly braces languages).
So this will do what you want:
repl=> (defn andexp [resty]
(println "here is resty:" resty)
(first resty))
#'repl/andexp
repl=> (andexp '(a b c))
here is resty: (a b c)
a
If you really want to destructure (like stated in the comments), you have to put your arguments into another pair of [] to destructure on the passed in list. E.g.
repl=> (defn andexp [[f & resty]]
(println "here is f and resty:" f resty)
(first resty))
#'repl/andexp
repl=> (andexp '(a b c))
here is f and resty: a (b c)
b
More infos about
destructuring
If you write it like so:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first resty))
you will get a result:
(andexp '(John is a beginner so have mercy)) #=>
here is resty: ((John is a beginner so have mercy))
(John is a beginner so have mercy)
but you probably wanted something more like this:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first resty))
(println :result (andexp 1 2 3 4) ) ; no wrapping parens
with result:
here is resty: (1 2 3 4)
:result 1
Another option:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first (first resty))) ; or `ffirst`
(println :result (andexp [1 2 3 4]) )
with result:
here is resty: ([1 2 3 4])
:result 1
It may help to clarify the answer if you add more info about the use-case or goal.
For background, please see the documentation listed here:
https://github.com/io-tupelo/clj-template#documentation

Get Clojure argument list

I want something that gives me the sequence of actual values passed to a function, similar to the arguments value in a javascript function.
I am aware that I can grab the entire function argument list using
(defn fx [& args]
args)
<= (fx {:a 1} 2)
=> ({:a 1} 2)
But this removes the arity on my function. I want to have something like
(defn fx [{:keys [a]} b]
(MAGIC_FUNCTION_THAT_RETURNS_THE_ARGS_VALUES))
<= (fx {:a 1} 2)
=> ({:a 1} 2)
Is it possible to get a raw sequence of the values passed to a function?
By the time the function body is executed, the parameters have already been destructured. You could define your own defn macro and expose those values. I know Lighttable does this in their Instarepl to show the argument values.
Using argument destruction can help. The following works fine for me (as far as I know, it also works for old versions of clojure).
(defn example [ & [a b :as args]] [a b args])
(example 1 2)
=> [1 2 (1 2)]
The key point is that you can destruct the argument after &. The drawback is that it is possible to call the function with more arguments than expected (for example (example 1 2 3) is a valid invocation. Special care should be taken if this might be a problem.
Note: I came across this question while I was searching for similar feature. I kept digging and using an idea from here and :as as it was suggested in this answer, I found a solution for my problem.
I don't know of a way to do this as you describe, but depending on what you're wanting to do there are some options.
If you're wanting to ensure the function is only called with two arguments, consider a precondition:
(defn fx [& args]
{:pre [(= 2 (count args))]}
args)
user=> (fx 1 2)
(1 2)
user=> (fx 1 2 3)
AssertionError Assert failed: (= 2 (count args)) user/fx (NO_SOURCE_FILE:1)
If you're wanting to keep track of your intended arity of a function, but still have access to a vector of all args, you could add your own metadata:
(defn
^{:my.ns/arglists '([{:keys [a]} b])}
fx [& args]
args)
user=> (fx 1 2)
(1 2)
user=> (-> #'fx meta :my.ns/arglists first)
[{:keys [a]} b]
If you're just wanting access to the destructured values you described and access to an args value, you could use let:
(defn fx [{:keys [a]} b]
(let [args [{:a a} b]]
[a b args]))
user=> (fx {:a 1 :c 3} 2)
[1 2 [{:a 1} 2]]
user=> (fx {:a 1 :c 3} 2 4)
ArityException Wrong number of args (3) passed to: user$fx clojure.lang.AFn.throwArity (AFn.java:437)
You could also do a combination of these.
Not very nice as it requires to pass params as a vector, but seems apt
user.main=> (defn fx [[{:keys [a] :as e} b :as o]] [a b e o])
#'user.main/fx
user.main=> (fx [{:a 1} 2])
[1 2 {:a 1} [{:a 1} 2]]
user.main=>
You can use a macro to bind the arguments to symbol, _args in this example.
(defmacro defn-args [name args & body]
`(defn ~name ~args
(let [~'_args ~args]
~#body)))
You can then use _args in the body of the function to refer to the arguments:
user> (defn-args foo [{:keys [a b]} y z] _args)
user> (foo {:a 1 :b 10} 2 3)
[{:a 1, :b 10} 2 3]
This is the best I could cook up.
(def ^:dynamic *arguments* nil)
(defn unstructure [form]
(cond
(or (vector? form) (map? form)) (gensym)
(= form '&) '&
:else form))
(defmacro bind-args-defn [name args & body]
(let [simple-args (vec (map unstructure args))
i (.lastIndexOf simple-args '&)
[h r] (split-at (if (neg? i) (count simple-args) i) simple-args)
r (drop 1 r)]
`(defn ~name
~simple-args
(binding [*arguments* (lazy-cat ~#(map vector h) ~#r)]
(let [~args *arguments*]
~#body)))))
(bind-args-defn ;;my special val binding defn
silly ;;the name
[{:keys [a]} [b & c] & d] ;;the arg vector
{:vals *arguments* :a a :b b :c c :d d}) ;;the body
Obviously, this does not accept the full set of defn options (metadata, docstring, pre and post, arities, etc) that can be passed to defn, but I think it illustrates the idea.
It works by capturing the args vector, and then creating a simple-args vector of the same length as the original args but with no destructuring; using that as the defn argument vector. It then massages this simple-args vector into a sort of flat vector without &, which it assigns to *arguments*. *arguments* is then destructured using the original args vector. Kind of convoluted, but it does what I want at the moment.
> (silly {:a 1} [2 3 4] 5 6)
{:vals ({:a 1} [2 3 4] 5 6), :a 1, :b 2, :c (3 4), :d (5 6)}

How does this defn method1 [{:keys [a b c] :as obj}] map method works?

I have the following Clojure code
(defn myFunction [{:keys [a b c] :as myAtom}]
(let [new-a 1]
(+ new-a a)
(assoc myAtom :a new-a)))
I would like to know how :keys and :as work
The first line defines a function (in a var) that takes a single argument which must be a map
(defn myFunction [{:keys [a b c] :as myAtom}]
Then It creates a let statement binding the given symbols a b c to the values obtained by looking up each of the symbols as a keyword in that argument, and bind the name myatom to the original map
(let [new-a 1]
Then bind new-a to the value 1
(+ new-a a)
do some math and ignore the result
(assoc myAtom :a new-a)))
This last segment uses the symbol bound to the whole map (passed as an argument) as and calls assoc to create a new map associating :a with the value in new-a. Then retrurns that as the value of the function.
This returns a new map with the value 1 for :a
user> (myFunction {:a 3 :b 3 :c 3})
{:a 1, :c 3, :b 3}

Better way to nest if-let in clojure

Say I have a map of this form:
(def m {:a "A" :b "B"})
and I want to do something if :a and :b are both not nil, I can do:
(if-let [a (:a m)]
(if-let [b (:b m)]
... etc ))
or
(if (and (:a m) (:b m))
(let [{a :a b :b} m]
... etc ))
or even
(if (every? m [:a :b])
(let [{a :a b :b} m]
... etc ))
Is there a neater (ie one-line) way to achieve this?
I think a macro may be necessary here to create the behavior you want. I have never written one (yet) but the following representation suggests to me that this might be fairly straightforward:
(let [{:keys [a b]} m]
(when (every? identity [a b])
(println (str "Processing " a " and " b))))
Using the :keys form of destructuring binding and every? enables a single specification of a vector of keys to destructure and check, and the bound locals are available in a following code block.
This could be used to make a macro such as (when-every? [keys coll] code-with-bindings)
I may update this answer with the macro code if I can take the time to work out how to do it.
You could use map destructuring -- a useful feature of Clojure. This also exploits the facts that and is short-circuiting, and any key in the first map not found in the second map gets nil, a falsy value:
(let [{a :a b :b} {:a 1 :b "blah"}]
(and a b (op a b)))
Okay, so it's two lines instead of one .... also this doesn't distinguish between nil and other falsy values.
not-any? is a nice shortcut for this:
user> (not-any? nil? [(m :a) (m :b)])
true
user> (not-any? nil? [(m :a) (m :b) (m :d)])
false
user>
I am not quite sure what you want to do if the keys have non-nil values or whether you want non-nil keys or values returned. So, I just solved it for non-nil keys being returned.
You'd use the following as an intermediate step as part of a final solution.
I'm showing all the steps I used, not to be pedantic, but to provide a complete answer. The namespace is repl-test. It has a main associated with it.
repl-test.core=> (def m {:a "A" :b "B" :c nil})
#'repl-test.core/m
repl-test.core=> (keys m)
(:a :c :b)
and then finally:
; Check key's value to determine what is filtered through.
repl-test.core=> (filter #(if-not (nil? (%1 m)) (%1 m)) (keys m) )
(:a :b)
By the way I found an ugly one-liner, which works because and returns the last thing in its argument list if they're all true:
(if-let [[a b] (and (:a m) (:b m) [(:a m)(:b m)])]
(println "neither " a " nor " b " is falsey")
(println "at least one of " a " or " b " is falsey"))

In Clojure how can I pass multiple arguments to a defmethod?

I wish to create a multi method which I call like this:
(defmethod some-method "some value"
[ a b ]
b)
: but which selects the function based only on the first paramter 'a'. How can I do this:
(defmulti some-method
WHAT GOES HERE?)
I didn't completely understand your question, but I think you want to
dispatch only on one argument. You can do that like this, I think:
user=> (defmulti even-or-odd (fn [x _] (even? x)))
#'user/even-or-odd
user=> (defmethod even-or-odd true [a _] :even)
#<MultiFn clojure.lang.MultiFn#293bdd36>
user=> (defmethod even-or-odd false [a _] :odd)
#<MultiFn clojure.lang.MultiFn#293bdd36>
user=> (even-or-odd 2 3)
:even
user=> (even-or-odd 3 3)
:odd
user=>
Do you mean select the function based on the value of a?
Then you just need
(defmulti some-method (fn [a b] a))