Fork me on GitHub
#beginners
<
2021-03-27
>
seancorfield00:03:26

Another option is to use some:

user=> (some #(when-not (% nil) %) [int? zero?])
#object[clojure.core$int_QMARK_ 0x57dc9128 "clojure.core$int_QMARK_@57dc9128"]
user=>

seancorfield00:03:47

You need (when-not (% nil) %) to get the actual predicate back

aratare00:03:41

Hi there. Is there a way to do something like (? value preds) where it will run value through preds and return false if any of the preds fails and true otherwise? Thanks in advance.

seancorfield00:03:15

@rextruong Use every? like this:

user=> (every? #(% 42) [int? pos? even?])
true
user=> (every? #(% 43) [int? pos? even?])
false

aratare00:03:21

I just found every-pred 😅

aratare00:03:55

@seancorfield I’ve thought of that but just wondering if there was a “shorter” way to do it

seancorfield00:03:25

user=> ((every-pred int? pos? even?) 42)
true
user=> ((every-pred int? pos? even?) 43)
false

👍 3
seancorfield00:03:46

If you have a collection of predicates, you'd need (apply every-pred preds)

aratare00:03:18

Will do. Thanks a lot 🙂

finchharold05:03:53

Any carmine (redis) guys?

seancorfield05:03:30

@kishorekaranam99 I really don't think many people use Carmine. You've asked three times now in different channels and no one has responded. You asked on ClojureVerse and I told you we'd moved off it (and no one else answered).

👍 3
raspasov05:03:57

@kishorekaranam99 I’ve used it a very a long time ago but I’ve since made the conscious decision to avoid Redis as much as possible;

raspasov05:03:36

IMO it strikes a confused middle ground between a cache and a database. Not something I want to use.

raspasov06:03:46

If you just want a cache without any on-disk persistence, you can probably achieve very similar to Redis results by simply using a Clojure atom (or the STM if really needed) + the built-in Clojure data structures: maps, sets, vectors, sorted-maps; I’ve built a simple/naive in-memory “search” service that way; it’s very easy to achieve correctness + super fast. Of course, no on-disk persistence.

seancorfield06:03:02

We still use Redis very heavily but we gave up on Carmine, switched to Redisson (and Java interop) and then switched to plain old Jedis via Java interop (because Redisson was a HUGE dependency and was very slow to become compatible with Java 9+ as I recall? Or maybe it was one of Redisson's (many) transitive dependencies?).

seancorfield06:03:34

We use Redis for pub/sub and also the TTL key/value stuff, but Jedis is sufficient. We did write our own connection pooling logic for it because the built in one was problematic (in some way I don't remember -- my teammate could talk about that perhaps).

raspasov06:03:58

@seancorfield do you guys run on AWS or your own data center?

raspasov06:03:20

Or other Azure/Google/etc

seancorfield06:03:34

We run on a private cloud in a managed data center. So, not quite any of the above

seancorfield06:03:39

Historical reasons: we used EdgeWeb Hosting for years before I joined, we switched to Clojure and just stayed with them, and now they're part of DataBank so they have plenty of data centers and a lot of VM experience.

seancorfield06:03:57

(and we're very small so we have no in-house ops talent)

raspasov06:03:08

Understood; you mentioned pub/sub and I thought of AWS SQS; but if you’re not on AWS, it probably doesn’t make sense.

seancorfield06:03:24

Redis pub/sub is a thing 🙂

raspasov06:03:56

Yeah I know 🙂

Kenneth Gitere06:03:14

Hello Clojurians. I was trying to get back into Clojure and thought of just trying out reducers to get the average of a sequence of numbers. I saw online that the most elegant way to do so is:

(defn average [coll] 
  (/ (reduce + coll) (count coll)))

Kenneth Gitere06:03:39

Unfortunately this loops over the collection twice and wondered if there's anything better than this that is still elegant. Personally, my attempt was this:

(defn mean
                 [xs]
                 (->> xs
                      (reduce (fn [acc n] 
                                {:total (+ n (acc :total))
                                 :count (inc (acc :count))})
                              {:total 0 :count 0})
                      (vals)
                      (apply /)))

seancorfield06:03:56

(defn mean [xs]
  (let [[total count] (reduce (fn [[t n] x] [(+ t x) (inc n)]) [0 0] xs)]
    (/ total count)))
How about that @gitere81?

Kenneth Gitere06:03:12

Thanks @seancorfield! This does omit the extra call to vals from before. I guess the function given to reduce can be extracted to its own defn for readability.

raspasov06:03:31

@gitere81 one more alternative using a library and transducers: https://github.com/cgrand/xforms

(transduce
  (map identity)
  net.cgrand.xforms.rfs/avg
  [2 4 6])
=> 4

👍 3
seancorfield06:03:32

BTW, if your xs is a vector rather than a sequence then count is O(1) because vectors know their size so it doesn’t “loop over the collection”

seancorfield06:03:23

So your initial (defn average [coll] (/ (reduce + coll) (count coll))) is pretty efficient if coll is a vector.

👆 3
Kenneth Gitere06:03:46

Alright. I assumed all sequences have their count lazily evaluated.

seancorfield06:03:42

Sequences generally, yes. Collections, not always.

seancorfield06:03:31

Have a read of this https://clojure.org/reference/data_structures which talks about performance guarantees of different collections. Also https://clojure.org/reference/sequences which talks about sequences.

seancorfield06:03:51

TL;DR: collection = concrete, sequence = abstract.

👍 3
raspasov06:03:57

@gitere81 (let [s (repeat 1000000 0) v (mapv identity s)] (time (count s)) (time (count v)))

raspasov06:03:13

“Elapsed time: 444.196333 msecs” “Elapsed time: 0.013042 msecs” 1000000

Kenneth Gitere06:03:26

This is probably the first time I've seen identity being used lol. Again, I have barely scratched the surface of Clojure

raspasov06:03:11

🙂 It’s literally

(defn identity
  "Returns its argument."
  [x] x)

seancorfield06:03:52

Yup, identity is one of those weird things that you think “What? Why would anyone want a function that does nothing?” and yet it turns out to be quite useful in functional programming.

seancorfield06:03:55

(we have about 150 instances of identity in our codebase at work)

raspasov06:03:55

I really prefer sticking to vectors/collections as much as possible unless there’s a specific need to use sequences; Easier to reason about IMO

raspasov06:03:20

mapv is my friend 🙂

Kenneth Gitere06:03:55

😂 I will try to find more situations to use mapv when I can

Kenneth Gitere06:03:25

Also, do Slack code blocks allow for language specific formatting?

raspasov06:03:31

Or transducers:

(into [] (map identity) (repeat 1000 0))

raspasov06:03:19

(MacOS) Cmd + Shift + C … still feels clunky sometimes but it works most of the time

raspasov06:03:39

Not sure about lang. specific

seancorfield06:03:39

You can add “Code snippets” which I think have some language-specific formatting but most folks just use plain triple-backticks…

seancorfield06:03:46

Snippet using Clojure formatting

seancorfield06:03:17

(I typed /snip and it offered a code snippet option)

raspasov06:03:18

Ha, that’s good to know 🙂 Even if I’m not a fan of the color scheme

seancorfield06:03:12

(you have to tell it to use Clojure — it doesn’t auto-detect it)

raspasov06:03:29

Yup, just tested that hah 🙂

hindol07:03:58

In this page https://clojure.org/news/2021/03/18/apis-serving-people-and-programs, it says, > For example, a function that takes a sequence and optional keyword arguments and returns a vector containing the values is defined as:

(defn destr [& {:keys [a b] :as opts}]
  [a b opts])

(destr :a 1)
->[1 nil {:a 1}]

(destr {:a 1 :b 2})
->[1 2 {:a 1 :b 2}]
But where is the sequence here? Is this a typo?

seancorfield07:03:13

It’s a typo: it should say takes a sequence of optional keyword arguments

seancorfield07:03:58

Since it’s come up a couple of times, I just submitted a PR to fix it.

👍 12
hindol08:03:46

Can we use doto with an unknown number of arguments? E.g. for every key value pair in a map, call a Java setter with the key and the value as two arguments.

teodorlu08:03:34

I think I might use doseq for that. https://clojuredocs.org/clojure.core/doseq

👍 3
hindol09:03:00

Yeah, doseq seems to be the best way.

hindol22:03:30

Looks like java.data has some nice facilities. to-java function can take a map and use it to call setters. Looks a little like magic though. Good to know this library exists. Thanks.

teodorlu22:03:23

There isn't actually too much magic to it. clojure.lang.IPersistentMap is an interface, and you can implement that interface on your own types. Here's an example: https://gist.github.com/david-mcneil/1684980#file-custom-clojure-map-clj-L78-L116 (maps also implement java.lang.Iterable, clojure.lang.Associative, clojure.lang.IPersistentCollection, clojure.lang.Seqable and clojure.lang.ILookup. Caveat: this is still new to me, but I think this is correct)

seancorfield23:03:59

@hindol.adhya It’s based on Java’s built-in Reflection APIs so it’s as “magic” as that 🙂

👍 3
andarp12:03:11

I’ve learned the basics of Clojure and liking it a lot. But now I want to understand the tools surrounding it. I’m thinking to just start a simple empty folder and learn deps.edn with the clj tool, doing everything manually step by step to understand. Is that a good approach? Any suggestions on good books/guides/articles covering the Clojure tools in that manner?

Dimitar Uzunov12:03:55

Well in its simplest deps.edn is just a hash map containing two important keys :paths and :deps which have the values of respectively a vector of paths to your source code and other resources relative to your deps.edn file and a hashmap containing your dependencies, I guess the latter is a bit tricky

Dimitar Uzunov12:03:09

{:paths [“src”] :deps {cheshire/cheshire {:mvn/version “5.10.0"} org.clojure/data.json {:mvn/version “1.0.0”} org.clojure/data.csv {:mvn/version “1.0.0"}}}

Dimitar Uzunov12:03:23

I think you can go pretty far with just that

Dimitar Uzunov12:03:23

in clojars you can even copy and paste the syntax for the dependencies: i.e. https://clojars.org/io.randomseed/bankster

Dimitar Uzunov12:03:25

then you need to put require forms in your ns declaration on the top of your file

Dimitar Uzunov12:03:06

oh and running is just clj while in the same directory to pull the dependencies

Dimitar Uzunov12:03:27

and clj -m <namespace containing your main function> to run your app

Dimitar Uzunov12:03:33

the above is the tl;dr you can always also read https://clojure.org/guides/deps_and_cli for more details

andarp13:03:33

@dimitar.ouzounoff Thank you! I know most of this, more or less, but I think what I’m missing is the “why” - why did clj and deps come about, what are they replacing, what context does the clj tools exist in in general? Etc. I think I just want to make up for my complete lack of experience of the clj and jvm world...

Dimitar Uzunov13:03:52

before tools.deps there was leiningen which is a more traditional build tool

Dimitar Uzunov13:03:30

build tools traditionally are separate tools from the language - like for example when writing C you will use a Makefile - a totally different, declarative language to build your program

sova-soars-the-sora18:03:00

would identity be similar to this in other langs?

ghadi18:03:45

not really - identity is a function, this is a reference/object

☝️ 3
sova-soars-the-sora18:03:12

maybe i just need to see more examples of identity

sova-soars-the-sora18:03:48

this would be a big ball of spaghetti with methods and variables... identity just "returns its argument" ... okay i think that makes sense... thank you @ghadi

seancorfield19:03:10

@sova Something I’ve used it for in the past: (into {} (map (juxt :id identity)) data) to turn a sequence of hash maps containing :id into a hash map indexed by the ID (whose values are the original hash maps).

seancorfield19:03:08

dev=> (into {} (map (juxt :id identity)) [{:id 1 :foo "bar"} {:id 2 :a 1 :foo "quux"}])
{1 {:id 1, :foo "bar"}, 2 {:id 2, :a 1, :foo "quux"}}

😮 3
ccortes19:03:15

I've used it to "recycle" functions, in this case I wrote a function to get a particular element inside the payload from the body of a http response:

(defn response-element
  "Gets the desired element from the payload coming from a http response"
  [response element]
  (-> response
      :body
      (json/read-str :key-fn keyword)
      :payload
      element))
So I used it like this: (response-element response :some-key) . Later I found out that sometimes I needed the whole payload and instead of making a new function I could use the same one using identity : (response-element response identity) . It's a nice thing that keywords are functions too.

metal 3
Azzurite22:03:19

I'm pretty sure I've come across a very easy way to convert a vector like this: [:a 1 :b 2] to a map like this {:a 1 :b 2}... how do I actually do it though? can't figure it out anymore

seancorfield22:03:49

(apply hash-map [:a 1 :b 2])

Azzurite22:03:34

yep, thank you very much! 🙂

seancorfield22:03:36

If you have a sequence of key/value pairs like [[:a 1] [:b 2]] you can use (into {} [[:a 1] [:b 2]])

Azzurite23:03:08

what should you do if you have a nice chain with 5 forms where -> works perfectly, but then have one in the middle where you'd need ->>? some stupid thing like (#(func arg1 arg2 %))?

walterl23:03:25

(-> my-input
    form1
    (->> map identity)
    form2)

3
3
☝️ 3
R.A. Porter00:03:40

I usually mutter under my breath and then use as->

walterl00:03:41

For cases where the threaded values somewhere in the middle of the args, yeah

seancorfield02:03:04

If the form should go in the last position, ->> in a -> pipeline is the “correct” thing. It the form should go elsewhere, then as-> might be right. But: https://stuartsierra.com/2018/07/06/threading-with-style (and read all his other do’s and don’ts).

🎉 9