Fork me on GitHub
#beginners
<
2019-11-01
>
Owen07:11:09

I'm using EMACS/cider and I've spec'd and instrumented a bunch of functions. The argument to a function is failing to meet the spec and cider is giving me a stacktrace. Nowehere in the stacktrace does it identify where in the code the spec failure is and that makes me think I must be doing something wrong. Symptoms:

Owen07:11:28

I have a line that says: Spec: #object[clojure.spec.alpha$every_impl$reify__2254 0x46fce19 "clojure.spec.alpha$every_impl$reify__2254@46fce19"]

Owen07:11:13

I have a stack trace that identifies: cityevolver.clj: 91 cityevolver/evaluate-placement-list

Owen07:11:30

As the last function I wrote. It calls 3 other functions and one of them has malformed arguments

Owen07:11:45

So I know that an argument has failed a spec but I get the impression something is wrong because there is no line saying "... and the spec was for arguments to [function-which-was-spec'd]"

Owen07:11:54

Any hints?

jumar08:11:23

That's interesting. It may actually be related to new clojure errror reporting. But you can try to inspect the exception manually in the REPL:

*e

jumar08:11:35

There you should see where it's coming from

metehan08:11:54

what is the simplest way to change this [30.723457672119142 36.869895291987106] to [30.7234 36.8698]

herald08:11:03

(mapv (comp float #(/ % 10000) int #(* % 10000)) [30.723457672119142 36.869895291987106])

metehan08:11:38

thank you very much, somehow a similar approach of mine looked weird I was expecting some shortcut or some magical core math function 😄

herald09:11:20

np! but yeah, java.lang.Math sadly doesn't have a function for truncating decimals.

erwinrooijakkers10:11:11

Other approach using format:

(defn round4 [n]
  (Double/parseDouble (format "%.4f" n)))

(mapv round4 [30.723457672119142 36.869895291987106])
;; => [30.7235 36.8699]

(mapv round4 [30.0000001 36.000001])
;; => [30.0 36.0]

erwinrooijakkers10:11:46

Oh wait that rounds up, if that’s a problem use another approach 🙂

erwinrooijakkers10:11:18

And if it’s a problem you should probably use BigDecimal

erwinrooijakkers10:11:16

Because floats aren’t precise

metehan11:11:11

haha I liked the quote 😄

metehan11:11:22

it was for geolocation openlayers(mapping library) gives long decimals. i don't need to be super precise. thank you

λraulain09:11:40

Hi everyone, I am new to Clojure (and emacs) and I am facing this challenge. I am following the instructions in the book/site Clojure for the brave and true and I can't start the REPL in emacs. when i do M-x cider-jack-in i get

error in process sentinel: could not start nREPL server: no Java runtime present, requesting install.
I am doing this on Mac OS and I already have Java 12 installed using sdkman I tried switching to Java 8 and got same result. I have also tried updating my emacs packages. Any suggestions please ?

Gerome09:11:14

You need to install a Java Runtime.

Gerome10:11:10

If you google Java JRE, you should be fine.

Gerome10:11:38

Something else though, I also started with this book. I had to start two times over in attempting to learn Clojure with this book because I always also wanted to learn using Emacs. Emacs is a great editor and maybe I'll get into it at some point. However, it does some things differently than most editors these days. Maybe that won't be an issue for you but if you feel frustrated with Emacs at some point, just throw it over board and focus on Clojure alone. Come here and find someone who can help you setting up your favorite editor/IDE for Clojure development.

Gerome10:11:17

This is my third attempt at getting something done with Clojure and it's my first attempt without Emacs and I'm finally getting somewhere.

λraulain10:11:05

Thanks a million @UPJP9G4G1 I think i will just keep emacs aside for a while and go with my familiar Intellij IDEA simple_smile

erwinrooijakkers10:11:40

Have you seen the Cursive plugin? https://cursive-ide.com/. Free for non-commercial use. Or look into Calva for Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva For an Emacs that works out-of-the-box you can look into Spacemacs with the Clojure layer: https://github.com/syl20bnr/spacemacs, https://github.com/syl20bnr/spacemacs/tree/c7a103a772d808101d7635ec10f292ab9202d9ee/layers/%2Blang/clojure. But Spacemacs works best I think if you use the Vim-bindings, which is another thing to master.

λraulain12:11:36

Thanks @U2PGHFU5U will add the Cursive plugin to IntelliJ

🙏 4
metehan12:11:34

I have another problem "4.455" to decimal 4.455 and "3.14" to 3.14 to do this my function became very long do you know short way?

dangercoder12:11:47

is this in cljs or clj?

dangercoder12:11:21

if you don't mind a "bigdecimal" you can use (bigdec "3.14")

Alex Miller (Clojure team)12:11:11

don't use Float/parseFloat - use Double/parseDouble in that case

8
metehan15:11:05

sorry i didn't mention that was CLJS I think we don't have Double/parseDouble in CLJS

Alex Miller (Clojure team)15:11:50

oh, yeah I was talking about clj/java

metehan15:11:04

(bigdec "3.14") also doesn't work

dangercoder15:11:52

(js/parseFloat "3.14") should work. I haven't tested it though. Not sure about the precision though on big numbers (decimals)🙂.

metehan15:11:38

yeah it works thank you. somehow I never check the hosting language. I have a bug in my brain. embarrassing 😄, thank you very much

dangercoder15:11:35

You are welcome! Good luck ✌️ 🙂

Linh Ta14:11:25

Hi guys, I'm an iOS developer trying to learn Clojure. I have no experience with the Java world or web development. This makes it quite difficult for me to learn Clojure and make use of it as I often improve best through pet projects. Tbh I feel kinda lost rn. Can anyone recommend me a learning path or point me to the right direction? Really appreciate your help!

ca-collins15:11:48

Hey there, I'm somewhat of a beginner myself, but was recommended this book early on in my learning (a great starting point imo): https://www.braveclojure.com/ You can read online for free or buy a e and/or physical book.

❤️ 4
Linh Ta16:11:12

Thank you! I will give it a look

Eric Ihli14:11:07

app:calculator.core=> (= js/Number (type 1))
true
app:calculator.core=> (instance? js/Number 1)
false
That's confusing to me. How do you check type in ClojureScript? Asking in this channel because it seems more beginner than ClojureScript.

Eric Ihli15:11:32

Never mind. I guess that's a JavaScript thing. 1 instanceof Number is also false.

Gerome16:11:44

It's because 1 is a primitive. If you do typeof 1 you should get "number". However, Number is an Object.

Gerome16:11:03

So, it shouldn't be possible to do 1.toString() but you can do Number(1).toString(). And the result of Number(1) should be an instance of Number

Gerome16:11:44

Number is basically a wrapper for numbers that offers some "convenience" methods.

Gerome16:11:06

Even though it's so inconvenient that nobody uses it, I think.

mloughlin15:11:00

I believe instanceof checks the prototype

Eric Ihli15:11:19

Aye. I see. Then this is proper, yes?

app:calculator.core=> (instance? js/Number (js/Object. 1))
true

mloughlin15:11:23

I think cljs has number?

mloughlin15:11:38

cljs.user=> (number? 1)

true

Eric Ihli15:11:41

Of course 🙂 Awesome. Thanks!

mloughlin15:11:07

and 1 is a primitive

mfikes19:11:49

In ClojureScript, you can (macroexpand '(number? 1)) to see what it does

Gleb Posobin19:11:41

Hi all, I am getting started with clojure and working through the brave and true book. In the exercises for chapter 10 (at the bottom of https://www.braveclojure.com/zombie-metaphysics/), there is one where I need to get several random quotes from in parallel and save word counts in an atom. I solved it, but I don't think my code is very idiomatic and I am trying to understand how to solve it better, can you please look at my solution and tell me how it could be improved? https://gist.github.com/passick/4d100029c217c2c9e80e8cd7f9990118

Gleb Posobin19:11:05

In particular, that stuff with doall and the way I create several futures seem fishy to me. Is there a better way to do it?

seancorfield19:11:34

Since you are using map/`doall` purely for side-effects, run! is probably what you want instead.

Ludwig19:11:10

is there a parallel run! ?

seancorfield19:11:57

https://clojure.org/reference/reducers#_reduce_and_fold -- but note the "chunks" are 512 by default so for small collections it won't be parallel

👏 4
seancorfield19:11:40

But look at repeatedly for invoking a no-argument (side-effecting) function a fixed number of times (instead of the first map over range)

seancorfield19:11:16

And instead of #(if %1 (inc %1) 1) you could use (fnil inc 0)

seancorfield19:11:51

fnil adapts a function such that if the argument(s) are nil, the replacement value is used instead before the function is called.

seancorfield19:11:46

So something like (run! deref (repeatedly n-quotes (fn [] (future (process-quote ,,,)))))

Gleb Posobin19:11:00

Ooh, didn’t find run! and fnil, thanks! I have tried using repeatedly at first, but couldn’t make it work with future, not sure why.

Gleb Posobin19:11:21

Thank you, will try again!

Gleb Posobin19:11:02

But wouldn’t (run! deref (repeatedly …)) create a future and dereference it immediately?

Gleb Posobin19:11:16

So the futures will be run in sequence?

seancorfield19:11:15

I'd have to check whether repeatedly is chunked or not. If you want real parallelization, sequences are not the way to do it in the first place.

seancorfield19:11:30

Doesn't look chunked... so, yeah, that might well produce sequential execution...

seancorfield19:11:36

If you want control over the level of concurrency, you pretty much have to drop down to Java and use executors etc.

Gleb Posobin20:11:24

I just want the tasks to be executed in parallel, that sounds like a basic thing.

Gleb Posobin20:11:47

I think my solution does it, no?

Alex Miller (Clojure team)20:11:54

well, pmapfor quick and dirty

seancorfield20:11:25

With the caveat that pmap is almost never what you want in production code 🙂

Ludwig20:11:24

because of lack of backpressure?

seancorfield04:11:07

@UJ9KCME4U Mostly because you have no control over it. When I was first using pmap, I threw it into a few places because I wanted "more concurrency" and it produced so much parallelism that I took down our search engine servers! 🙂

Ludwig11:11:22

how do you do now? core.async? , because I filled parts of my code with pmap hahaha

seancorfield20:11:18

@UJ9KCME4U We drop down to Java and use executors etc. That way we have a lot more control over concurrency. We also use core.async to help a lot of processes coordinate work.

👍 4
Alex Miller (Clojure team)20:11:03

hence the "quick and dirty" :)

Gleb Posobin20:11:16

Yes, looks like (doall (pmap...)) should do it. The exercise asks for futures explicitly, though.

seancorfield20:11:29

Since you don't need the result back, you could use dorun (which doall uses under the hood, but then returns the original collection).

seancorfield20:11:01

I suspect Brave&True is just expecting something simple like you had in your Gist -- but it's not idiomatic nor really "in parallel" so it's all a bit hand-wavey 🙂

Gleb Posobin20:11:41

I see. Thank you!

Chris20:11:38

I’m currently trying to create my first macro and still have some troubles…I’m trying to create a macro where I can define some bindings which are directories that should be removed in the end. The idea is basically to have a temporary directory, do something, and be sure that the directory is deleted afterwards. I tried to do it similar to the with-open macro since it seemed similar. Here is what I currently have:

(defmacro remove-after
  [bindings & body]
  (cond
    (= (count bindings) 0) `(do ~@body)
    (symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
                              (try
                                (remove-after ~(subvec bindings 2) ~@body)
                                (finally
                                  (let [dir ~(io/as-file (bindings 0))]
                                    (doseq [file (file-seq dir)
                                            :when (.isFile file)]
                                      (io/delete-file file))
                                    (io/delete-file dir)))))))
However when trying it out I get something like this:
No implementation of method: :as-file of protocol: #' found for class: clojure.lang.Symbol
Does somebody know what is happening here?

seancorfield20:11:13

@christian.paling A general bit of advice for writing macros is: try to write a function to do most of it first, and then write the macro as a wrapper around calling that function. That will help remove a lot of the problems.

Chris20:11:00

Thanks, yes that sounds like it would make matters more easier

seancorfield20:11:01

In the above, you're trying to introduce local bindings dir and file and those will be resolved to fully-qualified symbols which won't be correct. Use dir# and file# instead.

Chris20:11:31

Is the # special clojure syntax ?

seancorfield20:11:06

Yes, in macros, it uses gensym to create uniquely named local symbols.

Chris20:11:41

Thanks 🙂 there is so much new to learn when it comes to macros

seancorfield20:11:00

Yeah... for all that everyone tends to be like "Lisp! Macros! Yay!" in real-world code, macros are really very rarely used...

seancorfield20:11:02

Stats from our codebase at work:

Clojure build/config 52 files 921 total loc
Clojure source 303 files 70109 total loc,
    3170 fns, 605 of which are private,
    435 vars, 28 macros, 66 atoms,
    596 specs, 21 function specs.
Clojure tests 334 files 19294 total loc,
    4 specs, 1 function specs.
We have some more macros in the tests but don't bother counting those. So just 28 macros compared to nearly 3,200 functions, in just over 300 files.

Chris20:11:06

Interesting, thanks for letting me know!

Chris20:11:00

They will be resolved to fully-qualified symbols because of the ` right ?

hiredman20:11:15

you could start with f where f takes a function g and invokes g with a single argument which is a directory, and deletes the directory after g finishes executing

seancorfield20:11:23

And I think you want (io/as-file ~(bindings 0)) instead of ~(io/as-file ,,,) at a glance...

seancorfield20:11:35

Because (bindings 0) is a Symbol (as its value) so you'd end up calling io/as-file on (essentially) 'foo instead of the value of foo

Chris20:11:52

@hiredman Yes that’s true, that would be easier. I just liked the way with-open is doing it, and wanted to copy 😄

hiredman20:11:18

given the above approach you can write a simpler macro

hiredman20:11:08

(with-temp-files [a] b) =macro expands to=> (f (fn [a] b))

hiredman20:11:24

you are making it hard on yourself by combing the syntax rewriting and the actual computation in the macro

hiredman20:11:58

you can capture the computation you want as a function, and then simply use the macro to rewrite the syntax you want in to a call to that function

Chris20:11:49

@hiredman and in the fn [a] the a would basically be all symbols of the a vector that was passed to the with-temp-files vector ?

Chris20:11:08

and f would call the inner function with the values of these symbols ?

hiredman20:11:11

it depends how you implement it

hiredman20:11:15

(defn f [g] (let [tmp ...] (try (g tmp) (finally (delete tmp)))))

hiredman20:11:57

you have two things going on, something producing a value (the tmp directory) and something binding that value to a name

hiredman20:11:17

in your macro you are juggling both of those

hiredman20:11:54

the f function is only resposible for producing the value, the binding of the value to a name is taken care of via the construct clojure already has for that, a function

hiredman20:11:01

so something like (with-temp-files [a b] c) might expand to (f (fn [a] (f (fn [b] c ))))

Chris20:11:42

Alright, I see ! Still have to wrap my head around “thinking one level higher up”. Your approach seems to make things a lot easier, I’ll try it out. Thanks for the input !