Fork me on GitHub
#beginners
<
2017-12-22
>
lee.justin.m07:12:45

when you see code like this

(defn mmap [m f a] (->> m (f a) (into (empty m))))
(defn complete-all [v] (swap! todos mmap map #(assoc-in % [1 :done] v)))
(defn clear-done [] (swap! todos mmap remove #(get-in % [1 :done])))
how do folks figure out what’s going on? There are at least 3 places where arguments are getting moved around here (`swap!`, the anonymous function, and the threading macro) and I am having a hard time following. is there a trick to tackling stuff like this?

lee.justin.m07:12:24

for what it is worth, the specific thing that I can’t figure out is that “1” in the assoc-in and get-in which seems like they would only be operative on one element in the provided sorted-map

seancorfield07:12:16

@lee.justin.m I think that code could be written in a much clearer way. As for the 1, you'll need to look at the data structure that's being used to how the todos -- based on the code above, I'd expect each todo to be a vector who's second element is a hash map that has an element done (a Boolean).

seancorfield07:12:31

A vector is associative on its indices, so (get [:a :b :c] 1) is :b

claudiu08:12:39

Hi. I've been using clojure for ~1year. Recently did an overview of clojurescript presentation at the local javascript meetup. One thing that was really strange is that people associate functional programming with haskell aka monads, category theory, pattern matching. etc.. Just wondering is it wrong if I say something like to me functional programming in clojure seems more similar to javascript (except for: implicit returns , laziness & immutability). than to haskell, elm & ocaml ?

claudiu08:12:32

Still a bit of a beginner, might be missing some things. But really don't see FP in clojure as being hard, or something to be afraid of...

vinai10:12:53

I now and then encounter functions named with a trailing * in other peoples sourcecode. Is this a common naming convention? What exactly does it mean? Is there some documentation somewhere?

vinai13:12:27

Thanks, yes, those I‘m familiar with. But on functions? And only a right ear muff? :)

jumar13:12:32

Ah, I see. In clojure.core, these often denote special forms (e.g. fn*, let*). I'm not sure about other meanings, though.

manutter5113:12:39

The single * at the end of a function name is a common convention used when you’re writing a function that’s only used by a macro or another function. For example, I might write foo that does something to a collection of records, but each individual record has some complicated processing to go through, so I pull out a foo* function that does the heavy lifting.

manutter5113:12:01

For coders like me who are too lazy to think up a better name for foo* 😉

diginomad12:12:09

hello,is this the idiomatic way of iterating over a collection and modifying any stack abstraction in Clojure?

diginomad12:12:34

or,are there any macros in std library that make this easier?

codonnell12:12:47

@diginomad without more information it's hard to say for sure, but from what you've posted I'd likely look at reduce

diginomad12:12:40

i guess i didn't give a specific example

noisesmith13:12:33

what that code shows is exactly what reduce does - at each step you have a new accumulated value (called s here) and you do something with the nth item of the collection

noisesmith13:12:03

in fact as written, that is identical to (reduce f stack collection)

noisesmith13:12:49

(ignoring the ... at least)

troglotit15:12:51

is there a way to start a repl with some installed packages without creating a project?

ghsgd216:12:54

Could install https://github.com/pallet/alembic, then install and load needed libraries in REPL

manutter5116:12:44

If you’re ok using CLJS instead of CLJ, you could check out planck or lumo

troglotit16:12:27

thank you! lumo worked for me :+1:

sayammasood16:12:32

Is writing command-line applications with Clojure a thing?

ghsgd216:12:43

@sayammasood You could do this too. It's in https://clojure.org/guides/deps_and_cli

sayammasood16:12:10

I mean, something like this but for Clojure:

admay16:12:32

@sayammasood, I think @ghsgd2’s comment was in regards to the REPL question. As for your question, yes, CLI’s in Clojure is absolutely a thing! What you’re looking for is tools.cli https://github.com/clojure/tools.cli

sayammasood19:12:39

Thanks @admay! Will definitely take a look.

admay16:12:45

This will provide for you all of your command line arg parsing needs

admay16:12:20

Check out the quick start section of the readme

sabbatical201719:12:07

I have a bunch of pairs [a b] and I would like to know the smallest b for each a. What would be an idiomatic way of doing this in clojure?

sabbatical201719:12:48

E.g., if I start with [[0 0] [0 1] [1 1] [1 2] [1 3] [2 2]], I would like to get {0 0 1 1 2 2]

noisesmith19:12:18

@sabbatical2017 my first inclination is to start with (group-by first ...), then use sort or min to get the smallest b out of each a key

noisesmith19:12:51

but it could all be done in one go using a reduce instead

noisesmith19:12:51

something like (reduce (fn [smalls [a b]] (update smalls a (fnil min b) b)) {} ...)

noisesmith19:12:26

I guess there's a few tricks in that which might be unfamiliar given this is #beginners, but feel free to ask for clarification

alexmiller19:12:24

min-key might help

sabbatical201719:12:53

@noisesmith @alexmiller Thank you very much! I verified that (reduce (fn [smalls [a b]] (update smalls a (fnil min b) b)) {} ...) works but it does not look too idiomatic to me as it looks very different from what I am trying to accomplish.

sabbatical201719:12:27

E.g., in datomic there is functionality where you can say something along the lines of :first (min :second) which will automatically do this.

noisesmith19:12:31

IMHO that reduce looks exactly like the thing you are trying to accomplish, and reduce is idiomatic

noisesmith19:12:24

then again datomic never clicked for me - I did a workshop with Stuart Sierra even and it just never made sense

sabbatical201719:12:12

FTR I also looked at min-key but was not able to contort it into a reasonable-looking working solution.

sabbatical201719:12:07

My best shot at this is (map-vals #(apply min (map second %)) (group-by first x))

sabbatical201719:12:45

Where I defined (defn map-vals [f m] (make-map (keys m) (map f (vals m))))

sabbatical201719:12:12

and (defn make-map [ks vs] (apply array-map (map vector ks vs)))

sabbatical201719:12:30

I tested that make-map and map-vals work

sabbatical201719:12:59

And, for the record I have (def x [[0 0] [0 1] [1 1] [1 2] [1 3] [2 2]])

flowthing19:12:13

You probably want to use Specter for stuff like that, if possible. https://github.com/nathanmarz/specter

sabbatical201719:12:34

But when I run that first form (with the x in it), I get CompilerException java.lang.IllegalArgumentException: No value supplied for key: [2 2],

noisesmith19:12:00

@sabbatical2017 the idiomatic version of make-map is (into {} x)

noisesmith19:12:37

oh wait you are splitting so (into {} (map vector x y))

sabbatical201719:12:29

@noisesmith Yes, thank you, (defn make-map [ks vs] (into {} (map vector ks vs))) definitely looks nicer! It also fixes the IllegalArgumentException.

noisesmith19:12:18

oh wait - that's zipmap - a builtin

sabbatical201719:12:16

@flowthing Thank you! I was also wondering about specter, but thought I'd try with plain Clojure first. Do you know enough specter to write down an idiomatic specter solution to this to compare?

sabbatical201720:12:36

@noisesmith Thanks, that's great! I thought that make-map looked like something that would be provided! I also thought that about map-values BTW 🙂

noisesmith20:12:11

there's a few versions of map-vals, I like (into {} (map (fn [[k v]] [k (f v)])) coll) (note that map is called with just one arg there)

scot-brown20:12:51

thread-last macro arguably makes group-by solution cleaner

(def pairs [[0 0] [0 1] [1 1] [1 2] [1 3] [2 2]])

(->> pairs
     (group-by first)
     (map (fn [[k keyvals]]
            [k (apply min (map second keyvals))]))
     (into {}))

noisesmith20:12:40

also the map / fn combo can be an arg to into, it doesn't need to be executed separately

scot-brown20:12:16

right, as a transducer.

flowthing20:12:25

@sabbatical2017: I'm definitely not particularly well-versed with Specter, but (specter/transform [specter/ALL] (partial apply min) [[0 0] [0 1] [1 1] [1 2] [1 3] [2 2]]) yields [0 0 1 1 1 2].

noisesmith20:12:59

isn't that just taking the min of each pair?

flowthing20:12:20

Hmm, yes, I misunderstood.

scot-brown20:12:44

output should be [0 0 1 1 2 2], not [0 0 1 1 1 2]

flowthing20:12:09

Yep, I see now. Let's see...

admay20:12:47

I know the whole map-vals and map-keys thing is controversial but does anyone know why they aren’t in clojure.core?

flowthing20:12:05

Well, I can't immediately come up with anything that's an improvement over the existing solutions.

flowthing20:12:13

@admay https://dev.clojure.org/jira/browse/CLJ-1959 has some discussion on the topic.

admay20:12:42

@flowthing I found that PR and that’s what made me curious. Alex said in his first response, “Rich will have his own opinion regardless.” That’s what I was most curious about