Fork me on GitHub
Philip Hale00:06:57

Is it possible to use sequential destructuring to write multi-arity functions that do different things depending on the length of the input? Or is it necessary / better to rewrite the function to be variadic and call it with apply?


not sure what you mean


fn's out of the box support having different bodies for different argument counts, without destructuring


depending on what you mean, this works

ins)user=> (defmulti arg-count-based (comp count list))
(ins)user=> (defmethod arg-count-based :default [& etc] "?")
#object[clojure.lang.MultiFn 0x6622fc65 "clojure.lang.MultiFn@6622fc65"]
(ins)user=> (defmethod arg-count-based 2 [a b] (str "2 args" a b))
#object[clojure.lang.MultiFn 0x6622fc65 "clojure.lang.MultiFn@6622fc65"]
(ins)user=> (arg-count-based)
(ins)user=> (arg-count-based :x :y)
"2 args:x:y"
(ins)user=> (arg-count-based :x :y :z)


you don't need to use apply to invoke variadic functions


but yes, you can also match directly on arg count as @hiredman says and that's simpler


"match" may be something of a misnomer


that's fair, it can imply a lot more than I mean here


Clojure 1.9.0
user=> (defn f ([a] [a]) ([a b] [a b]) ([a b c] [a b c]))
user=> (f 1)
user=> (f 1 2)
[1 2]
user=> (apply f [1 2])
[1 2]


right the only difference with the multimethod is the ability to extend outside the definition and have a :default

Philip Hale00:06:20

Okay thanks, that's interesting. So a stupid example -- one using multi-arity methods and another using multimethods:

(defn juggle
  "I can only juggle three balls"
  ([b1] :trivial)
  ([b1 b2] :easy)
  ([b1 b2 b3] :hard)
  ([b1 b2 b3 & args] (throw (AssertionError. "Ouch, too many!"))))

(defmulti m-juggle (comp count list))
(defmethod m-juggle :default [& etc] (throw (AssertionError. "Ouch, too many!")))
(defmethod m-juggle 3 [& etc] :hard)
(defmethod m-juggle 2 [& etc] :easy)
(defmethod m-juggle 1 [& etc] :trivial)
Both are called with variable lists of arguments rather than a seq (which would need the apply)


you could just use count as your dispatch if you want to dispatch on verious sizes of sequential input


I may have misunderstood your question

Philip Hale00:06:24

no I think you're right...

Philip Hale00:06:19

what I'm actually working on is this fn:

(defn base64-expand-bytes
  "Base64 byte-expansion procedure, to reduce the number of possible values per
  byte from 256 to 64."
  (let [[b1 b2 b3] bs
        c1 (bit-shift-right b1 2)                             ; first 6 bits of b1
        c2 (bit-or (bit-shift-left (bit-and 2r00000011 b1) 4) ; last 2 bits of b1 plus first 4 bits of b2
                   (bit-shift-right b2 4))
        c3 (bit-or (bit-shift-left (bit-and 2r00001111 b2) 2) ; last 4 bits of b2 plus first 2 bits of b3
                   (bit-shift-right b3 6))
        c4 (bit-and 2r00111111 b3)]                           ; last 6 bits of b3
    [c1 c2 c3 c4]))
...which needs to behave slightly differently when passed a list of length 1 or 2 (it's going to return nil for bytes that should be padding). I was just wondering if it was possible to keep the argument a seq rather than changing the signature to be a list of args?


a multimethod with count as dispatch would do this without needing apply


but I wouldn't be reluctant to use apply


and the "special logic for 1 or 2 items, something else for anything greater" matches arg count dispatch perfectly

👍 4
Philip Hale01:06:37

Thanks 🙂 -- this is just a pet project so I have no qualms rewriting / using apply, just interested to see what's recommended. I haven't used multimethods much yet, will look into that. Appreciated!


personally I would only consider a multimethod if the pattern of dispatch doesn't match n, n+1, n+2, n+x pattern which function arg count dispatch does

Philip Hale01:06:06

yes that makes sense

Philip Hale01:06:31

i think that's right -- would rather keep it as a multi-arity fn and add apply in a couple of spots than use a multimethod, at least for this. although it probably doesn't matter too much either way


I would like to use embedded tests using cljs.test. (Embedded like this)

(defn- split
  "Splits text at idx"
  {:test (fn []
           (is (= [" " " "]
                  (split "  " 1)))
           (is (= ["(foo\n " "\n bar)"]
                  (split "(foo\n  \n bar)" 6))))}
  [text idx]
  [(subs text 0 idx) (subs text idx)])
It works using clojure.test, and I am told I could write a macro that makes something like this work with cljs.test as well. But I have no clue how to do it. Would it be a macro, maybe defnt that will do both the defn and the deftest forms for me? Also, I have never made a macro. 😄


Is there a way to with-redefs multiple deftests? When I wrap deftest with with-redefs, it doesn't seem to work for me


with-redefs is in effect for the body inside it. (with-redefs [...] (deftest ...)) says "redefine these globals while defining my test, then reset them". Thanks to clojure's rules for looking up global vars, the test that deftest creates looks for the var, not the the binding the var held when deftest ran


(deftest ... (with-redefs [...] ...)) almost works, as long as you never run tests in parallel


It's cleaner to use explicit arguments in the function being tested, instead of globals. Second best is to use dynamic vars and binding. with-redefs is a hack and has many gotchas


thanks for explaining! i see what the scope is now


sadly i'm just rewriting test for a already complex system, and the test is relying on lots of external resources which needs to be redefined..


previously it's just using midje, so it works


wish there's some fixtures that works for a subset of tests in namespace


i thinki I got it working by doing (deftest (with-redefs (deftest ...))) really hacky, but the only way I can get it working


one option is to make separate test namespaces (likely they would be named for scopes, eg. "" "" "")


so I'm using [com.novemberain/langohr "5.0.0"] in a project but it's logback configuration from one of it's dependencies com.rabbitmq/amqp-client is trumping my log4j2 configuration in my project. I've tried [com.novemberain/langohr "5.0.0" :exclusions [ch.qos.logback/logback-classic]], but still no dice. Help !!!!


i've also set log4j.configurationFile="resources/log4j2.xml" in


what makes you think it is a logback configuration issue?


whenever I remove the function calls that i'm using from langohr and the dependeny from my project, the logging turns back to my log4j2 config instead of the default logback


but that doesn't imply a logback configuration issue, right? that just implies something is pulling in logback (ruling out a dependency has a logback configuration file in its jar)


I forget how exclusions are applied, but you may need to specifically exclude the logback dependency from the library that is bringing it in, not the library that is transitively bringing it in


Really beginner here... how should I loop through a string list? doseq? for?


doseq if you are doing something for side effects, for if you are generating a sequential result and don't want to perform any operation which isn't consumed


@d.guay what are you trying to accomplish. a lot of times the result will drive how you get there


@dpsutton I'm trying to add strings to database


ok. so you want to side effect for each one. for and map are lazy and not what you want. doseq sounds like a good bet. also check to see if your database interaction layer handles collections and let that handle it for you


We use faraday


looks like this is where you want to look then


I'll look at this thanks !

Mario C.18:06:36

If I have a quoted function '(some-fn arg1 arg2). Is there a way to call it without using eval?


if it's a function and not a macro (apply (resolve (first l)) (rest l)) will work if some-fn actually exists in scope


but why do you end up in that situation and what are you actually trying to do? usually there's a better option


pedantically there's no function there - it's a list of symbols. resolve uses a symbol to look up a var


(yes a symbol is technically a function, but not the function you want)

Mario C.18:06:22

The quoted function is actually a constructor for a defrecord.


you need a mapping form symbol to function. You could use your own mapping for it i suppose {'constructor ->MapConstructor ....} or you can use eval to look them up


(let [constructors {'record hash-map}
      [constructor & args] '(record :a 1)]
  (apply (constructors constructor) args))
{:a 1}


resolve already does that lookup


but I do like using an explicit map if you are using user data (that way you have a whitelist of what they can run)


a constructor function for defrecord does work with resolve, as it is in a var


right. like making your own interpreter without exposing arbitrary eval


i've been reading lisp in small pieces and this is exactly how you make a lisp 2

Mario C.18:06:24

Thanks guys! Going to see what I can come up with


to be clear - my first example does work as shown

Clojure 1.9.0
(ins)user=> (def l '(+ 1 2 3))
(ins)user=> (apply (first l) (rest l))
ArityException Wrong number of args (3) passed to: Symbol  clojure.lang.AFn.throwArity (
(ins)user=> (map type l)
(clojure.lang.Symbol java.lang.Long java.lang.Long java.lang.Long)
(ins)user=> (apply (resolve (first l)) (rest l))

👍 4

(error is intentional to show why resolve is needed)


ah and it prevents arbitrary eval. (apply (resolve '(do (prn "hi") +)) 3 4) throws an error. wasn't sure what it would do


yeah, resolve only makes sense for a symbol - but an explicit lookup map is better if possible


Is there a way to create a map from the map function by getting key from coll?

(def my-coll [{:key1 "value1" :key2 "value2"} {:key1 "value1" :key2 "value9"} {:key1 "anothervalue" :key2 "value6"}])
then I can do that:
(map #(get % :key1) my-coll)    ;returns ("value1" "value1" "anothervalue")
(map #(get % :key2) my-coll)    ;returns ("value2" "value9" "value6")
I'd like to have another object that would look like this:
{:value1 ["value2" "value9"] :anothervalue ["value6"]}
not sure if that make any sense 😕

Mario C.19:06:11

Lets say I have something like this (def db-engines {"postgres" '(->PostgreSQL url)}) and I want to instantiate by calling a function say (defn db [] (get db-engines "postgres")

Mario C.19:06:56

@noisesmith By using your example would I be doing this (defn db [] (apply (resolve (first (get db-engines "postgres")))) ?


that could work but why not a mapping from string to function of no args, and call that function?

Mario C.23:06:55

Hmm didn't see this for some reason but what do you mean?


instead of (def db-engines {"postgres" '(->PostgreSQL url)}) (def db-engines {"postgres" #(->PostgreSQL url)})


that way the thing you get back from the map is just a function you can call

Mario C.23:06:55

I am going to try this tomorrow!


create a map != map just be aware @d.guay

👍 4

Yes I know


Is there a way to use the result of a function as a key in a map? (def my-map {:key "value"}) ;replace :key by a function result


update I think


isnt that to update a value in a map?


I want the key name to be the result of a function



=> {(inc 1) :two}
{2 :two}
you mean like this?


{:thisistheresultofmyfunction "value"}


there's not a built-in fn. you could do something with map or reduce

lilactown19:06:15 is also available but I'm not sure it fits your use-case


Actually I'm trying to go from this:

({:id "id1", :name "name1"} {:id "id1", :name "name2"} {:id "id2", :name "name3"} {:id "id2", :name "name4"}  {:id "id3", :name "name5"})
to this
{:id1 ["name1" "name2"] :id2 ["name3" "name4"] :id3 ["name5"]}
or something similar to work with


(reduce (fn [grouped m] (update grouped (keyword (:id m)) conj (:name m))) {} your-collection)


That look like it's working. I must say I don't understand much of it lol


Do you know how reduce works?


it applies a function on every item of a collection by accumulating the result?


yes, right


so that uses the 3-arity version of reduce that specifies the initial accumulator: in this case {}


empty map to start with


then for each of your input maps, it’s going to call update


update takes: 1. the map to update - grouped which is what your accumulator is called in the reduce function 2. key to update, in this case we take the value of :id in the input map and turn it into a keyword, so {:id "id1"} becomes :id1 3. the function to call on the current value of that key. In this case it’s conj. current value will be the first arg to this function 4. Any subsequent args to the function. In this case we get the value of :name from the input map to conj on


So the first time this is called, the accumulator is an empty map, and we’re calling the reducing function with {:id "id1" :name "name1"}. So the call to update will look like: (update {} :id1 conj "name1") Since there is no :id1 key in the accumulator map, the effect of this update is to set :id1 key to be equal to (conj nil "name1")


Very interesting


thanks a lot for your help !


@michael.gaare would there be a way for the key to be string instead?


like... {"id1" ("value1" "value2") ...}


sure. Just don’t call keyword on it.


(update grouped (:id m) ...)


I just put the keyword call in there so the output would match your example


Clojure is so powerful


How about

(into {}
  (for [[k v] (group-by :id m1)]
    [k (map :name v)]))


m1 being the OP’s map


not bad either


ends up doing more iterations though I think


Hi all, I've followed the guide here to get cider running for my clojurescript project. I was able to successfully connect and the repl does show up however when I try to evaluate cljs code I get that functions don't exist.


I'm using deps.edn with an alias like:

:repl {:extra-deps {cider/cider-nrepl {:mvn/version "0.18.0-SNAPSHOT}
       :main-opts ["repl.clj"]}
and then my repl.clj looks like what's on the guide
(require (quote cider-nrepl.main)) 
(cider-nrepl.main/init ["cider.nrepl/cider-middleware"])


Never mind 🙂 looks like I just had to run cider-jack-in-cljs


I'm looking to rewrite the structure of a map and I have something like this

(map #(do {:foo (:a %) :bar (:b %)}) '({:a 1 :b nil}))
How would I write this if I wanted to exclude :bar if : b was nil?


for starters (fn [m] ...) is better than #(do ...)


@jeremy642 could do this a couple ways. Here’s one simple one: (map #(let [{:keys [a b]} %] (cond-> {:foo a} bar (assoc :b bar)) ...)


I'd use cond-> (cond-> {:foo (:a m)} (:b m) (assoc :bar (:b m)))


@michael.gaare’s version is good too


I’ve seen this kind of pattern before too, although I have mixed feelings: (merge {:foo (:a %)} (when-let [b (:b %)] {:bar b}))


Okay, Looks like I have a few things to look into then but the basics are the same cond-> and assoc. Excluding the merge example.


I kinda agree with @noisesmith and generally disfavor #() except in the simplest cases


I'll keep the fn vs #() in mind. I figured it was an accepted shorthand but I don't have a preference.


right, #() is good as a shorthand, but when you add noise it stops being a shorthand


the difference in character count between #(do) and (fn []) is just two characters, and the do is only there as a hack


Yeah, I can see it.


also #(let [destructuring .... will always trigger me to switch to fn form

👍 4

yeah, to me any compound form inside #() is suspicious, including let (I will make exception for a meaningful usage of -> or ->> in there though)


unless the notion of % is painfully clear i will almost always write a (fn [customer] .. or put some kind of context in there with a var name

Mario C.23:06:50

Quick question: Lets says I have a two local Clojure projects in my directory, Project A and Project B. How can I use Project B in Project A? To give more context, Project B is a published Clojar that local Project A is using but I would like to change some inner workings of Project B so now I want my local Project A to reference my local copy of Project B.

Mario C.23:06:00

Hopefully that make some sense

Mario C.23:06:49

In my project.clj I have {:dependencies [ [projectB "x.x.x"] ]}