Fork me on GitHub
#beginners
<
2020-06-15
>
mister_m00:06:44

I'm getting a bit tripped up working with a vector, applying a filter, and suddenly having a lazy seq. It makes me feel like I am misunderstanding how things should be used. Can I filter a vector without producing a seq? Can I turn my seq back into a vector?

noisesmith01:06:28

on a more philosophical level, it can help to think of data belonging to functions, instead of functions being helpers for data - most clojure functions produce a specific data type, even if they are flexible about the type of their input

mister_m00:06:25

ah, filterv

seancorfield00:06:06

@radicalmatt Why do you need the vector? Could you do most stuff with sequences, in terms of map/filter/etc? Vectors are useful in some situations where you need specific performance guarantees, or you specifically need indexed access -- but trying to maintain vectorness all the time can be worse for performance, depending on what you're doing.

seancorfield00:06:26

For example, it's worth noting that mapv and filterv are eager (based on reduce) whereas map and filter are lazy.

mister_m00:06:28

in my case, I was using a vector as a stack with conj / pop

mister_m00:06:53

i could switch things around with first / cons and a seq

mister_m00:06:12

tbh I went with vector because it had an easy literal: []

seancorfield00:06:31

An empty list has an easy literal too () 🙂

mister_m00:06:42

you are not wrong

noisesmith01:06:12

but while lists and vectors are stacks, lazy-seqs are not

☝️ 3
Rob Aguilera02:06:10

Little confused on something, from what I understand for loops return lazy seqs and only run when it's results are required to run some calculations but isn't that what I'm doing by passing each user to send-to-api? I know doseq is more idiomatic approach, just curious about what's going on internally.

(defn send-to-api [user]
  (println "Sending to API:" user))

(let [users [{:name "Alysia"} {:name "Bob"}]]
  (for [user users]
    (send-to-api user))
  (println "DONE")) ;; => prints just DONE

;; using doseq achieves the desired results
;; prints each user followed the the "DONE" statement.
(let [users [{:name "Alysia"} {:name "Bob"}]]
  (doseq [user users]
    (send-to-api user))
  (println "DONE"))

hiredman02:06:44

for is not a loop at all

hiredman02:06:37

it is like a list comprehension or set builder notation

hiredman02:06:20

given this thing (a lazy seq), this is how you construct another thing from it

hiredman02:06:16

so your for is "given this list of users, create a new sequence by passing each one to send-to-api"

Rob Aguilera02:06:27

Oh that makes sense.

hiredman02:06:58

identical to (map send-to-api users)

Rob Aguilera02:06:10

Got it. Thank you.

fappy02:06:28

hi 🙂 are the following equivalent, or is there some sneaky way in which they are not?

(empty? x)
and
(nil? (seq x))

seancorfield02:06:54

@fappy In the REPL, you can see how empty? is defined:

user=> (source empty?)
(defn empty?
  "Returns true if coll has no items - same as (not (seq coll)).
  Please use the idiom (seq x) rather than (not (empty? x))"
  {:added "1.0"
   :static true}
  [coll] (not (seq coll)))
nil
user=>

seancorfield02:06:47

And seq either produces nil or a non-empty sequence. So nil? of nil is true and nil? of any non-empty sequence will be false.

seancorfield02:06:17

And in the definition of empty?, not of nil is true, and not of any non-empty sequence will be false.

thanks2 4
andy.fingerhut03:06:55

The for expression is not the last one of the let body, so its return value is never consumed by anything.

andy.fingerhut03:06:06

It goes to the bit bucket.

3
Pragyan Tripathi05:06:02

Hello Guys, I am trying to use reader conditionals for reading environment variables:

(def ^:dynamic *env*
   (into {} #?(:clj (System/getenv))))
I can’t seem to find a way to do this for cljs block.

phronmophobic05:06:50

is your cljs destined for the browser? nodejs? something else? multiple targets?

Pragyan Tripathi05:06:51

I am trying to build it for nodejs

phronmophobic05:06:44

looks like you can retrieve environment variables with https://nodejs.org/api/process.html#process_process_env in nodejs

phronmophobic05:06:14

potentially, something like:

(def ^:dynamic *env*
  #?(:clj (into {} (System/getenv))
     :cljs (js->clj (.-env js/process))))

👍 4
Pragyan Tripathi06:06:25

Cool thanks this works 🙂 … Appreciate the help 🙂

👍 3
MorongÖa07:06:36

Hi guys and girls, Does the latest `Clojurescript` make it easier to use `Storybook'??` How has anyone used `storybook`  `clojurescript?`

mloughlin08:06:51

what determines whether to use reduced or recur for function with an early return?

Lennart Buit09:06:20

They are not the same thing, recur is for calling a function recursively, reduced is to provide a reduced value in reduce (e.g. stop the reducing)

mloughlin10:06:25

both allow the same thing: doing something to each item in a collection, with the possibility returning early

mloughlin10:06:58

loop/recur being the lower level iterative construct in Clojure

Lennart Buit11:06:07

Then I misunderstood your question

mloughlin11:06:54

I've worded my question poorly, sorry

noisesmith15:06:21

my personal rule is never use loop to consume a single collection item by item in order - you can easily replace it with reduce thanks to reduced for early exit

noisesmith15:06:57

(that plus using a data structure as an accumulator when you need multiple values to update per step)

manas_marthi10:06:02

hey, can anyone share the link to Rich hickey's talk about semantic versioning, not changing meaning of apis, about software accretion etc..

manas_marthi10:06:51

thank you very much

🙏 3
andy.fingerhut13:06:12

There are transcripts of most of Rich Hickey's talks available in this Git repo, too, if you prefer reading, or want something text searchable later: https://github.com/matthiasn/talk-transcripts

manas_marthi10:06:31

Dumb question, if I use a library in my lein project and declare using clojure 1.10, but the source code of the library declares a dependency on clojure 1.3, how many versions of clojure exist in my JVM?

delaguardo11:06:23

only one, version is determined based on dependency resolution algorithm. With leiningen you can check which lib resolves to which version - lein deps :tree . for more options - lein deps -h

hindol14:06:33

I think Maven (and thus Lein) uses the version first encountered while traversing the dependency tree. Deps CLI improves this resolution logic by always picking the latest if there is a clash. If you are not tied to Lein, you may consider moving to deps.edn based project management. One downside is, Lein has more features and more readymade stuff that you may have to do yourself in Deps CLI.

sb13:06:01

Could you help me the scheme lambda is “equivalent” with Clojure anonymous functions? (if somebody is here with scheme language knowledge)

andy.fingerhut13:06:19

Yes, lambda in Scheme is equivalent to fn in Clojure

👍 3
andy.fingerhut13:06:42

I do not know if Scheme has anything like this, but Clojure's fn can optionally be followed by a symbol, which gives a local name to the otherwise anonymous function, which can be useful for identifying it if it ever appears in a stack trace of an exception.

👍 3
andy.fingerhut13:06:35

Clojure also has syntax like #(inc (* 2 %)) for declaring anonymous functions with a few less characters. That example is equivalent to (fn [x] (inc (* 2 x)))

👍 3
sb13:06:23

@U0CMVHBL2 thank you very much the detailed answer!

William Skinner13:06:28

How do I go about reading a large file into clojure for a batch process.

andy.fingerhut13:06:27

There are only about a dozen different ways, depending upon things like whether you want the whole thing in memory at once, what format the file is (text with no other structure, CSV, binary, etc.)

andy.fingerhut13:06:37

Clojure strings are Java strings, which require about 40 bytes of memory plus 1 byte per character, if they are all ASCII, or 2 bytes per character if there are non-ASCII characters in a line.

William Skinner13:06:47

Optimally I'd like to pull in batches of let's say 10,000 lines at a time and push that to another part of the process asynchronously

William Skinner13:06:21

(with-open [reader (io/reader "/home/wskinner/tmp/DX_****_20200422_214932.txt")]
  (count (line-seq reader)))
give me OOM exception for a 1.4 million line file.

andy.fingerhut13:06:16

You may need to specify a larger max heap when you start the JVM process on which Clojure runs, e.g. -Xmx2g for 2 Gbytes

William Skinner13:06:30

I'd like to keep this operating at or under 2 GB. It's going to eventually be running in resource constrained cluster.

andy.fingerhut13:06:38

If the file is completely ASCII characters, then 1 byte per character plus 40 bytes per line should be enough for that data

andy.fingerhut13:06:14

Have you explicitly specified max heap when you got OOM, and if so, what max heap did you try?

William Skinner13:06:28

I haven't let me do that now

dpsutton13:06:34

https://github.com/seancorfield/depstar/blob/master/src/hf/depstar/uberjar.clj#L135-L144 have a consume-file function that takes a function f and a chunk size. Call line-seq and then partition and call f on each partition

William Skinner13:06:46

Oh nice, thank you

ghadi13:06:52

(reduce (fn [n _] (inc n)) 0 (line-seq reader)) should avoid the original problem

ghadi13:06:32

and consume nearly no more memory than a couple of lines worth

ghadi13:06:59

@skinner89 just checking, but are you running under a debugger like intelliJ/cursive?

William Skinner14:06:42

Thanks @U050ECB92 with reduce it ran in 760 msecs w/o the OOM

William Skinner14:06:59

I'm running this in a cursive/line repl in intellij yes

William Skinner14:06:06

not in debug mode though

William Skinner14:06:40

The next part of this process is going to be pushing the chunks to a stage that inserts the lines into a sql database. As I understand channels will allow me to do that asynchronously while respecting backpressure so the process reading from the file will wait. Is that right?

William Skinner14:06:16

This has probably been solved by some clojure ETL libraries 🙂

fubar14:06:14

Hi, how do you convert a single character to a string in Clojure? I want \H to become "H" or basically (\H)

mister_m14:06:57

you can say (str \H) to create a java.lang.String

fubar14:06:55

Thanks, it worked!!

William Skinner15:06:14

The article introduces this function for creating something reducible from a bufferedReader:

(defn lines-reducible [^BufferedReader rdr]
  (reify clojure.lang.IReduceInit
    (reduce [this f init]
      (try
        (loop [state init]
          (if (reduced? state)
            state
            (if-let [line (.readLine rdr)]
              (recur (f state line))
              state)))
        (finally (.close rdr))))))
Has anything more idiomatic been added to the language?

noisesmith15:06:06

in theory that could become part of the definition of line-seq as a performance optimization

noisesmith15:06:04

but if you need that kind of performance, arguably that's idiomatic code

👍 3
Alex Miller (Clojure team)15:06:27

I would add something, not levy it on line-seq

Alex Miller (Clojure team)15:06:01

but adding something would be good (didn't we already have this discussion like a year ago?)

ghadi15:06:05

there is some work around a function called iteration https://clojure.atlassian.net/browse/CLJ-2555 @skinner89

delaguardo15:06:10

is it possible to check if an object implements a method -foo from protocol Foo ?

seancorfield15:06:32

satisfies? -- if it is declared to implement the protocol. You might also need to check the metadata on an object, if the protocol is declared to be extensible via metadata.

delaguardo15:06:44

yeah, but I’m interested in particular method

ghadi15:06:27

are you implementing a linter or writing ordinary clojure code?

delaguardo16:06:27

neither of both) just curious is it possible or not

delaguardo16:06:15

because it is possible to partially implement a Protocol

ghadi15:06:33

"business code"

jakubl15:06:56

could make a protocol - with just that one method - but it feels hacky

ghadi16:06:12

I would caution that doing dispatch checks is antithetical to the spirit of protocols

ghadi16:06:00

(if (satisfies? X target)
  (x-method target))

ghadi16:06:16

not a good pattern

delaguardo16:06:36

why is that?

ghadi16:06:19

because polymorphism is about not caring about the target, and having the dispatch automatically do the right thing

delaguardo16:06:10

so what you recommend? in case if I need to call x-method on something that is out from my control?

ghadi16:06:43

protocol-based polymorphism is intended to reduce manual dispatch/instance checking

delaguardo16:06:48

(defprotocol Foo
  (-foo [this])
  (-bar [this]))

(-bar
 (reify
   Foo
   (-foo [_] "!")))
anyway, this is what I’m interested in ^ -bar is not implemented and calling to it throws and because it is possible to partially implement a protocol I wonder do clojure have a way to check a method

Alex Miller (Clojure team)16:06:20

from a public api perspective, no

Alex Miller (Clojure team)16:06:14

if you want to get dirty, protocols are just maps and you could introspect those maps to answer this question

delaguardo16:06:58

I already tried that, no luck ( how could i introspect an object that reify is returning?

delaguardo17:06:00

Ok, thanks anyway)

Alex Miller (Clojure team)16:06:34

or at least partially answer it

pooya16:06:43

Hi everyone! First time here and just learning clojure!

👋 30
Chris16:06:57

I've got a noob question. I'm trying to process a string which contains at and it isn't being handled properly. How can I escape the at so the string works properly?

(def song "Nothing at All")
(println song)

Output: Nothing
Desired Output: Nothing at All

hiredman16:06:29

have you actually run this code and had this problem? or is this just a reduced example you haven't run and seen the problem on?

hiredman17:06:12

this example is such a reduced case, that if you do see the problem when you run it, my guess is there is some kind of error/bug in whatever codepad like thing you are using. but if you don't actually see the error when running this reduced case, my guess is you haven't correctly reduced it. something else in all the other code that are running is causing the issue

Chris14:06:17

Yeah, this was run and came out exactly like this. For that reason, I've concluded the issue was with the environment not my code.

Chris14:06:26

Thanks for your help.

Alex Miller (Clojure team)16:06:10

it should work that way already

Alex Miller (Clojure team)16:06:34

maybe you can share more of your actual code

Chris16:06:19

My actual code isn't much more involved honestly, I'm just trying to test the validity of the input, and I ran into this issue: This outputs: [Bye Bye Love Nothing

(defn print2DStringArray [sArr2D]
  (print sArr2D)
  (println)
  (println))

(def songs-2 [
  "Bye Bye Love"
  "Nothing at All"
  "Money for Nothing"
  "Love Me Do"
  "Do You Feel Like We Do"
  "Bye Bye Bye"
  "Do You Believe in Magic"
  "Bye Bye Baby"
  "Baby Ride Easy"
  "Easy Money"
  "All Right Now"
])

(printStringArray songs-2)

dpsutton16:06:59

i copied that and it should be print2DStringArray make sure you aren't calling an older function that is still in your repl

dpsutton16:06:31

but i get this when i call the 2D version:

user=> (print2DStringArray songs-2)
[Bye Bye Love Nothing at All Money for Nothing Love Me Do Do You Feel Like We Do Bye Bye Bye Do You Believe in Magic Bye Bye Baby Baby Ride Easy Easy Money All Right Now]

Chris16:06:32

Sorry, I copied the wrong piece:

(defn printStringArray [sArr]
  (print sArr)
  (println)
  (println))

Chris16:06:01

I think it might be a bug in my environment, I just tried another one and it worked fine. Sorry to both you guys and thanks for your help.

dpsutton16:06:34

how are you running this?

Chris16:06:25

It's in a private environment run by my company.

Chris16:06:35

Similar to CoderPad

dpsutton16:06:13

oh. i was wondering if its possible for the jvm to exit before its flushed everything to the output.

Chris16:06:38

I don't think it's that because it correctly prints other things later in the code (this is the middle of a larger section).

Alex Miller (Clojure team)16:06:25

flush is an explicit function you can call too if needed

Kazuki Yokoyama22:06:37

Hi, people. I want to delay the execution of a function (it is actually a simple TTL). I thought about using future, but then I'd have to use something like (Thread/sleep some-time) and, as far as I know, Thread/sleep still occupies some resources while "waiting". What would be the best way to accomplish it? Thank you.

dpsutton22:06:08

There’s “delay”

dpsutton22:06:29

What controls the length of the delay?

Kazuki Yokoyama23:06:39

Delay would require me to deref it explicitly, I think. I would like to schedule a cleanup function to execute after a given amount of time. The function calling this "scheduler" sets the amount of time.

Kazuki Yokoyama23:06:42

The "scheduler" would look like

(defn scheduler
  [ttl]
  (future
    (Thread/sleep ttl)
    (cleanup-fn)))
This is the behavior I want to achieve, but without the downside of Thread/sleep

Kazuki Yokoyama23:06:51

I believe that ScheduledthreadPoolExecutor will do the job. Thank you for your help!

noisesmith23:06:35

yeah, delay is not a timed delay, just a form of laziness

ghadi23:06:53

ScheduledThreadPoolExecutor has a method called schedule that accepts a delay

💯 3
Kazuki Yokoyama23:06:35

Cool. This is exactly what I was looking for. Thank you.