Fork me on GitHub
#beginners
<
2019-12-26
>
Alper Cugun08:12:11

I’m still not very happy with my setup. All the time that something like lein test takes, is that JVM startup time?

ghadi14:12:41

You typically run tests from inside the repl, not from the CLI

Alper Cugun14:12:54

How? And how should I have figured that out? Tests that you put in the test/ dir are not run by default anyway it seems…

Alper Cugun14:12:14

I also googled around a bit to figure out how to run tests with the standard runner and how to set this up but didn’t find that much.

kari16:12:33

Don't give up. The beginning may feel a bit slow but if you are persistent you will get there. Clojure is quite different compared to other languages. As ghadi mentioned above you usually don't run the unit tests using command line (e.g. lein test) that often. I use IntelliJ IDEA + Cursive and I can run one test or all tests in a namespace with a hot key and Cursive nicely shows a check mark in assertions that passed - and the tests run really fast since they will be run in the REPL session which doesn't need to boot as with running unit tests in command line.

seancorfield16:12:12

It's been years since I used lein but I would have expected lein repl to include test on the classpath...?

kari16:12:32

... and you don't need IntelliJ IDEA + Cursive for that. You can use any editor which has good REPL integration.

Alper Cugun16:12:49

As a beginner should I go straight to boot?

kari17:12:16

I would just use deps.edn nowadays.

seancorfield17:12:49

These days, I would advise beginners to start with the CLI/`deps.edn` -- or Leiningen (since so many tutorials and books still use it)

kari17:12:57

This is actually a very important topic if we want to attract more developers into the Clojure land: How newbies can start using Clojure and feel productive so that they don't get frustrated.

kari17:12:02

I watched @U050P0ACR's excellent REPL Driven Development course and I immediately wished I had watched it four years ago.

seancorfield17:12:00

@URK0TMXDG Regarding testing in the REPL:

(! 984)-> lein new myapp
Generating a project called myapp based on the 'default' template.
...
(! 985)-> cd myapp/
(! 986)-> tree test
test
|____myapp
| |____core_test.clj
(! 987)-> lein repl
nREPL server started on port 57269 on host 127.0.0.1 - 
...
myapp.core=> (require '[myapp.core-test :refer :all] '[clojure.test :refer :all])
nil
myapp.core=> (run-tests 'myapp.core-test)

Testing myapp.core-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
{:test 1, :pass 0, :fail 1, :error 0, :type :summary}

kari17:12:18

I think if any Clojure newcomer asks me what are the most important things to learn in Clojure - I would reply that one of the most important topics is to learn to use REPL efficiently.

seancorfield17:12:35

I expect whatever editor/integration you are using will have a hot key for running tests from the editor, in the connected REPL.

seancorfield17:12:04

☝️:skin-tone-2: Yes, effective Clojure means embracing the live development via the REPL model.

Alper Cugun19:12:59

I’m running the tests both from the REPL and using the editor command and they are running the core test for sure, but they don’t pick up the fact that I removed the breaking default test in there. Changes to files are not picked up here?

seancorfield19:12:15

Changes are picked up -- assuming you evaluated the code you changed -- but removing a definition from a file does not remove it from the REPL. When I remove a function (or rename it), I will add a (comment ,,,) form containing (ns-unmap *ns* 'old-name) and eval that form. My REPL workflow is not really file-based. I eval forms as I add/change them and I often don't save code between changes while I'm working on something specific.

seancorfield19:12:57

If you're evaluating code as you go, the REPL stays fresh, and you don't worry about the file or reloading files into the REPL. Takes practice to get into that flow -- but it means immediate feedback on every small change and that you've essentially "tested" all your code as you write it (in terms of evaluating code and seeing the results, preferably inline in the code depending on your editor setup).

Alper Cugun19:12:51

require '[myapp.core-test :refer :all] loads in the then-state of the file?

Alper Cugun19:12:52

But calling it again does not update the new state. I see.

seancorfield19:12:35

require only loads a file from disk if the namespace isn't already loaded.

seancorfield19:12:23

You shouldn't need to require manually in the REPL tho'. Just eval the changed code as you make each change -- with my note about deleting/renaming functions etc above.

seancorfield19:12:10

If you really want to reload from disk, you can eval (require '[myapp.core-test :refer :all] :reload) (or :reload-all) but if your REPL workflow is nice and tight, you should almost never need that.

seancorfield19:12:43

I don't remember the last time I had to manually require code like that...

Alper Cugun19:12:34

I wasn’t intending to do that that much, I was just trying out the bit of code you posted above about running the tests in the REPL. But I guess it makes sense that if it would evaluate the loaded test, that it would not refresh that on file change.

Alper Cugun20:12:22

But I see there is an entire section on the main website about REPLs. I’ll work through that I guess.

seancorfield20:12:54

Yup, it's a good tutorial.

seancorfield20:12:29

Also, if you don't mind spending some money, consider signing up for a month of http://purelyfunctional.tv and watch Eric's REPL-Driven Development course -- it's awesome.

seancorfield20:12:11

(for $50 you can get a month's access to everyone and if you don't want to spend more, you can cancel before the second month bills)

Alper Cugun20:12:28

I could do that, though right now I’m squeezing my learning into 1-2 hours each day after the kids are in bed. Would probably make more sense if I had dedicated time where I could dive in (unlikely). But fundamentally I’m not sure how to feel about paying for what is essentially my entry into the language.

seancorfield20:12:23

I meant more for "upping your game" once you're a bit further in -- that course is good for all levels of Clojurians (I got new stuff out of it and I've been using Clojure in production for nearly nine years now).

seancorfield20:12:47

If you haven't already watched them, I'd highly recommend Stu Halloway's talks on REPL-Driven Development (to the Chicago Clojure meetup a few years ago) and Running With Scissors (a more recent talk about the power of the REPL). Both free. Both on YouTube or Vimeo.

Alper Cugun08:12:33

Anybody have a good guide how to get setup on macOS?

jaihindhreddy10:12:51

lein starts a JVM for itself, which is an additional cost. The JVM itself starts up in ~200ms, but Clojure needs to bootstrap itself, which includes loading a lot of classes (a lot of work is done in static initializers). See: https://stuartsierra.com/2019/12/21/clojure-start-time-in-2019 Usually though, I don't think people run all their unit tests frequently. You make your changes with a REPL driven workflow, (possibly) convert some of those interactions into additional unit tests, and run them all before committing. You can check out Sean Corfield's excellent screencast on his workflow (as an example): https://www.youtube.com/watch?v=UFY2rd05W2g He uses the REBL as well, but even without it, all the principles still apply. This is the 3rd of his screencasts showcasing his setup (Atom) and workflow. His dot-clojure repo has lots of goodies too. Check 'em out.

jaihindhreddy10:12:32

Atom with Chlorine and Paredit installed is working out pretty sweetly for me rn.

Alper Cugun10:12:11

Thanks, I’ll check out the video. I think I’m doing something wrong still.

Alper Cugun10:12:31

And that post is very helpful. I suspected that Clojurescript would be a lot faster.

rakyi11:12:02

I use Spacemacs with Cider on macOS. Once I connect to REPL I can run all my tests with a single Cider command and there is no overhead of starting the REPL again, which might be a big chunk of lein test time. You should be able to achieve the same with any other Clojure editor with good REPL integration.

Alper Cugun11:12:05

I’m using calva but that freezes up every time I send my file to it. The file is not big, this is like a handful of functions for #adventofcode.

Alper Cugun11:12:54

The freeze is about as long as a Lein run just there is a lot less visual feedback about when it’s busy.

Alper Cugun11:12:28

Watched the video: it looks nice but the amount of setup required is pretty steep for a beginner. I’d have to take a different REPL and set that up, let alone the short keys etc.

Alper Cugun11:12:01

Do people use Clojurescript purely for the speed sometimes?

rakyi11:12:32

Yes, in use cases where startup time is important, e.g. AWS Lambda. Another way to reduce startup time is to compile Clojure to a GraalVM Native Image.

rakyi11:12:32

Never used Calva, but by your description it seems like either a bug or you’re doing something wrong.

Alper Cugun11:12:20

I’m using it vanilla. Will get around to submitting a bug. I’d say it should only redefine the functions that changed and that could be lightning quick?

rakyi11:12:15

Aren’t you actually computing a bunch of costly stuff when you evaluate the file? E.g. a top-level def, running an expensive computation, e.g. a solution to one of the AOC problems?

Alper Cugun11:12:51

Nah. Unless it’s running every function… is it?

rakyi11:12:27

I’m not familiar with how Calva works, but Clojure’s compiler in general simply evals all top-level forms in file from top to bottom, basically the same way as if you typed them into the REPL.

Alper Cugun11:12:47

What does that mean? Say a (defn solve-day3 [] (…)) would be run!?

Alper Cugun11:12:59

But functions with parameters wouldn’t be run?

Alper Cugun11:12:15

Or eval’ed means something different?

rakyi11:12:24

(defn name ...) is basically the same as (def name (fn [params* ] exprs*). So it only “creates the function”, doesn’t execute it itself, see https://clojure.org/reference/special_forms#def

rakyi11:12:51

But if you’d do something like (def solution (solve-day3)), that would execute solve-day3 on each file load.

Alper Cugun11:12:50

I’ll keep that in mind and check whether I’m doing that.

rakyi12:12:22

I was curious to see if Calva offers something similar to Cider w.r.t. running tests, and it indeed does, see “Running tests and mark failures and errors in the Problems pane” in https://calva.readthedocs.io/en/latest/commands-top10.html if you haven’t already.

Hi11:12:44

Hello! How can I convert ascii array like this: [ 64 100 123...] into String?

Sascha11:12:38

Something like this? user=> (apply str (map char [ 64 100 123])) “@d{”

SoV417:12:56

so i reload my server and it kills all the user sessions, of course, but maybe there is a way to persist sessions between server restarts?

Alper Cugun17:12:31

What kind of server?

SoV417:12:05

or maybe inadvisable? thoughts?

seancorfield17:12:21

@sova When you say "reload", what do you mean exactly?

Gulli17:12:15

@sova You could write to a redis db and and utilise EXPIRE to give it an expiration time

Gulli17:12:46

But it's a dependency investment

Raj18:12:23

Hi! Is there a way to avoid reflection warnings here? I've tried adding type-hints but the ctor warnings just don't stop

(defn text ^String [^InputStream stream size]
  (let [b (byte-array size)]
    (if (pos? (.read stream b))
      (case (aget b 0)
        0x00 (String. b 1          ;; ... call to java.lang.String ctor can't be resolved.
                      (if (and (> size 1)
                               (zero? (aget b (- size 1))))
                        (- size 2)
                        (- size 1))
                      StandardCharsets/ISO_8859_1)
        0x01 (String. b 1          ;; ... call to java.lang.String ctor can't be resolved.
                      (if (and (zero? (aget b (- size 1)))
                               (zero? (aget b (- size 2))))
                        (- size 3)
                        (- size 1))
                      StandardCharsets/UTF_16)
        (String. b StandardCharsets/ISO_8859_1)))))

bronsa18:12:27

wrap the int args in an (int ..)

Raj18:12:27

I tried almost everything: (int 1), ^bytes b, ^Charset Standard... but the warnings just don't leave

bfabry18:12:36

create a function that wraps the (String. ) ctor you want to use and type hint every parameter

Raj18:12:57

Thanks! that seems to do the job :+1:

hiredman18:12:58

Because of how the if expressions are nested the compiler maybe hoisting them out as separate functions (I forget if if causes that) which looses type hints, if that is the case you will want to wrap (int ...) around the whole if expression

bfabry18:12:14

imo, clearer to wrap the (String. ...) ctor in a fn that clearly calls out the type of each param

bfabry18:12:49

that chunk of code is already at "this is confusing" stage

Raj19:12:53

Wrapping in a new-string fn was probably the cleanest... Also came to realise that clojure doesn't support ^int hinting function parameters in defn form. only long and double are support for numeric types