Fork me on GitHub
#beginners
<
2021-09-03
>
Lucy Wang03:09:36

Hey channel, with rewrite-clj (or any other tool), is there a way to quickly parse a clojure source file into a plain-data presentation?, e.g. {:type :list :meta {:private true} :children [...] . IIUC with rewrite-clj I need to use it's nodes api and can't get this plain-data representation.

Bobbi Towers04:09:17

You might find the analysis output from clj-kondo more convenient: https://github.com/clj-kondo/clj-kondo/blob/master/analysis

👍 2
🙏 2
41ph4_Ph4un08:09:54

okay, silly question time! If I'm in ClojureScript and I print out and get #object[test$engine$backend$services$messaging_test$test_on_message] that means that we're having an object that's a JS-function rather than function in cljs, right?

41ph4_Ph4un08:09:47

(okay I admit, there's a follow up question to this too)

41ph4_Ph4un09:09:30

nevermind tho! got what I was pondering about 🙂

marreman10:09:48

Hello! I'd like to fetch about 150 URLs and then combine the results. Is core.async a reasonable choice for that or are the simpler future enough?

Ed11:09:20

future will allow you to parallelise the fetching of the urls pretty easily. I guess it depends on how you want to combine the results as to which is a better fit.

marreman11:09:39

Ah, ok! I can wait for them all to finish before doing anything. Then I want them in a single vector or something so that I can further analyze the results.

Ed11:09:51

something like this will create a load of futures and then deref them with a timeout into a vector

(let [urls ["" ""]]
    (->> urls
         (mapv (partial fetch-url))
         (mapv #(deref % 1000 ::timeout))))
is that what you mean?

marreman11:09:22

I think so, the requests happen even though you don't wait for them right?

marreman11:09:38

I mean they all happen in (sorta) parallell?

marreman11:09:10

In JavaScript I would do

await Promise.all([ request1, request2 ])

Ed12:09:24

yes ... there's a threadpool that all the futures run on. A future will start trying to generate a value as soon as it's created, and deref will get the value of that future ... if you want more control over the threadpool, I would reach for the java executors framework

noisesmith18:09:39

I say this frequently, but since this is a thread in #beginners I'll repeat myself: using core.async for long running IO tasks is a bad idea - if you have complex synchronization needs with throttling / timeout / retry / etc. it can help but you'll need to use async/thread or else you'll block up the tiny go block thread pool

noisesmith18:09:00

but in this case it doesn't sound like you have any complex coordination needs

noisesmith18:09:05

so you can skip using core.async entirely

Panagiotis Mamatsis12:09:16

Good afternoon everyone! First of all let me wish a Happy Autumn to everyone! It's a wish were are sending around in my country! I have a question that might sound a bit stupid...from what I have understood up to now (while I'm trying learning Clojure by myself) the language does not evaluating a function's arguments lazily. This is something that has to do with the language design?

Ed12:09:57

Correct. Clojure will eagerly evaluate the arguments to a function. So in the expression

(+ (* 2 3) (/ 16 4))
the (* 2 3) will be evaluated first, then the (/16 4) to produce (+ 6 4) which will then be evaluated. The + function doesn't get the chance to say "I'm not going to use the second argument, don't evaluate it". However, we do have lazy sequences which won't necessarily consume all their input, allowing you to write code that processes an infinite sequence. Macros however can control the evaluation of their arguments if they need to. That is how the language is designed. And a Happy Autumn to you.

Panagiotis Mamatsis13:09:43

Hi @U0P0TMEFJ. Thank you so much for your reply! Oh, I see! It's because Clojure is working on lists!

Ed13:09:39

Yes. Your code is a tree of expressions that are recursively evaluated. If you want to delay execution, you can wrap an expression in a function and call it later.

noisesmith18:09:07

there are cases where function args are lazily evaluated - for example

(ins)user=> (defn foo [& args] (+ (first args) (second args)))
#'user/foo
(ins)user=> (apply foo (range))
1

noisesmith18:09:47

(range) would take a very long time to execute and finally error if that were not lazy

Drew Verlee12:09:50

There are lazy functions and structures. More over, I suspect there is a trade off between compiler optimaztions and lazyness but I don't know enough to speak on the topic. Clojure was built to target java so there were probably practical limitations as to why not everything could be lazy. But I'm guessing :)

Panagiotis Mamatsis13:09:14

Hi @drewverlee. Thank you so much for your reply! I was thinking about how Scala is doing it...

ghadi13:09:18

Clojure is strictly evaluated

ghadi13:09:33

there are lazy data structures: seqs

☝️ 2
Noah Bogart13:09:21

and certain functions will return those lazy data structures (which makes it look lazy), such as map , but the evaluation is “eager”

😮 2
Jelle Licht14:09:37

Is the logic that is used by satisfies? different from what is used to actually dispatch calls to protocol fns? Or could something iffy be going in with a (Java framework provided) com.sun.proxy.$Proxy66 where this is not even supposed to work?

Jelle Licht14:09:41

So there is an existing interface IInternal , and I extend my Foo protocol with some-fn to it. Then I somehow get my mitts on an opaque object (the proxy) that implements IInternal , yet calling some-fn on it results in:

Jelle Licht14:09:31

java.lang.IllegalArgumentException: No implementation of method: :some-fn of protocol: #'user/Foo found for class: com.sun.proxy.$Proxy66

Alex Miller (Clojure team)14:09:50

or you may have reloaded your protocol (which creates a new interface of the same name)

Jelle Licht14:09:14

(definterface IInternal) (defprotocol Foo (somefn [this n])) (extend-type IInternal Foo (somefn [this n] n)) (let [p (proxy [IInternal] [])] (and (isa? (class p) IInternal) (satisfies? Foo p) (somefn p 7))

Jelle Licht14:09:40

Am I misunderstanding how proxy-&-friends fit into the whole Interfaces-and-Objects hierarchy?

Ed14:09:14

it works for me, if I pass the right number of arguments to somefn

(definterface IInternal)
  (defprotocol Foo
    (somefn [this n]))
  (extend-type IInternal
    Foo
    (somefn [this n] n))
  (let [p (proxy [IInternal] [])]
    (vector (isa? (class p) IInternal)
            (satisfies? Foo p)
            (somefn p 1)))
  ;; => [true true 1]

Jelle Licht14:09:40

Hmm, you are right. Then it must be something else. Off to the Java-mines with me it is!

Alex Miller (Clojure team)15:09:02

the reloading thing I mentioned above can definitely trip you up here

Jelle Licht15:09:20

It does (and has), but this time it’s something else as this happens as my opaque handle works with satisfies?

Jelle Licht15:09:57

unless there is some way for my require’d protocol to be out of sync with its own methods, but I have no clue how that could happen

Jelle Licht16:09:29

To answer my own question/misunderstanding: satisfies? Only checks to see if you have extended a protocol to a certain thing, not whether there are actual implementations defined for each of the fns in the protocol. Also, contrary to defrecord/deftype , you can implement random fns in an extend-type call, which in my case made me miss my typo in a protocol fn name:clown_face:

hiredman16:09:57

double compiling

hiredman16:09:55

my guess is com.github.benjaminasdf.idlelib.compsearch depends on com.github.benjaminasdf.idlelib.compsearchdispatch, so when com.github.benjaminasdf.idlelib.compsearch is compiled com.github.benjaminasdf.idlelib.compsearchdispatch is also loaded and compiled, and then it is loaded and compiled again on its own, and that kind of double compiling does weird things

hiredman16:09:59

I am going to revise that, my guess is actually that like the exception says, you are missing the class gnu.trove.TObjectHashingStrategy

Benjamin07:09:49

Indeed adding a dependency to gnu.trove.whatever fixed it. I just assumed that this intellij.open.api thing would take care of that. Usually a package would declare it's dependencies right?

hiredman16:09:45

the reason it doesn't error at the repl is gen-class is sort of a non-op at the repl, it only does anything when aot compiling

👍 2
Harshit Joshi17:09:35

hey how do i convert a string to an int ?

Russell Mull17:09:14

(Integer/parseInt "123")

Harshit Joshi17:09:29

ok i will try that

Harshit Joshi17:09:00

ya this works !

Harshit Joshi17:09:18

but why doesnt (int userInput) work ?

Russell Mull17:09:24

np. You should be aware that this will throw an exception if it can't parse, see https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#parseInt(java.lang.String)

Russell Mull17:09:42

Because coercision isn't parsing?

👀 2
Russell Mull17:09:10

int is meant to be used with other numeric types

Harshit Joshi17:09:32

wait can i use the entire java APi ?

Russell Mull17:09:43

yep 🙂 it's a good feature

Harshit Joshi17:09:42

Because coercision isn't parsing? how are they different ?

Russell Mull17:09:00

A good question. To me, parsing is extracting structured information out of unstructured data. Coercion is taking a piece of structured data and converting it to a different kind of structured data.

🙌 2
Ed17:09:23

Also, it's worth noting that entering a 7 into a repl will get you a java.lang.Long rather than an int so you might want Long/parseLong instead. Also it's worth noting that "7" is also valid edn so you could:

(require '[clojure.edn :as edn])
(edn/read-string "7") ;; => 7

Ed17:09:02

not sure if this will matter to you 😉

Alex Miller (Clojure team)19:09:14

btw, given how frequently this question comes up, one thing I'm looking at right now is adding a function to the Clojure API for it (along with other similar things)

Harshit Joshi19:09:03

oh ya that would be helpful

Harshit Joshi17:09:14

i seem to be unable able to use (cast Number userInput) & (int userInput)

noisesmith18:09:10

cast does only one thing: throw an error if its arg doesn't fulfill the required class, and return the object otherwise (with no conversion or alteration)

🙌 2
noisesmith18:09:32

(its presence in your code does allow the compiler to do things it wouldn't otherwise attempt though...)

noisesmith18:09:48

int only works on numeric inputs

🙌 2
noisesmith19:09:27

you have too many parens around (if (> randNo userIn) ...) - that will lead to a null pointer exception

🙌 2
👍 2
noisesmith19:09:54

in clojure, () doesn't mean "sub expression" like in math, it means "execute this"

Harshit Joshi19:09:49

hey is there something wrong with this code

Harshit Joshi19:09:56

(def randNo (rand-int 10))

(defn guess []
  (let [userIn (read-line)]
    (if (= randNo (Integer/parseInt userIn))
      (println "Well Done !") 
      (
        (if (> randNo userIn)
          (println "Oops try smaller !!")
          (println "Oops try bigger !!"))
        (guess)
      )
    )
  )
)

Harshit Joshi19:09:15

wait i figured it out !

Harshit Joshi19:09:46

i didnt use parseInt the 2nd time that i tried to use a numeric operator

Harshit Joshi19:09:18

is there any way that to use parseInt on the input at the same time of defining it ?

Harshit Joshi19:09:21

something like this

Harshit Joshi19:09:45

let [userIn (Integer/parseInt (readline))]

Harshit Joshi19:09:50

wait thats legal right

Harshit Joshi19:09:57

i am going to try this

Harshit Joshi19:09:09

yup sorry for spamming the chat

hiredman19:09:05

Something to keep in mind is parens are not blocks or grouping, they are function application

tschady19:09:41

randNo will always return the same number, whatever it’s first evaluated at compile time, but maybe that’s what you wanted

2
☝️ 2
Harshit Joshi19:09:09

Hmm thanks, can you elaborate a bit I don't think I get it entirely @hiredman

noisesmith19:09:04

(ins)user=> (println "OK")
OK
nil
(ins)user=> ((println "OK"))
OK
Execution error (NullPointerException) at user/eval147 (REPL:1).
null
(x ...) means "resolve or evaluate x, and then call it", println returns nil, you can' t call nil

🙌 2
noisesmith19:09:36

@U02DGP08V5F does this example help?

noisesmith20:09:07

in this code fragment:

(if (= randNo (Integer/parseInt userIn))
      (println "Well Done !") 
      (
        (if (> randNo userIn)
          (println "Oops try smaller !!")
          (println "Oops try bigger !!"))
        (guess)
      )
it looks like you want do, which is a macro that takes all its args and evaluates them in order (like {} would in eg. js)

🙌 2
noisesmith20:09:41

(if (= randNo (Integer/parseInt userIn))
      (println "Well Done !") 
      (do
        (if (> randNo userIn)
          (println "Oops try smaller !!")
          (println "Oops try bigger !!"))
        (guess))

noisesmith20:09:46

also, as a style thing, we don't leave ) on a line by itself (which is different from most languages)

🙌 2
Harshit Joshi22:09:32

Omg thanks for the elaborate explanation !

Harshit Joshi22:09:52

Thanks Yes I guess do Is what I needed

Harshit Joshi19:09:47

@tws oh really didn't notice that

sova-soars-the-sora22:09:29

Hi 😄 I have two vectors. One is a vector of vectors... [[:x :b :q :r :z] [:c :d :n :i :p :z] [:e :f :g :h :o]] Two is a vector of desired-number-of-items from the subvecs of vector 1 [5 2 1] The output would be [[:x :b :q :r :z] [:c :d] [:e]] (5 elements of the first subvec, 2 of the second, 1 of the last) What's a good way to implement this?

ChillPillzKillzBillz19:09:21

A bit late to the party... but I love solving this kinda problems... also as a beginner it gives me practice... here is my solution...

(def v1 [[:x :b :q :r :z] [:c :d :n :i :p :z] [:e :f :g :h :o]])
(def v2 [5 2 1])
(map (fn [v num] (take num v)) v1 v2)
This gives
((:x :b :q :r :z) (:c :d) (:e))

sun-one23:09:46

(map (fn [v num] (subvec v 0 num)) [[:x :b :q :r :z] [:c :d :n :i :p :z] [:e :f :g :h :o]] [5 2 1])
You could just map over it and run subvec

sun-one23:09:58

Map with two vecs zips them together.

sova-soars-the-sora23:09:04

That's really smart. I mostly get how it works... it surprises me that the arguments stay aligned pair-wise

Nik08:09:08

RxJS has very helpful diagrams to visualise these higher order functions. It should have all the common ones For Zip pattern - http://reactivex.io/documentation/operators/zip.html#collapseRxJS

sun-one23:09:10

One thing to be aware I believe if the vectors are not the same length, map will only run to the shortest vector.

sova-soars-the-sora23:09:26

Ah that's a good caveat to keep in mind @chesslunatic