Fork me on GitHub
#beginners
<
2020-04-26
>
Timofey Sitnikov01:04:29

Thank you all for being available for my first day of Closure. So far I am finding that the things I expect to be difficult are simple and it is a joy to be discovering that. Sorry Emacs users, but it just does not feel right even with all the nice integration. Five years of nvim/tmux feels like a fast and maneuverable sports car that gets you there immediately.

J01:04:11

(let [out (f args)]
  (doseq [_ (range (count args))]
    (prn (<! out))))
I'm new to clojurescript and I'm trying to write above function (not the whole function but the gut of it) that given a list of args that I want to apply f (f returns a go block which is a channel) to each of the arg so instead of prn how can I exhaust out channel and return materialized list of values? Also I need to close the out channel at the very end what's the best way to do it? Thanks.

amarus01:04:49

Hi! Does someone have example simple frontend-backend CRUD project with reitit? I cannot understand how to interact from frontend to backend rest api. May I just use simple http requests from frontend route handlers to bakend?

andy.fingerhut02:04:50

There is apparently a #reitit channel that might have more people who have used that library who may know

andy.fingerhut02:04:16

@J I am not sure I understand what you are looking for. Could it be something like this?

(defn foo [args]
  (doseq [arg args]
    (prn (<! (f arg)))))

J05:04:40

Thanks for the reply, I'm trying to drain the channel first then return all values.

ghadi16:04:50

use async/into

J22:04:21

Thanks, will take a look. Is this similar to merge? https://clojure.github.io/core.async/#clojure.core.async/merge I tried merge as I could not close the channel before producing a result, however I needed throttling and merge for some reasons, with cljs, buf-or-n didn't seem to work. Looks like into only works once a channel is closed.

andy.fingerhut02:04:15

In particular, note that creating a range over the count of args is unnecessary -- you can just as well traverse over the sequence args itself.

👍 1
Bhougland03:04:51

trying deps.edn. Umm.. dumb question but how do i get it to acutally download the dependencies?

andy.fingerhut03:04:24

It should download any that you do not already have by running clj or clojure commands, automatically

andy.fingerhut03:04:18

and print messages about the ones it is downloading, as it does so

Bhougland03:04:05

I just typed clj and the repl prompt showed but it did not download limo or any of the selenium jars

andy.fingerhut03:04:38

And you believe those are in your deps.edn file as dependencies?

Bhougland03:04:06

limo is (it is a selenium wrapper)

andy.fingerhut03:04:11

If you are willing to publish your deps.edn file contents somewhere public and link to it here, I can look at it.

andy.fingerhut03:04:53

Or you can copy and paste it here, if it is short

andy.fingerhut03:04:46

When I create a new directory with a deps.edn file with the same contents as that, and run the clj command in that directory, I see about 50 lines of output about dependencies being downloaded, before I see the REPL prompt appear. The first 2 lines of output look like this:

Downloading: limo/limo/0.2.11/limo-0.2.11.pom from clojars
Downloading: org/seleniumhq/selenium/selenium-api/3.141.59/selenium-api-3.141.59.pom from central

andy.fingerhut03:04:25

What command did you try, and did you run it from the same directory where that deps.edn file exists? What is the output of clj -Sdescribe command on your system?

Bhougland03:04:29

clj -Sdescribe {:version "1.10.1.489" :config-files ["/home/ben/.clojure/deps.edn" "deps.edn" ] :config-user "/home/ben/.clojure/deps.edn" :config-project "deps.edn" :install-dir "/nix/store/5v3qrsa7mkx433b35yglw146krbnzyil-clojure-1.10.1.489-prefix" :config-dir "/home/ben/.clojure" :cache-dir ".cpcache" :force false :repro false :resolve-aliases "" :classpath-aliases "" :jvm-aliases "" :main-aliases "" :all-aliases ""}

andy.fingerhut03:04:58

Do you get a REPL prompt when you run clj or clojure commands?

andy.fingerhut03:04:58

Very likely something else you did before running those commands already downloaded those dependencies and put them inside your $HOME/.m2 directory. For example, if you ever used a Leiningen project that used those same dependencies, they would have been downloaded then, and the clj command is finding them there already, and not downloading them again.

Bhougland03:04:18

limo is in the .m2 directory

andy.fingerhut03:04:36

If you really want to, you could force it to download all needed dependencies again by deleting your entire $HOME/.m2 directory. Do not do that if there is anything in there that you have any doubts about being able to retrieve again, but if it is all open source stuff available from public sources, there is unlikely to be any problem with doing so, except for the extra time to download dependencies again.

andy.fingerhut03:04:04

But using existing dependencies inside of $HOME/.m2 is the reason they are put in there, as a local cache on your system.

Bhougland03:04:49

I am experimenting with using Kakoune as a text editor and i wired it up to the klipse-repl (which I did fire up before running clj)

Bhougland03:04:00

maybe that downloaded it first and didn't tell me

Bhougland03:04:47

It is not as intuitive (yet0 to see all the downloads (as opposed to something like cursive)

andy.fingerhut03:04:56

I am not aware of any record kept of the reason that files were put into $HOME/.m2 stored there, except perhaps your operating system's date and time stamp on the file, but that still doesn't tell you which software caused it to be downloaded.

justin03:04:30

For clojurescript, is there another way to shift code to compile time besides macros? I have a function that updates an atom multiple times to construct a final hashmap. the input is pre-determined. I honestly don't need to optimize this....just curious 🙂. I'm basically constructing map to represent groups of items, where the groups have names and use keywords to reference the actual items.

andy.fingerhut03:04:52

If it isn't doing fresh downloads, then as long as your $HOME/.m2 files don't get corrupted or in some weird state, it is there to make things go faster.

Bhougland03:04:32

thanks Andy! I am pretty new to clojure and wasn't aware of this folder

Bhougland03:04:59

I will delete it and see if I can force it to download these files again.

Bhougland03:04:48

delete the .m2 folder itself, or just the directories inside?

andy.fingerhut03:04:52

It is a Java/Maven thing that Clojure/Java uses, too.

Bhougland03:04:17

okay. Thank for the help!

Bhougland03:04:39

Andy, I deleted .m2 and am getting the following errors:

Bhougland03:04:05

Error: Could not find or load main class clojure.main Caused by: java.lang.ClassNotFoundException: clojure.main

andy.fingerhut03:04:12

As output from running clj command with no arguments, in the same directory where that same deps.edn file is? Or from doing something else?

Bhougland03:04:24

same directory

Bhougland03:04:50

just running the clj command

andy.fingerhut03:04:09

Hmm. I am getting the same messages when I try removing the $HOME/.m2 directory on my system. I have not encountered this before. Doing some experiments to see if I can figure out why that happens.

andy.fingerhut03:04:36

Never had that problem when using Leiningen or Maven for Clojure things, so didn't expect it to be any different for clj

Bhougland03:04:12

I use Nixos so I can just roll back my system, but wanted to see if using this exotic OS might be the cause...I guess not

andy.fingerhut03:04:24

Ah, I know. I forget sometimes that there is another kind of caching going on by clj that is stored in a .cpcache directory in the same directory where your deps.edn file is.

Bhougland03:04:03

i see the file

andy.fingerhut03:04:06

Files in the .cpcache directory often contain full path names to files in your $HOME/.m2 directory, so if you delete $HOME/.m2, but not also .cpcache, it will believe what is in .cpcache and give that error.

andy.fingerhut03:04:19

Should work if you also delete .cpcache directory.

andy.fingerhut03:04:29

The next clj command should rebuild its contents.

Bhougland03:04:48

that did the trick!

seancorfield03:04:56

Or clj -Sforce

Bhougland03:04:06

Man, I really appreciate your help. I learned a lot

andy.fingerhut03:04:20

caching is great, except when it causes things to break 🙂

👍 1
justin04:04:00

Is conjoining a vector of 2 elements with a hashmap , such as (conj {} [:1 2]) , defined behavior and will continue to operate this way in the future?

justin04:04:32

Ah, seq can be used as sort of the inverse of this operation, so seems to be planned out this way.

Chris K12:04:19

Anyone know how I can leave the debugger for cider emacs? After I do <f9> (I have doom emacs), there is a red rectangle around the functions and I cannot turn them off. This also happened after I pressed 'q'

Ben Sless12:04:13

do you mean un-instrument the def? you can just eval it regularly

Chris K12:04:50

so.. how would I do that?

Chris K12:04:07

because when I run it on the repl, it causes problem

Ben Sless12:04:34

go to the def and cider-eval-defun-at-point

Ben Sless12:04:32

I don't know the DooM shortcut for it

Chris K12:04:50

I'm pretty sure it's C-x C-e

Chris K12:04:28

i mean it executes, but I want to get rid of the debug thing. Any idea for that?

Ben Sless12:04:14

It didn't remove it? it should have :thinking_face:

Chris K12:04:27

I somehow deleted it but not sure how I did it. Id think it was from defun at point

Chris K12:04:34

I should be able to figure it out soon tho

theeternalpulse16:04:37

If you type q does it say it's recording a macro? Does doom also have a "run next command as emacs"? Sometimes the evil bindings take over and I have to type \ to have it recognize q as the quit command.

practicalli_john20:04:45

@U012BEGBECC Evaluate the (defn ,,,) expression you instrumented with cider debugger from within the source code buffer. Evaluating the defn expression will remove the breakpoints that cider debugger creates and removes the red boarder from the defn name. I recommend getting used to evaluating within the source code buffer rather than using a repl buffer directly, its much more efficient in the long run.

Chris K15:04:50

thxs! That works. This has been making me quite annoyed... Also.. are u the practicalli? If you are, I love your website and your youtube channel. 😄

kimim12:04:21

Hello all, is it possible to use clojure for computer graphics and computer vision? Thanks.

Chris K13:04:29

tbh I don't know any clojure libraries or ways that u can do those stuff, but I just found this website and maybe this will help https://www.clojure-toolbox.com/

Isaac16:04:16

Hi everyone! I'm an intermediate python coder, and complete clojure beginner

Isaac16:04:35

'evaluate form' in Calva isn't working for me exactly

Isaac16:04:18

it doesn't reflect changes that I make to a given function, but instead acts as if it was never changed

Isaac16:04:37

disconnecting from the REPL, and then reconnecting fixes this

alidlorenzo16:04:46

@irabicoff you need to reload the file so that the changes can get picked up

alidlorenzo16:04:45

to see command for that look at keyboard shortcuts “Load current file and dependencies”

Isaac17:04:29

thank you! worked instantly of course 🙂

Isaac17:04:53

do you have advance for quick shortcuts for things like 'load ... dependencies' and evaluate form?

alidlorenzo17:04:58

main ones I use are connect to repl, load file/deps, and evaluate form you can evaluate form in repl, as a comment, or in Calva terminal output (which is what I mostly use) also there’s a #calva channel

Isaac17:04:43

ah wonderful -- just figured out how to 'load current namespace in REPL..' which is what I think you mean by 'calva terminal output'

alidlorenzo18:04:21

no, there’s a repl window, but there’s also a terminal output

alidlorenzo18:04:27

do you know how to open terminal in vscode? in that same panel there’s an “output” tab and to the right there’s a dropdown with a list of extension that print their output, see “Calva says”

❤️ 1
seancorfield17:04:44

@irabicoff There's a #calva channel where you'll be able to get in-depth help on Calva. I would have expected that you can eval each top-level form as you edit, without even saving your file, to get updated function definitions into the REPL. Nearly all Clojure editors support that -- I'm sure Calva does too.

Isaac17:04:35

thanks! It turns out that you do need to reload before evaluating again, but this can be done upon save

Isaac17:04:49

just joined #calva

seancorfield17:04:10

That really doesn't sound right. All the Clojure editors I know let you evaluate the current (top-level) form into the REPL without saving the file. No reloading is needed.

hindol18:04:38

And with Calva, no need to evaluate the whole file also, if you don't need it. To quickly eval something and test, I first evaluate the ns form at the top and the needed form. Of course if that form has a dependency that dependency needs to be evaluated first.

hindol18:04:32

The shortcut is Ctrl+C, V

alidlorenzo18:04:55

@seancorfield is that meant to apply to definitions outside the evaluated form? not sure what ideal experience should be but I find that I if I change code in the file I have to reload it so that evaluate form can get latest definitions

seancorfield19:04:58

@UPH6EL9DH Every time you make any change in the editor (to a defn), you can evaluate that top-level form (definition) into the REPL without needing to save the file. Assuming you're doing Clojure, not ClojureScript.

seancorfield19:04:45

I make a change, hit ctrl-; B in my editor, it evals that whole form into the REPL.

seancorfield19:04:02

I go back and forth between files, making changes, hitting ctrl-; B (eval top-level form), running tests (`ctrl-; t` for a single test, ctrl-; x for all tests in a single ns). No file saving needed.

alidlorenzo18:04:41

so to be clear, the evaluate form is always its latest version, but other definitions in the file might not be.

pez18:04:02

I think it would be a bit difficult to create a Clojure editor where it is not the latest evaluated definitions of things that are used. It is how Clojure works. Calva does not by default evaluate saved files, a lot because of this. The workflow is supposed to be that you edit a form and then re-evaluate it, and then use it. It is not file based, but rather form based.

didibus19:04:36

One thing to understand is that Clojure does not operate over files. Source files is often an abstraction in other languages. In Clojure, code is at the form level. A form is any unit that can be compiled and evaluated on its own. So a string is a form, a symbol, an S-expression, etc.

didibus19:04:08

When Clojure runs a program, it asks for forms to evaluate in order.

didibus19:04:43

You can see this at the REPL. The REPL isn't just for development, the REPL is also how programs are ran

phronmophobic20:04:55

I wouldn’t say the REPL (read, eval, print, loop) is how programs are ran. 1. programs aren’t typically printing at each read and eval. 2. after your program starts, it’s uncommon to keep reading in expressions to keep your program going. while it’s technically true that your program is still processing whatever forms its already read, I think it gives the wrong intuition to you’re program is still “running via the repl”

didibus19:04:43

Clojure reads some form, then evaluates it, then it prints it (that's the step it wouldn't do for a real program) and then it loops back to waiting for the next form to read

didibus19:04:53

So when you run a script, meaning some Clojure code stored in a file. All that happens is Clojure will read the file from top to bottom and it will read and eval each form one by one as it encounters them in the file

didibus19:04:47

When you run Clojure with a main and add libs, it does something similar. The point is it is all a convention. By convention, Clojure reads and evaluate forms in a file from top to bottom.

didibus19:04:48

By convention, if it encounters a "ns" form, it will pause the reading and evaluating of the current file, and go read and evaluate the required libs defined in the ns, etc.

alexmiller19:04:47

It’s not “pausing”, it just another expression that it evaluates like any other

didibus19:04:55

Well, I mean that it'll take a detour, like the next form to evaluate is not the one after ns in the current file, but the ones in the require libs. But you're right, in theory that's just part of the evaluation of ns.

didibus19:04:24

When you are at the REPL, you are in charge of choosing in what order you want forms read and evaluated by the REPL

didibus19:04:00

This confuses people a lot at first. But there are good reasons to want to skip over some forms or change the order when you are at the REPL, often so you can prevent state from being overridden which you were currently using to test things, or when you want to explicitly create some temporary state only for the current REPL session

didibus19:04:39

So the editor you use should give you at minimum three features: (1) A way to send a single form to the REPL. (2) A way to send all forms from top to bottom in a buffer to the REPL. (3) A way to send all files in the project to the REPL starting from the main entry if there is one. Additionally, some editor will also give you a way to clear the REPL state for some or all namespaces. The reason for this last one is that when you send forms to be evaluated, they don't replace the old form that was previously sent. They simply add a new one. So old functions of the same name still exist in memory. If you want to remove those you need to explicitly remove them.

alexmiller19:04:55

I dont understand what 3 means or why you would do that?

didibus19:04:46

I might not have explained that well. But it reloads the file that depends on the file you are on

didibus19:04:38

I think, anyways, I don't actually use that one very much :p, but I've seen editors have a third thing. Cursive calls it sync files in REPL, and forgot what's the name for it in Cider

didibus19:04:20

It's like, say you changed 4 different buffers. And want them all sent to the REPL

didibus19:04:35

Instead of manually going to each and doing a Load file in REPL

didibus19:04:59

You can run Sync file in REPL and it'll do it for you, somehow figuring out in what order based on their requires

didibus19:04:51

So my thought was that it'll find every root of the namespace trees of the files in your src folder, and then do a load file on those

alidlorenzo19:04:16

^ @pez i think Calva is missing (3) ? i.e. as opposed to reloading all of a file’s dependencies manually though maybe I just need to get used to re-evaluating forms as I make changes

didibus19:04:44

I think it's best to just get used to controlling it manually. Once you start to setup state that you depend on for your REPL session, the fine grained control comes in handy

didibus19:04:38

Some people like to rely heavily on defounce instead, but I find that kind of weird, cause then once you want to force the defounce to reevaluate it gets tricky

didibus19:04:56

I'll give an example:

(def items (atom []))

; At the REPL, not in your code:
(swap! items conj 1 2 3)
 
;; Back in code:
(defn process-items [items] ...)
Now imagine you are working on process items, and as you implement it, you want to keep calling it with your list of items. If you Load file, you will re-evaluate the (def items (atom []) which will reset the list to empty. And you will lose your test lost that you were using to call process-items with.

seancorfield19:04:08

Why switch from the editor to the REPL? Why not just have

(comment
  (swap! items conj 1 2 3))
inline in the source file and eval that code without even leaving the editor!

didibus19:04:11

Just to keep my example to the point. I wouldn't actually switch. But I mean to say, here's code that you won't commit to git, (or which will be commented when committed). So like, code you don't actually intend to be part of your program

didibus19:04:39

Though sometimes I switch to the REPL still, because the REPL can be in a different namespace.

didibus19:04:15

Sometimes I want to test calling my thing from the context of a calling namespace for example. Maybe because my items list is in that other namespace

didibus20:04:21

And part of that is because Cider does some magic that makes it I can't just do: (comment (in-ns foo) ... (in-ns bla))

didibus20:04:04

But probably I should get used to having the other namespace open on a separate buffer as well :man-shrugging:😝

seancorfield19:04:14

Stu Halloway talks about this in one or two of his presentations: don't type into the REPL, type into your editor and eval forms as you go.

didibus20:04:48

Well, I'm reducing the details a bit. A program won't print, and won't read from the command line. But you can think of it as reading a recorded REPL session from a file. Where it'll replay things in order from top to bottom.

didibus20:04:22

The important bit is that it is always read a form, eval the form. That's the basic. Files don't really exist. Everything is just forms that are read and evaluated one after another in some order. And then, around this mechanism, there are conventions and tooling build for different use case.

phronmophobic20:04:32

I agree that having read and eval are the important bits. I think it’s also worth emphasizing that read and eval are distinct, separate steps (which is different from most non lisps).

didibus20:04:17

It's hard to explain the machinery, but it is all much simpler in some way, and the primitive is read and eval, and everything else are things that you could have build yourself. While other languages have them as a part of their compiler and run-time, and you can only change if you change the code for the compiler and run-time

phronmophobic20:04:08

I would be careful about explaining read and eval in terms of a “repl” since the “repl” has connotations of a command line program that’s connected to stdin and stdout that the programmer is typing directly into (which is how the repl is used in other languages).

didibus20:04:50

People at my work new to Clojure seem to understand it better when I phrase it like that

didibus20:04:22

If I say, when your program runs it's literally doing exactly the same thing as if you had typed it in at the REPL in order from top to bottom

didibus20:04:56

They often go: Ohhh! Really! Same order? Same thing?

phronmophobic20:04:23

how is that different than python/javascript

didibus20:04:14

Hum.. well it's not really different then those I don't think. But it's very different from Java, which is often the background people at my work have.

phronmophobic20:04:19

I guess most of the people I talk to have a js or python background

didibus20:04:55

Ya so they might not have this problem. In Python/JavaScript, can't you use a function defined after from before it?

phronmophobic20:04:37

in js, there’s hoisting, but it’s not good to rely upon it

phronmophobic20:04:18

your program should run the same as if you had typed into the repl, but you can do some funky things if you want

didibus20:04:52

Hum, so you need to have things ordered as they depend on each other then?

didibus20:04:48

Ok I see, never heard the term hoistong

didibus20:04:55

So, it isn't the same at all then :p

didibus20:04:11

Seems JS has a 2 pass process which operates at the file level

didibus20:04:45

Maybe by convention it isn't good practice, but it still would be different, because it now operates at the file level and has concept of files

didibus20:04:18

The next question would be about how modules are handled and if they are tied to files, and if hoisting is at the file level or module level

didibus20:04:29

But I think JS modules are kind of a mess so :man-shrugging:

phronmophobic20:04:44

so while those are technical differences, I’m not sure they highlight why clojure might be a good option compared to dynamic alternatives like python and js

didibus20:04:32

It doesn't. I'm not trying to make a judgement call. Just that this is how it works, and it works in a way that often doesn't line up with people's expectations coming from other language. Like why is the REPL not automatically in-sync with the files? Why does it say it can't find the function foo when I run my script even though it is defined in the file?

phronmophobic20:04:03

:thumbsup: that makes a lot of sense if you’re coming from a java, c++

didibus20:04:35

The REPL is a tooling around this to allow you to send code to be read/evaluated on command in the order you choose. clojure.main is another tool which will send forms from a set of files or libs you specify to it which it will use to read/eval from

didibus20:04:25

I could build a tool which runs Clojure programs stored in a database for example.

didibus20:04:39

Or where files are loaded in reverse order from bottom to top

didibus20:04:55

At least this holds for Clojure, when you hit Java things change, and you hit limitations.

Joe20:04:32

After a couple of previous failed attempts to understand transducers I sat down today to try and get my head round the concepts. After a few passes through the docs and a couple of videos, I came up with this as a possibly correct and hopefully easy to understand summary. Putting it out here because a) any feedback or corrections would be appreciated and b) maybe it will be useful to others. https://redpenguin101.github.io/Notes_Clojure/transducers2

🎉 2
upside_down_parrot 1
didibus21:04:41

Ya not bad

didibus21:04:33

It helps me to think of them as just a way to compose reducing functions together

didibus21:04:20

And reducing functions don't need to produce a smaller output. Like you can do what you do with map with reduce as well. The output can even be larger

Joe21:04:11

Thanks! I get your second comment. Your first one I will have to noodle on for a bit... > a way to compose reducing functions together

didibus21:04:36

You'll understand if you try writing your own transducer

didibus22:04:35

Its like, the transducing function takes a reducing function as argument, and can choose to call it or not and when on the element to process. And in turn, it will return a reducing function, which can be passed to the next transducer for which it too will choose if it wants to call it or not, etc.

didibus22:04:27

So what happens is you get a chain of reducing functions calling one another recursively and finally returning the transformed value

didibus22:04:03

The transducing functions are used to create this chain for arbitrary reducing functions

didibus22:04:55

For example, you can write a reducing function which will increment all elements. And another one which will filter even elements. But what if you want to now increment all elements and filter them? You can perform both reduce one after another. But there is another way. You could have the first reducer increment the element, and then call the second reducer to have it filtered. So now one reducer calls the other. But, if you do it like that, everytime you need to compose two or more reducers, you have to write a custom reducing function that calls each other inside themselves. With transducer, you generalize this so you can have the transducer return you a reducing function that calls the others for you.

sova-soars-the-sora20:04:22

@allaboutthatmace1789 awesome! while no expert, i look forward to reading it momentarily