Fork me on GitHub
#beginners
<
2018-12-12
>
sova-soars-the-sora01:12:11

how can i get paginated results of an atom?

sova-soars-the-sora01:12:59

for example, maybe i have an atom of maps that each have a :timestamp 10110 key with unique timestamps. i'd like to sort them, and i'd like to return the first 20, and then on another request the next 20 (21-40) and then again the next 20 (41-60) etc

noisesmith01:12:02

the atom part doesn't really effect any of this at all (partition-all 20 (sort-by :timestamp @foo))

noisesmith01:12:21

to split it between requests you can combine that with drop

sova-soars-the-sora01:12:00

please elaborate 😄

noisesmith01:12:34

user=> (nth (partition-all 20 (range)) 10000)
(200000 200001 200002 200003 200004 200005 200006 200007 200008 200009 200010 200011 200012 200013 200014 200015 200016 200017 200018 200019)

noisesmith01:12:55

that's the 10000th group of 20 results from the list of all numbers starting at 0

dpsutton01:12:17

Hiredman put it very well one time. Atom is an interface with two operations: swap and reset. Your question is orthogonal to the use of an atom

sova-soars-the-sora01:12:02

i suppose. i just equate atom with "where the data lives" which is not 100% accurate but in practice it is applicable

dpsutton01:12:37

You just care that you have some sequential thing. The atom says nothing about what it contains

mpcarolin03:12:07

Why should I prefer using partial over an anonymous function? For example:

#(+ 5 %)
vs
(partial + 5)
The former is more succinct and arguably clearer to me, given that the % symbol communicates the expectation of an argument as well as its location.

mpcarolin03:12:57

I ask because [this](https://github.com/bbatsov/clojure-style-guide) style guide states:

mpcarolin03:12:11

;; good
(map #(+ 5 %) (range 1 10))

;; (arguably) better
(map (partial + 5) (range 1 10))

Alex Miller (Clojure team)03:12:32

I’d say that’s in dispute. I think everyone on the core team would tell you anonymous functions are preferred to partial.

👍 8
seancorfield03:12:18

One difference to be aware of is the effective arity of the resulting function

user=> (#(+ 5 %) 1 2 3 4)
ArityException Wrong number of args (4) passed to: user/eval149/fn--150  clojure.lang.AFn.throwArity (AFn.java:429)
user=> ((partial + 5) 1 2 3 4)
15
user=>

👍 4
seancorfield03:12:06

To do the same with an anonymous function, you would need (#(apply + 5 %&) 1 2 3 4)

8
mpcarolin03:12:01

Interesting, I did no consider cases like that. There partial does seem preferable

mpcarolin03:12:23

Thanks for the responses!

yuhan03:12:54

Is there any performance difference between the two approaches to be aware of? Coming from a bit of ML background I tend to use partial more often, surprised to hear that anonymous functions are preferred by the core team

seancorfield04:12:31

I very much doubt that any performance differences are worth worrying about.

seancorfield04:12:56

If you have performance-critical code, you're more likely to be focused on boxed vs primitive math and vector vs primitive array and stuff like that I suspect.

stardiviner03:12:49

I got a weird problem in my simple crawler code https://github.com/stardiviner/clojurians-slack-log/blob/master/src/clojurians_slack_log/core.clj#L87 . Here is the problem function. When I use CIDER debug, it can spit text to file, but when I execute code to invoke function as below the function definiction in (comment ..) block, it has no content write into file. Don't know how to figure this out. Can someone check my code out to help me?

seancorfield04:12:39

@stardiviner Most likely because you're using map for side-effecting functions. You either want doseq or run! here.

seancorfield04:12:00

map is lazy so it won't actually do anything unless something forces the result to be realized all the way through (which CIDER debugging is doing, I'm sure). But your -main won't do anything because nothing will cause that map call to be realized.

seancorfield04:12:15

As written, channel-messages won't do anything when run (unless you're forcing realization via CIDER debugging). You have a call to map, and then a call to prn. So that entire map expression will essentially be thrown away and not realized, because only the last expression in a function (or in a let) is returned and only that value could be realized (by a caller).

stardiviner04:12:08

I'm start project REPL to testing. Let me take a try.

stardiviner04:12:55

@seancorfield I changed code into this

clojure
(defn channel-messages
  "Extract all message a in channel's all dates and write into channel-named file." 
 [{channel-name :name channel-url :url}]
  (map #(doseq [message (channel-date-log %)]
          (let [filename (str "log_files/" channel-name ".txt")]
            (io/make-parents filename) 
           (spit filename
                  (compose-message message)
                  :append true) 
           ;; FIXME: debug spit every message?
            (prn "spit: ..")))
       (map :url (channel-log-dates channel-url))) 
 (prn (format "Channel [%s] exported." channel-name)))
Still does not work.

stardiviner04:12:21

Maybe I should use (type ..) around all (map ) forms to know which one return a lazy sequence, then I need to use doseq or run! on it?

noisesmith15:12:45

map is always lazy, there's no need to check the type. It gets forced if something else consumes its result. That's the only variable.

seancorfield04:12:37

Your outer map will not be realized.

seancorfield04:12:28

If you change both map on the first line of channel-messages to run! -- and the map in -main to run!, that should work.

seancorfield04:12:22

So it will become (run! #(run! (fn [message] ...

seancorfield04:12:50

And in -main: (run! channel-messages (all-channels))

stardiviner04:12:50

Use your advice, the code works now. How can I avoid this trap in future? Because when I write the code, I have not even realized this lazy sequence probelm.

seancorfield04:12:32

map is always lazy. If you are trying to do side-effects, it is always the wrong function.

seancorfield05:12:40

for is also lazy, so watch out for that too.

seancorfield05:12:39

Lots of Clojure core functions are lazy -- always check the docstrings!

stardiviner05:12:36

@seancorfield What about doall? It is better?

pradyumna06:12:24

hi all, is there any library on server-side, which kind of provides a single store with subscriptions with deduplication

nikola08:12:14

I have a function that removes an element from a list based on some criteria, it returns the same data structure, so I can chain multiple function invocations like (remove :el (remove :el2 list)). I now would like to re-use the function with a list of elements to remove. How can I achieve that?

nikola08:12:10

recur comes to mind, but is there a smarter way?

Ali Ebadian08:12:47

Is there a learning Clojure by examples video/tutorial/blog or everything else you can recomend?

nikola08:12:30

In the function itself, I use filter, but it takes a single element, not multiple.

nikola08:12:34

I'll try to tweak the function to take a list to remove, so it will work with one or many

schmee08:12:37

something like this maybe?

user=> (defn remove-elems [list elems] 
         (remove #(contains? elems %) list))
#'user/remove-elems
user=> (remove-elems [:a :b :c] #{:a :c})
(:b)

👍 4
pavlosmelissinos08:12:43

I'm not sure I understand filter takes two parameters, a filter function that returns boolean and a collection (filter pred coll)

pavlosmelissinos08:12:31

pred is applied to each element in coll

pavlosmelissinos09:12:39

and if the result is true then the element is kept, otherwise it's not included in the output

pavlosmelissinos09:12:42

So, if you write a function to replace pred with, you don't need to mess with single vs multiple elements

pavlosmelissinos09:12:03

What am I missing?

nikola21:12:20

thank you for your ideas, I didn't know remove existed. I should just read the complete documentation 😂

Ali Ebadian08:12:59

@nikola I started with that but as i am retard i need to do till ii learn

pavlosmelissinos08:12:47

@ali.ebadian clojure koans is perfect for absolute beginners: https://github.com/functional-koans/clojure-koans and if you don't want to setup anything locally there's http://clojurescriptkoans.com/ (not 100% up to date with clojure-koans master)

Ali Ebadian10:12:16

Hey all again, here with another basic silly question, why do we use [] with let?

Ali Ebadian10:12:41

We dont use them for bindings in def

pavlosmelissinos10:12:17

def defines a single binding, right? let can handle multiple pairs, so it has to know where the binding block ends

Ali Ebadian10:12:41

Oh I didnt know it does multiple onse, ive only seen it used to bind one thing

Ali Ebadian10:12:07

(let [s "Something" "someting else"])

pavlosmelissinos10:12:55

(let [s "something" t "something else"])

pavlosmelissinos10:12:02

"key" "value" pairs

Ali Ebadian10:12:25

oh thats what I meant

pavlosmelissinos10:12:30

usually written as such in order to be easier on the eyes:

(let [s "something"
      t "something else"])

Ali Ebadian10:12:16

btw did this prefix notation made life make sense to you too?

Ali Ebadian10:12:35

its fucking fantastic

pavlosmelissinos10:12:00

yeah it is!

👍 4
Ali Ebadian10:12:17

Once I master this thing I am so quiting Java

4
jaihindhreddy-duplicate10:12:05

^ Along with the prefix notation, the fact that LISPs define their syntax as interpretation of data structures instead of text is decidedly simpler IMO.

pavlosmelissinos10:12:22

Is anyone else slightly frustrated with put off by the inconsistencies of threading?

pavlosmelissinos10:12:57

(-> "sTring" lower-case) -> "string" (-> "sTring" (lower-case)) -> "string" (-> "sTring" @#'random-private-function) -> arity exception (-> "sTring" #(clojure.string/lower-case %)) -> CompilerException (-> "sTring" (#(clojure.string/lower-case %))) -> "string"

nitaai11:12:20

I understand your point. But keeping in mind that it is a macro (and understanding what it does), it is not inconsistent then.

pavlosmelissinos11:12:57

Yeah I think I understand why you must enclose anonymous functions and private functions in parentheses but parentheses being optional in some cases and mandatory in others is still inconsistent behaviour. Not sure it could/should be "fixed". I can live with it obviously but it's one of my first pet peeves with clojure! 😄

nitaai12:12:51

“…but parentheses being optional in some cases and mandatory in others is still inconsistent behaviour.” yes, you are right. you are welcome!

noisesmith15:12:03

threading is not a semantic operation, it's a syntactic one. Thanks to this fact you can do very strange things with thread forms.

user=> (->> (+ a b) (let [a 10 b 31]))
41

👍 8
pavlosmelissinos11:12:19

Just me then? ok 😛

bronsa11:12:56

what inconsistencies?

bronsa11:12:17

those are not inconsistencies

bronsa11:12:29

that's how -> should work

bronsa11:12:36

if you macroexpand you'll understand why

bronsa11:12:43

-> is merely syntax transformation

bronsa11:12:35

what you're ignoring here is that #() expands to (fn [..] ..) and @x expands to (deref x)

bronsa11:12:54

(and #'x expands to (var x))

bronsa11:12:37

so you see how (-> foo @x) doesn't translate to (@x foo), but to (-> foo (deref x)) and then (deref x foo)

bronsa11:12:43

same for #()

pavlosmelissinos11:12:14

I see, that clears things up a lot, thanks

pavlosmelissinos11:12:42

However, from the user's perspective, sometimes parentheses are optional and in other cases they are mandatory

bronsa11:12:56

from the perspective of a user who misunderstands the semantics of clojure :)

😆 4
bronsa11:12:13

but then you could say lots of things are confusing from that perspective

pavlosmelissinos11:12:22

Yeah, I suppose 😛

bronsa11:12:06

(don't take it personally, this confused me too when I was starting out!)

pavlosmelissinos11:12:51

Is there a point to not having macros like these expand to expressions with enclosing parentheses?

bronsa11:12:15

what do you mean?

bronsa11:12:06

automatically doing (-> foo #(..)) -> (-> foo (#(..)))?

bronsa11:12:30

you couldn't do it in the general case

bronsa11:12:52

as that would mean also transforming (-> foo (baz bar)) -> (-> foo ((baz bar)))

bronsa11:12:07

unless you only special cased (var ..) and (fn* ..) say

bronsa11:12:15

but then, if somebody did want to thread into those it wouldn't be able to

bronsa11:12:34

and you just made -> less consistent and predictable

bronsa11:12:54

does that make sense?

pavlosmelissinos11:12:27

Yeah, I meant the "problematic" cases specifically, not the general case

bronsa11:12:49

but everybody has a different idea of what "problematic" cases should be

pavlosmelissinos11:12:58

(e.g. anonymous and private functions)

bronsa11:12:06

if you ask an expert clojure developer what you think of "problematic" cases are not

bronsa11:12:19

and your proposed behaviour would be considered inconsistent and special-casey

bronsa11:12:49

you have to understand that macros are just synctactic transformations

bronsa11:12:05

what you are proposing would require semantic understanding of the code being transformed

bronsa11:12:16

and introduce a lot of ambiguities

bronsa11:12:30

the current behaviour is consistent, generic, predictable

bronsa11:12:08

and what a non beginner would expect and not find surprising :)

pavlosmelissinos11:12:41

I see, you've been really helpful! Thanks a lot!

bronsa11:12:51

np! it's going to click eventually

pavlosmelissinos11:12:04

heh, hopefully 😉

Casey12:12:21

I have a spec like so

(s/def ::x int?)
(s/def ::y int?)
(s/def ::z int?)
(s/def ::coord (s/keys :req [::x ::y ::z]))

Casey12:12:36

if I try to validate it with a map like {:x 0 :y 0 :z 0}, I get an error that my input map doesn't have :my-spec-ns/x, :my-spec-ns/y, nor :my-spec-ns/z in it

Casey12:12:18

When defining specs, I understand you need to use fully qualified keywords. But when actually using them to test data.. all the keywords in my maps need to be fully qualified too?

Roger12:12:56

They don't have to. instead of (s/keys :req ... try (s/keys :req-un ...

Casey12:12:02

For good measure, here is the function and function spec (e is where the s/def above are)

(defn distance-squared
  "Returns the Euclidean squared distance between the coordinates"
  [c1 c2]
   (let [{x1 :x y1 :y z1 :z}  c1
        {x2 :x y2 :y z2 :z}  c2
        dx (- x1 x2)
        dy (- y1 y2)
        dz (- z1 z2)]
    (+ (* dx dx) (* dy dy) (* dz dz))))

(s/fdef distance-squared
        :args (s/cat :c1 ::e/coord :c2 ::e/coord)
        :ret int?)

Radek15:12:49

Guys, would this alignment of code

(s/consume-async     (partial message-handler exc) socket)
    (s/on-closed socket  (partial closed-handler  exc))
    (s/on-drained socket (partial drained-handler exc))

be preferred before this one?
(s/consume-async (partial message-handler exc) socket)
    (s/on-closed socket (partial closed-handler exc))
    (s/on-drained socket (partial drained-handler exc))

danielneal16:12:54

Personally, I prefer the second one, - less rules / maintenance but I've seen both

Radek16:12:45

Don't you find the first one more readable?

danielneal16:12:34

A little, it kind of trades readability for typing/maintenance if things change name.

johnj16:12:28

In which cases is better to just write java for better interop with clojure?

jeff16:12:39

(sorta beginner) I have an old project I wanted to pick up again. I've updated my installed version of lein, and when I try lein repl I'm getting an error Exception in thread "main" java.lang.AssertionError: Assert failed: transport-fn . This is Leiningen 2.8.2 on Java 1.8.0_25

jeff16:12:57

google isn't giving much relevant, any ideas on what's probably broken?

dpsutton16:12:29

there's some turmoil right now with the latest release of lein which came out yesterday. if you do lein upgrade 2.8.1 you should have smooth sailing

dpsutton16:12:14

there's a migration from clojure.tools.nrepl to a newer community version and this migration is proving a little tricky at the moment

jeff16:12:24

thanks! sorry for missing that when searching

dpsutton16:12:49

this is not something you should have to worry about 🙂 you're in the beginners channel and things "should just work"

jeff16:12:38

yeah well...otoh I'm restarting a slack bot project that I had working a year ago so there's beginner and then there's beginner 😉

dpsutton16:12:29

for sure. it just happened on the day after there was an update to a build tool that hasn't seen an update in a long time 🙂

jaide17:12:29

Is that a problem specific to Clojure or any language?

dpsutton17:12:07

not sure what you mean. if you came back to c after a bug in gcc was released, or elm and a bug in 0.19, or haskell and a bug in cabal or stack, or rust and a bug in rustc or cargo, etc. every language has some build tooling that just needs to work. and there was a bug (now resolved) in lein which is a pain and unfortunate but it is fixed

jaide17:12:42

Sorry I just meant is there something about Clojure that lends itself to experiencing that problem more frequently than others or is it more of a freak chance?

dpsutton17:12:41

i'm not sure Clojure does experience that more frequently. Lein has been rock solid for a long time it seems. a single bug the day of release when swapping out really low level core stuff once in a few years doesn't seem like the sky is falling.

jaide17:12:17

Agreed. It’s a small bug that flew into the window of opportunity while moving to greener pastures. I am fairly new to the lang and was interested in your experience is all 🙂

dpsutton17:12:45

the language and most libs are rock solid

dpsutton17:12:21

cider has had some issues with stability due to an internal reworking of connections but things are smoothing out

jaide17:12:30

That’s good to hear!

jeff16:12:50

yup that's how it always happens

jaide17:12:35

@jeff.bachtel You won the lottery…but instead of winning money it’s a broken release.

😂 12
noisesmith17:12:21

this is normal for CIDER, I guess it will be normal for nrepl too now

noisesmith17:12:53

oh - that's lein not nrepl, my bad

dpsutton17:12:27

due to lein moving from clojure.tools.nrepl -> nrepl 0.5.2

lispyclouds17:12:44

The fix got merged just now. maybe a new leiningen release soon

RodgerDodger18:12:23

I am trying to just hold onto the printed value of:

RodgerDodger18:12:50

Is there an obvious way to bind the printed time value as opposed to the value of the expression?

noisesmith18:12:14

with-out-str will capture everything a form prints to *out*

noisesmith18:12:51

(ins)user=> (def t (with-out-str (time (+ 1 1))))
#'user/t
(ins)user=> t
"\"Elapsed time: 0.008802 msecs\"\n"

RodgerDodger18:12:07

@noisesmith Perfect! Thank you, sir!

johnj20:12:21

is the resources directory a good place to store my db schema?

hiredman20:12:18

it isn't an unsurprising place to find it, but "good" is hard to say

hiredman20:12:56

that'll depend if you are actually just storing a schema or migrations, how those migrations are run, etc

johnj20:12:55

just schema, version controlled

johnj20:12:01

I'll just put it anywhere for now, like in some data dir

Casey20:12:23

Can anyone share some code (from github perhaps) or blog article that shows idomatic clojure error handling?

Casey20:12:27

I find that I write these beautiful simple functions but then struggle at adding error handling to them 😕

nikola22:12:33

How can I locate the position of a regex match in Clojure? I'm looking for functions that search for patterns in strings, doesn't have to be regex

seancorfield22:12:39

@nikola.kasev If you just want to find the index of a substring in another string, look at clojure.string/index-of (from memory -- but clojure.string is where you'll find all the native string functions... except for the few in clojure.core 🙂 )

👍 4
seancorfield22:12:08

@ramblurr I'm not sure there is One True Idiomatic Way for error handling in Clojure. It's going to depend on the situation. Do you just need to know something "failed" but otherwise get back a computed result? Do you need to know why something failed? Is it an "expected" failure or are you asking about "exceptional" failures?

noisesmith22:12:15

@nikola.kasev you can do this with re-matcher

user=> (let [m (re-matcher #"a" "bbbabbb")] (.find m) (.start m))
3

nikola22:12:13

If I understand correctly, .find is calling the method of the Java object that the function re-matcher returns?

noisesmith22:12:23

right, m is a Matcher

user=> (type (re-matcher #"a" "bbbabbb"))
java.util.regex.Matcher

noisesmith22:12:23

The api is easy to find via google. The find method looks for the first match (if any), the start method tells you the starting index of the most recent match.

seancorfield22:12:17

In a lot of cases where you either get a computed (non-`nil`) result or "failure", returning nil is often good enough -- and a lot of Clojure functions behave that way (so you can use if-let or when-let with the result).

seancorfield22:12:05

If you want to return a successful value or an error, returning either {:result <whatever>} or {:error "It blew up!"} can be useful so you can use destructuring

(let [{:keys [result error]} (some-computation)]
  (if error
    (report error)
    (process result)))

dfcarpenter23:12:43

I'm trying to run a basic lein project and I keep getting the error

`
Error occurred during initialization of boot layer
java.lang.module.FindException: Module java.xml.bind not found
Even through I have :jvm-opts ["--add-modules" "java.xml.bind"] in my lein project.clj. I'm on JDK 10. No idea why this is occuring

seancorfield23:12:46

@dfcarpenter You may need to upgrade your version of Leiningen...

dfcarpenter00:12:45

I just left jvm-opts []

dfcarpenter00:12:54

*empty and it worked