Fork me on GitHub
#beginners
<
2022-09-16
>
skylize01:09:10

Is there a cleaner way to include a value in a map only when truthy?

(if foo
    {:foo foo :bar bar}
    {:bar bar})

seancorfield02:09:44

I like this:

(cond-> {:bar bar}
  foo (assoc :foo foo))

skylize02:09:50

yeah. that's an improvement

skylize02:09:21

Anything about destructuring rules that could help make it even cleaner in a let binding?

(let [m (cond-> {:bar bar}
          foo (assoc :foo foo))]
  ... )

didibus03:09:29

A util function you write could

didibus03:09:30

(defn assoc? [m k v]
  (if v (assoc m k v) m))

didibus04:09:17

If you want the key to automatically derive from the variable name, you could also make it a macro, though I'd say it might be a bit confusing to the user since it only makes sense if you pass in a symbol referencing a local or global var.

(defmacro assoc? [m v]
  (if (symbol? v)
    `(if ~v (assoc ~m (keyword (name '~v)) ~v) ~m)
     (throw (ex-info "assoc? value must be a symbol referencing the value." {}))))

(let [foo 12]
  (assoc? {} foo))
;;=> {:foo 12}

didibus04:09:40

Though, maybe if you overload it, then it can be a convenience 2-arity:

(defmacro assoc?
  ([m v]
   (if (symbol? v)
     `(if ~v (assoc ~m (keyword (name '~v)) ~v) ~m)
     (throw (ex-info "assoc? value must be a symbol referencing the value." {}))))
  ([m k v]
   `(if ~v (assoc ~m ~k ~v) ~m)))

(assoc? {} :foo 12)
;;=> {:foo 12}
(assoc? {} :foo nil)
;;=> {}
(def foo 12)
(assoc? {} foo)
;;=> {:foo 12}
(def foo nil)
(assoc? {} foo)
;;=> {}

metal 1
seepel04:09:16

I've always done

(merge {:bar bar }
       (when foo {:foo foo }))
I've also always wondered in what scenario I would use cond-> > I like this: >
(cond-> {:bar bar}
>   foo (assoc :foo foo))
Thank you @U04V70XH6

Volodymyr11:09:48

Hello Guys! I beginner of clojure. And I have question about httpkit and ring flash message middleware. I have small application with next routes

(GET "/login" [] #'auth/Login)
(POST "/login" [] #'auth/LoginCheck)
GET /login - just render form via formative POST /login - validate form-params Also I use middleware ring.middleware.flash and ring.middleware.session After submit form on GET /login, handler validate form-params on POST /login and return next map {:status 302 :headers {"Location" "/login"} :flash {:problems {...}}} And when I tried to get flash message on GET /login, I got nil. Can you help why flash messages is nil after redirect? Thank you

Volodymyr12:09:16

Forme works when I change position, move session middleware after flash middleware

sheluchin12:09:19

Some things are both functions and macros in CLJS. Where can I read about this topic to understand it better?

vonadz17:09:16

How does one run a jvm-opts alias in the command line? I'm trying to launch a repl like so, with an alias :jdk-17 defined in deps.edn

clj -M:repl/nrepl -O:jdk-17

hiredman17:09:38

I don't think -O is a valid flag for clj, but everything after the -M flag and its args may get passed to your main function

pavlosmelissinos17:09:20

is jdk-17 an alias that sets a jvm-opts flag? If so clj -M:repl/nrepl:jdk-17 should work

Alex Miller (Clojure team)17:09:01

-O used to be a thing but is not anymore

Alex Miller (Clojure team)17:09:36

Should just use with -A or -M or -X as appropriate

vonadz17:09:06

@UEQPKG7HQ sweet, this worked. Thanks! @U0NCTKEV8 @U064X3EF3 thanks for the info!

fadrian20:09:18

I am looking for a simple way to split a list as follows: (split nil nil) -> nil (split '(1 2 3) '(true false)) -> ((1) (2 3)) (split '(1 2 3) '(false true)) -> ((1 2) (3)) (split '(1 2 3) '(true true)) -> ((1) (2) (3)) Essentially, the list is split where the corresponding entry in the second parameter is true and is not split where it is false. Anybody have a neat way of doing this?

pppaul20:09:38

group-by is pretty good at splitting lists. i don't understand your examples, though

pppaul20:09:31

so, this is sorta like a map that takes a 2-arity fn and tags the output as split/no-split... something like that?

pppaul20:09:20

try map, i think you can get what you want from a map that takes in 2 lists, and then another processing step after that

fadrian20:09:32

As to your second comment, that is how the data is being derived. Basically, I have a list of dates. The dates have gaps between them. I need to split the list wherever the gap is larger than a particular size.

pppaul20:09:34

(->> (map vector list1 list2) (split-with second)) maybe something like this,

pppaul20:09:06

hmm, that example doesn't cover the 3rd case

pppaul20:09:10

so, if you extend the 2nd list, you assume the rest of the values are true?

pppaul20:09:41

ok, i think my solution is close, but can't use split-with and need to use mapcat

pppaul20:09:52

give me a second, testing out a solution

pppaul20:09:50

i think your first example is incorrect

fadrian20:09:31

In the first example, because list2[0] is true, the list is split after list1[0]; because list2[1] is false, the next list is not split after list1[1].

pppaul20:09:46

ok i found a solution using partition-by

pppaul20:09:44

sorta, it doesn't really work, but it is close

pppaul20:09:27

so, if parition-by splits on true/false, then i have a solution

fadrian20:09:49

Yeah, I was looking at that, but I couldn't figure out how to make the polarity of the function argument change at the right time.

pppaul20:09:21

partition-by is pretty big, to get it to do what you want is probably 4-5 lines of code

pppaul20:09:35

cus you don't need memory, and partition-by has a memory

fadrian20:09:41

What I'd need is a function that changes polarity each time the list element is true.

fadrian20:09:08

Looking at it that way, I think I can figure it out.

pppaul20:09:21

actually, i'm not sure changing partition-by will do this

pppaul20:09:27

cus, i just did that

pppaul20:09:49

it's using take-while, which isn't going to behave the way you want

Ben Sless20:09:21

This gets a bit close:

(defn interleave-all
  "Returns a lazy seq of the first item in each coll, then the second etc."
  {:added "1.0"
   :static true}
  ([] ())
  ([c1] (lazy-seq c1))
  ([c1 c2]
   (lazy-seq
    (let [s1 (seq c1) s2 (seq c2)]
      (cond
        (and s1 s2)
        (cons (first s1) (cons (first s2)
                               (interleave-all (rest s1) (rest s2))))
        s2 s2
        s1 s1
        :else nil)))))

(partition-all 2 2 (interleave-all '(1 2 3) '(false true)))

pppaul21:09:27

the description is actually a bit off

pppaul21:09:42

splits are required at both true and false values, depending on the previous value

pppaul21:09:48

so, the fn needs memory

pppaul21:09:07

are you sure your examples are correct, cus i feel like the examples do not match the description.

pppaul21:09:42

(defn split [l1 l2]
  (->>
    (map vector l1 (concat l2 (repeat true)))
    (reduce (fn [{:keys [last coll] :as acc} [k t-f]]
              (let [last-run              (peek coll)
                    coll-without-last-run (pop coll)]
                (cond
                  (or t-f
                      (and (not t-f) last)) (-> acc
                                                (update :coll conj [k])
                                                (assoc :last t-f))
                  :else (let [updated-coll (conj coll-without-last-run (conj last-run k))]
                          (assoc acc
                                 :last t-f
                                 :coll updated-coll)))))
            {:last false
             :coll [[]]})))
i came up with this, but it follows the logic from the description, and not the examples

pppaul21:09:16

also the 1st entry is broken in the output.

Martin Půda21:09:07

(defn split [v1 v2]
  (->> (interleave v1 (conj v2 (peek v2)))
       (partition-by true?)
       (remove #{[true]})
       (map #(remove false? %))))

(split nil nil)
=> ()
(split '(1 2 3) [true true])
=> ((1) (2) (3))
(split '(1 2 3) [false true])
=> ((1 2) (3))
(split '(1 2 3) [true false])
=> ((1) (2 3))

😯 1
👍 1
stantheman10:09:33

My less erudite version? (defn split-use-bool "Split a list according to list of booleans" [[hd & tail], bool-sq] (let [flst (fn flst [num] (cons num '()))] ;; flst wrap num in list (case bool-sq ((true true)) (cons (flst hd) (cons (flst (first tail)) (flst (rest tail)))) ((true false)) (cons (flst hd) (flst tail)) ((false true)) (cons (cons hd (flst (first tail))) (flst (second tail))) ((false false)) (cons hd tail) "bool seq error"))) ;test (defn test-splits [] (let [lss (list 1 2 3)] (for [fbool (list true false) sbool (list true false)] (prn [fbool sbool]) (split-use-bool lss [fbool sbool])))) (test-splits)

Andrew Leverette21:09:51

I have a beginner's question about tooling in Clojure. What is the recommended build/package management tool? I see that the Clojure website suggests using clj, but I've also seen that many projects and resources refer to lein. The clj doesn't seem to have a convenient way to start a new project, but it does seem to have really good support for building and installing dependencies. The lein tool, on the other hand, seems to have a better user experience but seems to lack build options and package management support. I would appreciate any thoughts or guidance on this. For some context, I have read through the Learn Clojure guide and I've been reading through Clojure for the Brave and True.

R.A. Porter22:09:41

One way to ease the beginner's onboarding with Deps is with something like this - https://github.com/seancorfield/deps-new - to give you project templating/bootstrapping without much effort.

1
hiredman22:09:33

both lein and clj (or tools.deps which is the library behind the clj script) have a file format for specifying dependencies and will fetch and make those dependencies available

hiredman22:09:33

dependencies are mostly maven artifacts, which both lein and clj support. clj adds some support for some other dependency types, most notably it can depend directly on a git repo

hiredman22:09:30

lein is older, is primarily a build tool, has a lot of built in build tool type functionality (packaging your project as a jar, etc) and has a large ecosystem of plugins

Andrew Leverette22:09:49

So is there one that is the standard?

hiredman22:09:57

clj is newer, is primarily a clojure process launcher, and a lot of functionality for doing build kind of tasks come from extra libraries

hiredman22:09:38

clj is "official" in that it is from the same people that write clojure

1
Andrew Leverette22:09:43

clj feels really nice, but it seems to be missing convenient tooling for starting a project.

hiredman22:09:07

so there is the deps-new tool linked above

hiredman22:09:00

you'll also want to look at tools.build which is a library that adds build tool like functionality to clj

Andrew Leverette22:09:33

I was aware of clj-new but that seemed complicated to set up.

Andrew Leverette22:09:43

I'll give deps-new a try

hiredman22:09:20

if you project is light enough, you can get away without any kind of dependency file at all

Alex Miller (Clojure team)22:09:28

to get started with clj, you technically don't need anything to get a project started, even a deps.edn with just {} will work

hiredman22:09:22

for example https://git.sr.ht/~hiredman/lions doesn't have a deps.edn at all because it doesn't use any external libraries and whatever version of clojure clj defaults too is fine

Alex Miller (Clojure team)22:09:26

https://clojure.org/guides/deps_and_cli is a guide that demonstrates adding deps, referring to other local deps etc. And since clj/deps.edn supports git deps, pushing it to github makes it available for others too

Andrew Leverette22:09:05

Ah, I must have missed this.

Alex Miller (Clojure team)22:09:04

for future questions, #tools-deps is a good place to ask any deps.edn or clj related questions, and #tools-build is good for tools.build once you grow to that point (also see that guide https://clojure.org/guides/tools_build)

Andrew Leverette22:09:51

Okay, good to know. Sorry for posting in the wrong channel.

R.A. Porter22:09:32

Not the wrong channel. You'll just get better, more in-depth answers in those other channels as you continue.

1
Alex Miller (Clojure team)22:09:36

seconded - perfectly ok here (but more specialized forums exist)

Andrew Leverette22:09:56

Thanks, again. I'm new to the Clojure community, so I'll be sure to explore the Slack channels. If any of you have any must-use resources, I would be happy to have any recommendations.

Andrew Leverette22:09:52

I've been using Exercism and working on some of the Project Euler problems.

didibus06:09:23

One big difference is lein includes most functionality inside itself, it's a big monolith. With clj, you're meant to install tools which are compatible with it, which are seperate programs maintained by others, such as deps-new linked above. Through installing various tools over tools.deps, and using tools.build for your build tasks, you get back all the same functionality as lein, but in a more micro-tooling kind of way.

Andrew Leverette22:09:30

Thanks, @U0K064KQV! That was a very helpful way to break down the differences.

practicalli-johnny07:09:31

I created a user configuration for Clojure CLI that adds a wide range of tools, providing the same kind of tasks as Leiningen https://practical.li/clojure/clojure-cli/install/community-tools.html https://github.com/practicalli/clojure-deps-edn

👀 1
Andrew Leverette12:09:05

Thank you, @U05254DQM! I must say that you have a very nice collection of Clojure resources. I will be exploring that more.

👍 1
practicalli-johnny16:09:21

Thanks. I’m always updating (Covid allowing) so let me know if there is anything missing that would be useful or anything that may need more explination.