Fork me on GitHub
#beginners
<
2017-08-29
>
byron-woodfork01:08:04

Is there a more idiomatic way to handle this?

(mapcat #(clojure.string/split % #" ") ["I am groot"])

-> ("I" "am" "groot")
I don't want it to output a nested collection. So mapcat seems the way to go. Any better options?

Alex Miller (Clojure team)02:08:05

is there some reason that’s not sufficient?

Alex Miller (Clojure team)02:08:56

I guess maybe you don’t like having to wrap vector on the input?

Alex Miller (Clojure team)02:08:06

(clojure.string/split "I am groot" #" ") is basically same

bfabry02:08:13

I guess if that vector is only ever expected to be of length 1 then (clojure.string/split (first v) #" ") may be more intention-revealing. but if not what you have makes sense

Alex Miller (Clojure team)02:08:53

(into [] (mapcat #(clojure.string/split % #" ")) ["I am groot" "hey there"]) ;; transducer style

Alex Miller (Clojure team)02:08:53

not sure what you care about whitespace, but #"\s" uses Java regex pattern for whitespace, so will catch tabs etc

byron-woodfork02:08:35

I think it's just the idea of structuring a nested map using map and then destructuring it with cat. But I suppose that has to happen with any implementation really. Probably just overthinking it 😃

bfabry02:08:52

it's been a while since I've thought about them but I think the transducer version doesn't have that interim structure

nbardy03:08:35

Is there a function that splits a collection into two groups based on bool. e.x. [(filter coll) (remove coll)]

srihari04:08:34

@nbardy: is partition-by, or group-by what you want? (vals (group-by pred coll)) should be pretty close.

nbardy04:08:24

@srihari (vals (group-by pred coll)) does what I want. Wondering if there was a single function for it.

emilaasa10:08:19

I'm trying to learn the very basics of clojure now by reading some csv data, parsing it into a map and playing around with transforming it in different ways. What's a good clojurian way to filter a sequence of maps based on the values of a particular key? Data looks like this:

[{:a "" :b "foo"}, {:a "2", :b "bar"}, {:a "3", :b "baz"}]
And I only want to keep the maps where :a is not ""

sundarj10:08:32

(remove (comp empty? :a) [{:a "" :b "foo"}, {:a "2", :b "bar"}, {:a "3", :b "baz"}])

emilaasa10:08:57

That is very not close to what I was attempting haha 😄

emilaasa10:08:07

my brain hurts

sundarj10:08:29

sorry 😉

emilaasa10:08:19

Thanks a lot, now I'll just need a few hours to understand it 😄

danm10:08:43

You could also use the threading macros and the 'filter' function

sundarj10:08:31

keywords are functions that get the corresponding keys out of maps (:a {:a 2}) returns 2

sundarj10:08:18

remove is essentially (filter #(not ...) ...)

sundarj11:08:13

comp is for function composition. (comp empty? :a) is equivalent to (fn [m] (empty? (:a m)))

danm11:08:10

I have learned a thing 🙂

danm11:08:17

I wasn't aware of remove

sundarj11:08:52

handy, eh? 🙂

sundarj11:08:58

there's also complement, which creates the inverse of a predicate

danm11:08:18

Now I just have to find the few places where I have an awkward (filter #(not (...))) and replace it with remove 😉

sundarj11:08:43

hahah 😁

sundarj11:08:56

>>>((complement even?) 3) true

danm11:08:58

Or possibly #(filter (complement ...)), but either way

emilaasa13:08:18

Thank you guys a ton! I've started using the threading macro and comp seems super duper helpful

sundarj13:08:38

no worries :~)

donyorm12:08:03

So I'm getting this weird error where a specific "namespace is not found." However I have the namespace clearly defined and it works occaisonally (I'm doing this in emacs with cider if that matters). It especially works after I run cider-load-buffer (at least usually)

donyorm12:08:20

what would cause that/how to fix it?

gon12:08:07

@emilaasa use filter with a predicate that returns false when the :a value is ""

gon12:08:30

@emilaasa (filter #(not (empty? (:a %))) your-collection)

Keith12:08:42

I’m sort of confused about how cljs-http works in terms of Clojure data structures. I have two functions that are simply making an HTTP GET request, returning the body of the response, and attempting to print the first item in the response collection:

(defn get-http-result [path callback-fn]
  (go (let [response (<! (http/get (str js/location.pathname path)))]
        (callback-fn (:body response)))))

(defn get-available-commands []
  (get-http-result "/commands" #(prn (first %))))

pkova13:08:49

it seems like you're getting a string back as the body

pkova13:08:08

have you tried adding {:accept "application/json"} as the second argument to http/get?

Keith14:08:42

Hmm. I added what you said:

(defn get-http-result [path callback-fn]
  (go (let [response (<! (http/get (str js/location.pathname path) {:accept "application/json"}))]
       (callback-fn (:body response))))) 

Keith14:08:54

Unfortunately it doesn’t seem to have changed anything 😞

Keith14:08:31

I imagine I can reduce over the response and convert it to a vec of maps, but that seems highly unnecessary

Keith14:08:45

So I got it to work, but again, I feel like it’s highly unnecessary lol:

(defn get-available-commands []
  (get-http-result "/commands" #(println (first (js->clj (js/JSON.parse %)))))) 

Keith18:08:40

Never mind, I figured out what I was doing wrong. On the back-end, I was serving the response with a content type of “text/html” rather than “application/json”. Also, I changed my cljs-http using function to:

(defn get-http-result [path callback-fn]
  (go (let [{response :body} (<! (http/get (str js/location.pathname path)))]
       (callback-fn (vec response))))) 
Now the response is a Clojure vector containing Clojure maps.

Keith18:08:28

In case anyone else runs into this, this guide is what led me in the right direction: https://zaiste.net/posts/web_applications_in_clojure_all_the_way_with_compojure_and_om/

Keith13:08:28

The issue I’m running into is that (get-http-result "/commands" #(prn (first %))) seems to only print [ to the console log, rather than the first item in the collection.

Keith13:08:34

I guess should also say that the response body looks like this: [{"section":"System Status","command":"Media Files Uploaded","important":true},{"section":"System Status","command":"Temperature","important":false}]

Lucas Barbosa13:08:49

Are functions like swap!, alter and commute synchronous? I mean, do they block until completion?

leonoel13:08:15

they're synchronous, that is, the computation is guaranteed to be terminated when they return

leonoel13:08:01

but they don't really block the thread, they retry until the transaction is consistent

Lucas Barbosa13:08:55

@leonoel nice, thanks! Still related to causing side effects: What if I want to do something like a “Java business transaction”? Say that I have to send an email, put a message on a MoM queue and persist to a database. How do I prevent these from being repeated every time the transaction retries? If the answer to this is long, you can give me some terms or key-words for a research

leonoel13:08:28

you can use STM with agents, they support this use case

leonoel13:08:52

all sends to agents performed during the transaction will be delayed until the transaction is successful, so they will effectively happen just once

Lucas Barbosa13:08:09

Makes sense. The side effect will be caused on the async execution, right? Stretching this just a little bit more: what if the side effect fails? For example, what if the email server fails and the other two servers commit the changes? In Java EE (and in other places too, but I’m really used to Java EE) we have the two-phase commits to address this (not completely, but with an extra layer of certainty). Is there anything similar in Clojure or is this a simple matter of dealing with it manually?

leonoel13:08:21

there is no such mechanism out of the box, you will have to handle async errors manually

Chris Bidler13:08:34

I’d take a few minutes with the Google - unlike the Java EE world, the Clojure community is very much about composing smaller, specialized libraries to do what you want

Chris Bidler13:08:03

I’d bet someone has written a thing to abstract “handle async failures gracefully” and put it on Clojars, is what I’m saying.

Lucas Barbosa13:08:05

I see. Well, that is a very difficult problem, because if you try to be prepared for every possible failure, things will get complicated 😅

Lucas Barbosa13:08:52

@chris_johnson that is beautiful! I’m loving the idea of composing the solutions from small libraries that do one thing right, feels a lot like unix

Chris Bidler13:08:26

Welcome to the Clojure community! You’re going to like it here. 😄

donaldball14:08:16

@lvbarbosa I think many developers, and perhaps most clojurists, would look to publish a message to a durable topic and have e.g. the email system subscribe to that topic, instead of trying to ensure multi-party transactions resolve all effects synchronously

Lucas Barbosa17:08:24

@donaldball I see, makes more sense!

Keith18:08:40

Never mind, I figured out what I was doing wrong. On the back-end, I was serving the response with a content type of “text/html” rather than “application/json”. Also, I changed my cljs-http using function to:

(defn get-http-result [path callback-fn]
  (go (let [{response :body} (<! (http/get (str js/location.pathname path)))]
       (callback-fn (vec response))))) 
Now the response is a Clojure vector containing Clojure maps.

dadair20:08:05

Let's say I have a vector [a a a a a a a] where a is a gigantic map; does the vector contain 7 copies of a in memory? Or is the value of a cached and referenced for each (to reduce memory)?

chrisjd20:08:08

@dadair There will only be one instance of the map; this is one of the benefits of persistent collections. 🙂

noisesmith21:08:40

in this case I think the fact that java doesn’t have value types would ensure this anyway

dadair21:08:25

thanks, I assumed as much, wanted to confirm before propagating a huge map to a bunch of smaller datastructures as "metadata"

stvnmllr221:08:40

(defn setup-types [userid]
    (import-action-template 1 userid) 
    (import-action-template 2 userid) 
    (import-action-template 3 userid) 
    (import-action-template 4 userid))
I have a block like that, and inside the import-action-template, it runs some jdbc inserts using
(map #(write-to-db! %) list-of-maps) 
Only the last import actually runs the sql statements. Guessing it's something to do with laziness. Any ideas?

stvnmllr221:08:15

To answer myself, had to put a (doall) around the (map)

noisesmith21:08:44

or you can replace map with run! - doall is for when you still need the return value of map, and dorun is for when you need an argument arity that run! doesn’t support but don’t need the return value, and run! is for when you don’t need the return value and are only providing a function and a single collection as args

eggsyntax23:08:50

Terrific summary of those 🙂. I’d never thought of it in quite that way.

eggsyntax23:08:18

I’d love to see an example of dorun supporting a different arity; I’m not quite getting that.

noisesmith23:08:45

map supports arities that run! doesn’t

noisesmith23:08:57

I communicated that poorly

eggsyntax13:08:23

Oh, now I gotcha. Thanks!

stvnmllr221:08:29

thx. will check that out