Fork me on GitHub
#beginners
<
2019-09-10
>
valerauko04:09:31

i thought it was the way middleware are layered like an onion (or rings in 2d)

Daouda11:09:53

Hey Folks, I need your help to understand how commute work on ref?

zygon413:09:57

It's the same as the other api alter? but it's faster, there are less/no ordering guarentees

alexmiller13:09:01

"faster" sounds like latency, but the benefit is really greater concurrency (due to fewer constraints/conflicts), which will typically give you better transaction throughput

mloughlin15:09:07

what's the best way to check equality on a specific list value (I'm filtering the result of partition-by)? I feel like #(= '(1) %) is not the way

mloughlin15:09:38

(I'm actually filtering for not equals)

alexmiller15:09:43

sequential collections are equal regardless of coll type. so (= '(1) [1]) in case that makes anything simpler

mloughlin15:09:46

good to know, I'll keep doing what I'm doing. Thanks Alex

fappy17:09:48

hi all! some? is an interesting name for a function. Can someone explain why that's the name for that particular function instead of not-nil? or non-null?

fappy17:09:21

using (def not-nil? (comp not nil?)) might read more like plain English ... but using a built-in that already exists seems like it should be the way to go ...

alexmiller17:09:41

It’s like math notation, “there exists some x, such that ...”

Daouda17:09:13

I understand that commute is faster than a`alter` but what I would like to know is how commute get to be fast? What does it do and how to make it faster? I read that it doesn't retry transaction and use intransaction-value. To be honest I don't know what that mean

alexmiller17:09:15

ok, again, it's not faster, it's greater concurrency

alexmiller17:09:30

transactions track all of the refs used in the transaction

alexmiller17:09:54

if two transactions happen at the same time and write the same refs, one fails and must be retried

alexmiller17:09:18

commute means you don't fail the transaction in this case, you go ahead and complete both (in some undefined order)

alexmiller17:09:32

the canonical example is that commute updates a stat counter

alexmiller17:09:57

you don't care in which order two transactions update it - they can both inc the counter and your stats are still correct, and no transactions fail

johnjelinek18:09:48

can this be written as a (def predicate instead of a (defn?

(defn is-sleeping? [state players]
   (and (= "night" (:time-of-day @state))
        (remove is-werewolf players)))

hiredman18:09:40

Yes, but it will terrible, also you should wrap seq around that call to remove

hiredman18:09:16

Seq functions may return the empty seq instead of nil

johnjelinek18:09:22

@hiredman: I was wondering how to do that, thanks!

johnjelinek18:09:33

hmm

(defn is-sleeping? [state players]
   (and (= "night" (:time-of-day @state))
        (into () (remove is-werewolf players))))
tried (into and (seq and still returns false when there's nil instead of ()

andy.fingerhut19:09:35

Not sure why you want to use into there?

user=> (if (and true (into () (remove even? [2 4 6]))) "truthy" "falsey")
"truthy"

chepprey19:09:43

user=> (remove even? [2 3 4])
(3)
user=> (remove even? [2 4])
()
user=> (seq (remove even? [2 3 4]))
(3)
user=> (seq (remove even? [2 4]))
nil
user=> 

andy.fingerhut19:09:28

With or without the into, that is-sleeping? will return a value that is treated as logical true in an if or cond condition.

andy.fingerhut19:09:37

it cannot return a value that is treated as logical false.

johnjelinek19:09:37

oh -- I think it's that and that is making it true/false instead of ()

andy.fingerhut19:09:05

user=> (if () "truthy" "falsey")
"truthy"

johnjelinek19:09:08

@hiredman: what is the terrible way to write the predicate as a (def?

andy.fingerhut19:09:52

In an if or cond conditional expression, if it evaluates to false or nil, it is logical false. Any other value returned by that expression, including (), is treated as logical true.

hiredman19:09:15

A combination of partial, comp, and juxt

hiredman19:09:38

But it is bad, don't do it

hiredman19:09:47

It is less readable, and breaks useful features like being able to redefine functions

hiredman19:09:17

Point free style is not good

andy.fingerhut19:09:42

and does not change values of its subexpressions. It uses the same rules that if and cond do to separate out the falsey from the truthy, and returns them unchanged, including ().

andy.fingerhut19:09:00

I guess we might be misinterpreting how you want to use your function is-sleeping?, because it is a (not enforced) convention that functions ending with ? return true or false. Perhaps your intent is different for that function?

Ewan Valentine19:09:24

Does anyone have any examples of routing PUT/PATCH requests to Compujure, using hiccup.forms?

(PATCH "/update/:id" [id completed] (update-task id completed)))
I have a handler defined like that, and my form has a hidden _method field automatically with PATCH. But it's showing up as POST which 404's. Much appreciated!

hiredman20:09:19

The _method thing is a work around some frameworks support

hiredman20:09:48

It is not part of html/http or universally implemented

David Pham20:09:39

It is not really about a beginner Clojure question per se, but I had a weird experience today as a beginner clojure dev and I don’t really know where I could share, but desired to get some feedback. To put in context, I decided to take the risk of building our front-end tools in our team (60 persons) in my company using CLJS in January (I am mathematician/statistician working in a bank), and the feedback has been overwhelmingly positive (i.e. most don’t understand how I create the tools, but they want me to continue the good work). However, during lunch, I share my experience with another team which is technically much more experienced (typical Java, Scala, C++ skill set for AI) and I have been belittled because I was using Lisp at work. Usually, I don’t really care, but this time I really wondered if I were doing something wrong. I strongly believe Clojure and ClojureScript are amazing instruments to make effective stuff and I have tons of fun, but I got my first doubt today. I wondered if anyone had a similar experience?

dpsutton20:09:39

user reports outweigh developer language arguments 100%

ghadi20:09:55

makers always win... enjoy the hate

manutter5120:09:43

By the time it’s compiled and run, Clojure is just JVM bytecodes same as any other JVM application, so prejudice against Clojure just because it’s a Lisp is definitely misplaced. Clojure is amazingly good at writing industrial strength enterprise applications.

mzavarella20:09:05

hey all, if I had a vector of vectors like, [[1 2] [3 4] [5 6] [7 8] ... ] how can I go about combining them such that every two vectors are merged together resulting in [[1 2 3 4] [5 6 7 8] ... ] and so on?

mzavarella21:09:06

Looks like (map flatten (partition 2 [[1 2] [3 4] [5 6] [7 8]])) gets it done. I think I was just overthinking it and assuming that clojure had a built in function to do that

Toby Clemson21:09:26

Or (partition 4 (flatten [[1 2] [3 4] [5 6] [7 8]])) I think

hiredman22:09:04

Never use flatten

hiredman22:09:08

because flatten is indiscriminate (and flattens anything) and at some point you will have a list of lists of lists where you only want two levels collapsed

hiredman22:09:08

flatten is lazy, not in the evaluation sense, but on the part of the programmer, oh, I am not sure what the structure of this thing is, but I'll just bash it with this hammer and maybe I'll get what I want

hiredman22:09:12

you can think of tree traversal (nested lists are trees) as a sort of balanced equation, as you descend levels you remove nesting from one side and add context information to the other (context information may just be stack depth, etc). flatten is "meh, I don't need that information" which are famous last words

borkdude22:09:27

(I do use concat, just something to consider)

hiredman22:09:34

yeah, well, don't use concat like that

borkdude22:09:36

(mapv #(apply into %) (partition 2 [[1 2] [3 4] [5 6] [7 8] [9 10] [11 12]]))
[[1 2 3 4] [5 6 7 8] [9 10 11 12]]

borkdude22:09:09

or just plain old loop:

(def vecs [[1 2] [3 4] [5 6] [7 8] [9 10] [11 12]])
(loop [[fst snd & rst] vecs, res []]
  (if fst (recur rst (conj res (into fst snd)))
    res))
;;=> [[1 2 3 4] [5 6 7 8] [9 10 11 12]]

hiredman22:09:36

(into []
      (comp (partition-all 2)
            (map (fn [[a b]] (into a b))))
      [[1 2] [3 4] [5 6] [7 8] [9 10] [11 12]])

borkdude22:09:56

nice transducer solution 🙂

hiredman22:09:41

I feel like there is something that could be done with cat

borkdude22:09:08

user=> (map into (take-nth 2 vecs) (take-nth 2 (rest vecs)))
([1 2 3 4] [5 6 7 8] [9 10 11 12])

borkdude22:09:41

(I know, not the best solution complexity-wise)

Ewan Valentine22:09:14

Another noob question, I have a function, I'm passing in a hash map and a single integer value, I need to destructure the hashmap and add in the integer:

(defn update [id task]
  (sql/query spec ["update todos set title = ?, completed = ? where id = ?"
                   (:title :completed task)]))
So I need to fit id in there, but I can't figure it out

andy.fingerhut22:09:29

task is a Clojure map with keys :title and :completed, and perhaps others?

andy.fingerhut22:09:59

Because if so, evaluating (:title :completed task) will likely give an error, or at least not what you want.

andy.fingerhut22:09:07

Or else sql/query is a macro that I have never seen before, so do not know what it does with that expression.

Ewan Valentine22:09:49

Yes it's a map, that's correct

andy.fingerhut22:09:53

Wrapping the body of the function with (let [task-with-id (assoc task :id id)] (sql/query ...)) might be close.

Ewan Valentine22:09:16

I'll give that a go 🙂

andy.fingerhut22:09:32

then use task-with-id in the body where you currently have task

andy.fingerhut23:09:21

I am not sure about the sql/query part, but perhaps where you currently have (:title :completed task) you instead want (:title task-with-id) (:completed task-with-id) (:id task-with-id) ?

Ewan Valentine23:09:07

I'll give that a go, I am getting a weird Postgres error, that could be why

dpsutton23:09:50

three ? require three parameters. you're only passing one and its the entire task object which won't work

dpsutton23:09:32

(:keyword :access {:a :b}) #_>> {:a :b}

andy.fingerhut23:09:40

Yeah, it is a little bit unusual why this is the case, but (:title :completed task) returns task

Ewan Valentine23:09:36

I thought I was destructuring it, but it was a misunderstanding of how destructuring worked basically

Ewan Valentine23:09:24

I come from a Go background, so a lot of this is very, very... new to me haha 😅

andy.fingerhut23:09:20

I believe this would be a correct use of Clojure destructuring on the map named task:

(defn update2 [id task]
  (let [{:keys [title completed]} task]
    (sql/query spec ["update todos set title = ?, completed = ? where id = ?"
                     title completed id])))

andy.fingerhut23:09:42

No need to create a different version of the map in that case.

Ewan Valentine23:09:07

Ahaaa that makes sense, that's pretty neat

Ewan Valentine23:09:16

Thanks both, much appreciated 🙂

jaihindhreddy23:09:01

Destructuring also works in function args BTW...

(defn update3 [id {:keys [title completed]}]
  (sql/query spec ["update todos set title = ?, completed = ? where id = ?"
                   title completed id]))

Ewan Valentine23:09:34

Even more succinct! Love it, great suggestion, that's really handy

jaihindhreddy23:09:48

It is good to keep in mind though that in that example, I lost the name task when destructuring it which might have served as documentation. The function is called update and we implicitly understand that it updates a task because of the arg name, which is gone now. Because of this, it is many times better to just destructure in an inner let, or retain the name task like this:

(defn update3 [id {:keys [title completed] :as task}]
  (sql/query spec ["update todos set title = ?, completed = ? where id = ?"
                   title completed id]))