Fork me on GitHub
#beginners
<
2017-06-30
>
devn03:06:43

Aleph is good kit

aconanlai05:06:30

hi everyone 👋

aconanlai05:06:13

just learning clojure over here

aconanlai05:06:40

i'm wondering what the colon prefix on :when, :let and :while do in the first two examples

noisesmith05:06:35

they are keyword literals

seancorfield05:06:42

:something is a keyword. for recognizes a number of keywords to specify qualifiers on the iteration

alice05:06:07

I have never used that feature in my life

seancorfield05:06:01

@alice I don't think I have either...

aconanlai05:06:36

thank you all!

aconanlai05:06:59

@alice , do you mean you haven't used keywords, or for ?

noisesmith05:06:00

:when inside for saves some complexity compared to (filter .... (for ...)) imho

alice05:06:11

I haven't used the literals in for

alice05:06:23

I just... do for... I guess?

alice05:06:40

I don't think it's something that would ever occur to me to use

aconanlai05:06:57

i see now that clojuredocs page on for also mentions :when :let and :while

seancorfield05:06:35

wherever you have (filter pred (map f coll)) you can do (for [x coll :when (pred x)] (f x)) -- think I got that right...

noisesmith05:06:03

well - sortof

noisesmith05:06:14

yours is more like (map f (filter pred coll))

alice05:06:14

So it's implicity fiiltering the inputs or something?

noisesmith05:06:22

no, he had it scrambled

noisesmith05:06:49

and I had my justification scrambled too, so...

seancorfield05:06:08

(for [x coll :let y (f x) :when (pred y)] y) -- yes?

noisesmith05:06:10

the :when filters the input, so never replaces a filter wrapping the form, my bad

noisesmith05:06:19

oh - that should work, yes

noisesmith05:06:08

user=> (for [x (range 10) :let [y (* x 3)] :when (even? y)] y)
(0 6 12 18 24)

seancorfield05:06:20

and :while lets you do the equivalent of take-while on a collection

noisesmith05:06:30

only on the inner index!

noisesmith05:06:39

which is interesting but counterintuitive

seancorfield05:06:00

Yes... true... using macroexpand here helps right?

noisesmith05:06:39

user=> (for [x (range 10) y (range 10) :while (< y x)] y)
(0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0 1 2 3 4 5 0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8)

noisesmith05:06:54

macroexpand on for is nearly a lovecraftian horror - but sometimes it might help?

noisesmith05:06:24

user=> (macroexpand '(for [x (range 10) y (range 10) :while (< y x)] y))
(let* [iter__7307__auto__ (clojure.core/fn iter__916 [s__917] (clojure.core/lazy-seq (clojure.core/loop [s__917 s__917] (clojure.core/when-first [x s__917] (clojure.core/let [iterys__7303__auto__ (clojure.core/fn iter__918 [s__919] (clojure.core/lazy-seq (clojure.core/loop [s__919 s__919] (clojure.core/when-let [s__919 (clojure.core/seq s__919)] (if (clojure.core/chunked-seq? s__919) (clojure.core/let [c__7305__auto__ (clojure.core/chunk-first s__919) size__7306__auto__ (clojure.core/int (clojure.core/count c__7305__auto__)) b__921 (clojure.core/chunk-buffer size__7306__auto__)] (if (clojure.core/loop [i__920 (clojure.core/int 0)] (if (clojure.core/< i__920 size__7306__auto__) (clojure.core/let [y (.nth c__7305__auto__ i__920)] (clojure.core/when (< y x) (do (clojure.core/chunk-append b__921 y) (recur (clojure.core/unchecked-inc i__920))))) true)) (clojure.core/chunk-cons (clojure.core/chunk b__921) (iter__918 (clojure.core/chunk-rest s__919))) (clojure.core/chunk-cons (clojure.core/chunk b__921) nil))) (clojure.core/let [y (clojure.core/first s__919)] (clojure.core/when (< y x) (clojure.core/cons y (iter__918 (clojure.core/rest s__919)))))))))) fs__7304__auto__ (clojure.core/seq (iterys__7303__auto__ (range 10)))] (if fs__7304__auto__ (clojure.core/concat fs__7304__auto__ (iter__916 (clojure.core/rest s__917))) (recur (clojure.core/rest s__917))))))))] (iter__7307__auto__ (range 10)))

alice05:06:37

Jesus Christ you weren't lying

alice05:06:58

I don't know if I'll be able to sleep after that

seancorfield05:06:01

Macro expand a go block if you really don't want to sleep 😈

noisesmith05:06:37

and this is why people get "method too large" errors when they try to use core.async, for, doseq, and core.match together in the same code

alice05:06:11

I had no clue that error even existed

noisesmith05:06:32

well, if you look at the generated code, you can probably imagine how it happens

noisesmith05:06:37

it's a vm limitation thing

alice05:06:50

So you're saying 2gb of code generated from just a few nested bits

noisesmith05:06:13

well, the jvm limit for method bytes is a lot smaller than 2 gb

alice05:06:28

Isn't that the total memory limit

alice05:06:30

I just assumed

alice05:06:44

Speaking of which, that's like 1/16 my ram, should be higher

alice05:06:19

Well darn 😮

noisesmith05:06:27

if the Java virtual machine code for a method is exactly 65535 bytes long and ends with an instruction that is 1 byte long, then that instruction cannot be protected by an exception handler

noisesmith05:06:59

so really, in this case, 64k is big enough for anyone

alice10:06:47

So, I use clojure A LOT, and I've really been sponging up material from talks, but I still don't know how to use transducers really tbh, it feels like Monads when I was first learning Haskell, but I've seen Rich's talk on them. Does anyone else have some really nice info on using transducers practically and what they are?

curlyfry10:06:13

@alice Perhaps https://www.youtube.com/watch?v=WkHdqg_DBBs The rest of the videos cost money, but I believe you can cheat by getting the free trial and watching all the transucer videos

matan17:06:45

@alice If I recall correctly, in very succinct terms they can be described as a means to compose operations on data collections, whereby the operations you describe through your transducers, aren't applied each on the entire input collection as in normal plain clojure code, but rather every element in the collection gets acted upon by a composition of what you specify in your transducers. Meaning, the input collection gets walked only once!

matan17:06:48

So, it is an alternative way to act on data collections, which requires of you to stray from the plain clojure API you'd normally would, but brings along some benefits like lazy evaluation and/or different performance trade-offs.

matan17:06:56

In addition, some advantages in terms of affording greater modularity in applying transformations on data, are mentioned in the linked SO question above ― although not really well elucidated in code examples there.

matan17:06:35

I hope these comments might help easing in

noisesmith19:06:01

@matan - I'd agree except the part where you mention "collections" - items coming from a collection are a common case with transducers, but a big motivation for making transducers was the ability to apply transformation directly to the source of data, without forcing it into a collection first (eg. a core.async channel or a network stream etc.)

noisesmith19:06:13

the fact that you can chain transducing functions without intermediate collections comes from this - the context of another transducer is one of the data sources you can apply a transducer to

matan19:06:40

@noisesmith good point, which I was oblivious to until now, and thanks for the feedback!

matan19:06:08

care to elucidate the connection to channels or network streams though?

noisesmith19:06:30

you can use (map f), as one example of a transducing function, directly on a channel

noisesmith19:06:50

it's an optional arg when you create the channel, and causes that transform to be applied to all data that goes through the channel

matan19:06:03

ah, really this makes transducers and core.async channels a match made in heaven or something like that

noisesmith19:06:50

channels are part of why they were invented - the clojure team discovered they were re-implementing filter, map, partition, etc. etc. on core.async and realized it would be better to have an abstraction for the transforms independent of the source and destination of data

matan19:06:28

mmmm thanks for this key perspective @noisesmith . so transducers are the only abstraction that can "equally" apply to "plain" collections as well as abstractions like async channels

seancorfield21:06:51

As of java.jdbc 0.7.0-beta1, you can also apply transducers to “reducible queries” (and reducible result sets).

seancorfield21:06:33

Instead of passing in functions to transform rows and process the transformed result set, you can now just create a “reducible query” and hand it off to anything that knows how to reduce it: reducers, transducers, plain ol’ reduce and into etc…