Fork me on GitHub
#beginners
<
2021-09-18
>
Benjamin11:09:01

Is common lisp prog1 (aka do but return the first value instead of the last) also a bad sign? I want to say

(and
       (.exists it)
       (slurp it)
       (io/delete-file it)
       )
but return the contents of the file

Benjamin11:09:32

now I made do1 macro cuz lisp 😛

R.A. Porter12:09:17

Look at doto.

Drew Verlee16:09:53

I assume you need to use the "when" function to do the logic your code suggests

hiredman19:09:58

((constantly (and (.exists it) (slurp it))) (.delete it))

hiredman19:09:32

But seriously, use a try/finally when deleting files, so you don't leak temp files on errors

hiredman19:09:01

(and (.exists it) ((constantly (slurp it)) (.delete it))) moving the and out is ever so slightly less heinous

didibus21:09:59

I don't know about being a bad sign, but I would say it isn't really needed and I would get use to doing things a different way instead, like:

(let [file (io/file ".foo")]
  (and (.exists file)
       (try (slurp file)
            (finally
              (io/delete-file file)))))

didibus22:09:16

Or really just use let if you don't want to use try:

(and
  (.exists it)
  (let [ret (slurp it)]
    (io/delete-file it)
    ret))
It isn't much more characters:
(and
  (.exists it)
  (do1
    (slurp it)
    (io/delete-file it)))

didibus22:09:49

But if you want to have a macro for it, ya go ahead, its just not a common idiom I'd say for other readers, since Clojure is mostly functional, you rarely need to do side effect after the result you want, resource cleanup is probably a common one, and for that like others have said, you probably want to use finally to be sure that the resources are cleaned up even on error and never leaks. And a lot of resources in Clojure will be Closable, which then can use with-open instead.

Benjamin12:09:31

thanks those answers helped me

Old account19:09:51

how to map on Hashmap values?

Ed19:09:22

If you want to apply a function to each of the values in a map, and return a new map, you can do something like this: (into {} (map (juxt key (comp inc val))) {:a 1 :b 2 :c 3})

Old account19:09:38

thanks but current code sample doesn't work

Ed19:09:40

Apologies ... I'm typing on my phone ... I've edited it

💪 2
didibus21:09:28

(map (fn[[k v]] (inc v)) {:a 1 :b 2 :c 3})
;;> (2 3 4)

didibus21:09:17

One thing which is good to learn early on though is that map is a sequence function, that means it always returns a sequence, it does not preserve the input type. So if you want to modify the values of a Map by some function and get a new Map back with the modified values on them you would need to from the sequence returned by map create a new Map out of it. You can do that with into like so:

(into {}
  (map (fn[[k v]] [k (inc v)])
       {:a 1 :b 2 :c 3}))
;;> {:a 2, :b 3, :c 4}

didibus21:09:42

But once you call into you are now being eager. While map being a sequence function is a lazy computation. Which is something to be aware of. So one thing you can do instead is stay in the land of lazyness until you need a Map back. Something like:

(let [m {:a 1 :b 2 :c 3}]
  (->> (vals m)
       (map inc)
       (map #(* 2 %))
       (map str)
       (zipmap (keys m))
       (into {})))
;;> {:a "4", :b "6", :c "8"}

didibus21:09:09

In 1.11 (which isn't out yet), there will be a update-vals function, which is a Map function, not a sequence function, and it applies a function and returns a Map directly and is always eager.

didibus21:09:37

Another approach is also to use reduce-kv.

(reduce-kv
  (fn[m k v]
    (assoc m k (inc v)))
  {}
  {:a 1 :b 2 :c 3})
;> {:a 2, :b 3, :c 4}

emccue19:09:30

clojure 1.11 has update-vals

✅ 2
👌 2
emccue19:09:19

but there are quite a few libraries which provide a map-vals or something similarly named