Fork me on GitHub
#beginners
<
2020-07-01
>
ksd00:07:21

Am I doing something wrong? Or do -> and #() not like each other?

(macroexpand '(-> file slurp json/read-str #(select-keys % keys)))
(fn* (json/read (slurp file)) [p1__141#] (select-keys p1__141# keys))
Also I’m having trouble finding documentation on fn*

ksd00:07:26

oh I see

ksd00:07:52

user=> (macroexpand '(-> file slurp json/read (#(select-keys % keys))))
((fn* [p1__145#] (select-keys p1__145# keys)) (json/read (slurp file)))

noisesmith00:07:46

you don't need `#() for this

noisesmith00:07:01

(-> file slurp json/read (select-keys keys))

noisesmith00:07:24

good job finding why it didn't work though

ksd00:07:58

interesting

Chris McCormick01:07:45

Hello, can anybody help me figure out what I am going wrong here? This is an excerpt from my repl:

> (clojure.repl/dir emorogue.core)
component-game
component-game-map
component-title-screen
from-bag
main
start
start-game
state
vec-remove
nil
> emorogue.core/vec-remove
#object[TypeError TypeError: emorogue.core is undefined]
If I do (in-ns 'emorogue.core) i get the same error.

Chris McCormick02:07:33

using shadow-cljs repl

Chris McCormick02:07:14

wait i just got it working facepalm

Chris McCormick02:07:25

reloaded the browser window. sorry for the noise.

Chris McCormick02:07:59

i've got too used to never having to reload the browser window because of cljs tooling heheh

Alex Miller (Clojure team)02:07:00

just fyi, there is also #shadow-cljs here if you need it

🙏 3
stopa01:07:15

Noob question: How does clojure do (= [{:foo 1}, {:foo 2}] [{:foo 1} {:foo 2}]) Does it in effect do a deep equality check?

Alex Miller (Clojure team)02:07:17

all values are compared for equality, including collections

👍 3
bones04:07:55

Would map be the idiomatic structure to base a linked-list on?

hiredman04:07:45

Lists are already linked lists

bones04:07:46

oh and just use first and rest?

bones04:07:42

Not quite sure what you mean, I thought lists were not intended for every-day use?

raspasov05:07:43

@bones vectors are much more frequently used in Clojure; if you’re sure you’re not going to need random performant access via (nth …), then lists are fine; In practice, I prefer vectors in 99% of cases

bones05:07:00

Thanks @raspasov, do you mean use vectors for the implementation? I was just looking to make my own linked-list as a learning experiment

raspasov05:07:22

ah; that’s a different story; then you should probably not use vectors

raspasov05:07:05

I guess in that case you should intentionally avoid using the built-in stuff 🙂

bones05:07:12

Yeah that’s what I thought - I mean, you could probably use vectors to implement the same functionality, but it wouldn’t really be a linked list, right? Since in a vector there’s an implicit relationship between items, whereas in a linked-list it’s explicit. Does that sound about right?

raspasov05:07:01

As far as I can tell, you’re correct; if you’re “serious” about this experiment, I’d suggest looking at Clojure protocols https://clojure.org/reference/protocols and also https://clojure.org/reference/datatypes ; those are generally the “proper” ways to design custom data structures in Clojure

raspasov05:07:43

But that might be beyond a “beginners” exercise 🙂

bones05:07:00

Thanks 🙂 So the protocol could be the linked-list “API” so-to-speak, with next and val functions, and the datatype would define a linked-list node?

raspasov05:07:02

yes, I think so

noisesmith15:07:15

there's a few ways to approach this - the least overhead would be using hash-maps with {:value x :next y} and then my-first that gives you value, and my-rest which gives you next

noisesmith15:07:07

if you use the clojure interfaces as @raspasov suggests, you should probaly use deftype, then all the list funcitons would work on your datatype like magic

noisesmith15:07:44

in the middle would be defrecord which both lets you define things as keys in a map, and lets you implement the interfaces

seancorfield05:07:30

@bones Building your own linked list as an exercise in Clojure is kind of weird since it already has a core data structure that is a linked list (as noted above) and pretty much any implementation you build is going to be somewhat non-idiomatic I suspect...

seancorfield05:07:25

It's one of those things that is a good exercise in, say, OO languages that don't have native linked lists...

bones05:07:25

Hi @seancorfield , I guess I’m confused because that’s what I’ve heard but from what I see I can’t build up a list by specifying the next prop of a linked-list node.

bones05:07:24

From what I understand the data structures are implemented underneath as linked-lists but that functionality isn’t surfaced anywhere for me to use explicitly as a linked-list. Does that make sense?

seancorfield05:07:28

No, I'm not following you...

seancorfield05:07:16

user=> (def a '(1 2 3))
#'user/a
user=> (first a)
1
user=> (rest a)
(2 3)
user=> (cons 0 a)
(0 1 2 3)
user=>

raspasov05:07:13

I would agree with @seancorfield; probably there are exercises with greater learning benefit in Clojure than building a linked list 🙂

bones05:07:27

So how would I add say, a 7 between 2 and 3?

bones05:07:07

Yeah, there probably are but now this is a bee in my bonnet ha ha

raspasov05:07:38

@bones you…. don’t… if you need that kind of random associative behavior, you can use a sorted map

raspasov05:07:50

Actually… you’re asking to add in-between; my bad

bones05:07:18

Sorry, I’m probably missing something simple here but I just don’t see how you can use a list like a linked-list

seancorfield05:07:46

@bones remember that Clojure data structures are immutable and persistent. You don't "edit" lists, you construct new ones. So if you had '(1 2 3) and wanted to produce '(1 2 7 3) you'd need to walk the list, building a new list, with the extra element constructed as part of that.

bones05:07:48

The type is usually nil | node, not list

bones05:07:37

Yip I’m with you on that one @seancorfield :thumbsup:

seancorfield05:07:03

(def a '(1 2 3))
(concat (take 2 a) (cons 7 (drop 2 a)))

bones05:07:41

ha ha yeah, i was imagining something like that

seancorfield05:07:29

We just don't really think of it as a linked list -- that's a fairly generic sequence operation.

seancorfield05:07:51

Because it works on a vector too:

user=> (def a [1 2 3])
#'user/a
user=> (concat (take 2 a) (cons 7 (drop 2 a)))
(1 2 7 3)
user=>

bones05:07:11

Yip :thumbsup:

seancorfield05:07:51

And of course it "works" on hash maps too -- it just doesn't make much sense:

user=> (def a {:a 1 :b 2 :c 3})
#'user/a
user=> (concat (take 2 a) (cons 7 (drop 2 a)))
([:a 1] [:b 2] 7 [:c 3])
user=>

seancorfield05:07:06

(and don't rely on the order of keys in a hash map!)

bones05:07:29

Roger that, thanks for the help

raspasov05:07:49

another way:

(let [a [1 2 3]]
  (apply conj (subvec a 0 2) 7 (subvec a 2)))
=> [1 2 7 3] (only works with vectors)

seancorfield05:07:50

For example

user=> (def a {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9 :j 10})
#'user/a
user=> (concat (take 2 a) (cons 7 (drop 2 a)))
([:e 5] [:g 7] 7 [:c 3] [:j 10] [:h 8] [:b 2] [:d 4] [:f 6] [:i 9] [:a 1])
user=>

stopa05:07:39

Hey team, noob question: https://github.com/stopachka/clap/blob/master/deps.edn If I build a jar and run it, is there a way I could connect to it via nrepl? (I am using depstar + deps -- am not quite sure what command to run, or if my deps.edn is proper -- perhaps I should also include nrepl in the uberjar extra-deps?)

jumar05:07:21

I'd avoid adding nrepl and other dev-only dependencies to the uberjar. What I occasionally use for interacting with running JAR is Socket REPL: https://clojure.org/reference/repl_and_main#_launching_a_socket_server

seancorfield05:07:32

Yup. +1 for using a Socket REPL.

seancorfield05:07:40

I never use nREPL these days. I run a Socket REPL in many of our processes locally, on QA, and on production so I can connect to them either via telnet or via an SSH tunnel and use my editor (Atom/Chlorine) to connect to the Socket REPL and interact with the live, running process.

seancorfield05:07:58

The nice thing about the Socket REPL is that it is built into Clojure so you can run absolutely any Clojure program -- a basic REPL, a script, a JAR file, even a Java program that has Clojure as a dependency -- and tell Clojure to start a Socket REPL on a given port using just a JVM option.

seancorfield06:07:53

^ @U0C5DE6RK If you really do need to use nREPL, you have to make it a regular dependency of your project so that it gets added to the uberjar, and you have to include code in your main function (or somewhere that you can trigger, once it is running) to explicitly start the nREPL server. But I'd really recommend not doing that and use the plain Socket REPL instead.

stopa16:07:04

I see, thanks team! Wasn't aware of socket repl -- makes a lot of sense!

stopa16:07:46

To make sure I understand -- the purpose of nrepl then, is for ide integrations, etc (as it works with messages and values, etc) -- otherwise socket repl works just as well -- do I understand correctly?

seancorfield16:07:19

nREPL is a protocol (as well as several libraries) and it provides some IDE niceties over a bare REPL, such as some degree of async execution and interruptability, but it breaks the streaming idiom of the plain REPL (which the socket REPL and the built-in prepl also follow). I just don't find it adds enough value to warrant having that dependency added to everything. I like being able to decide at process start time whether or not I want a REPL (starting a Socket REPL is just a JVM option so it's completely external to the process and you can control the port or even have it pick one, and you can choose a plain Socket REPL or a prepl for tooling). Basic command-line tooling (e.g., telnet) can connect to it so you don't need anything fancy on the client. Clients can connect using the Socket REPL and then start additional REPLs as they wish (and there's talk of nREPL clients being able to connect to a Socket REPL and "upgrade" it to an nREPL process).

seancorfield16:07:04

The Socket REPL is just like the plain command-line REPL you'd get from lein repl or clj (or clj -r if you're providing other "main" options). Several editor/IDE packages are starting to support it -- Chlorine for Atom was designed specifically for Socket REPL from day one (and it actually upgrades it using an "unrepl" library to provide a simple nREPL-like protocol without needing any dependencies).

seancorfield16:07:38

I think it's important to have simple tools that compose well, to avoid some of the weirdness that can creep in with more complex tooling. For example, I've seen people run into problems with some lazy expressions because something about nREPL changes the behavior from how core Clojure semantics are intended to behave: so you see the correct behavior in clj but the wrong behavior in lein repl and all nREPL-based tooling).

stopa18:07:01

Wow, thanks for the deep context Sean! Agreed 100% on the philosophy of simple and composable tools. I somehow assumed that the only way to connect to running processes was nrepl. Glad to know this, and happy to see how the clojure env is getting simpler and simpler

seancorfield18:07:09

Back in the day, Leiningen and nREPL (and CIDER) were really the only games in town. Then Boot came along but still used nREPL. And other editors had packages developed for them that implemented the nREPL protocol. I switched from Emacs to Atom back in 2015 I think, using ProtoREPL which supported nREPL and I also switched from Leiningen to Boot around that time. Then at work we switched from Boot to the Clojure CLI (back in 2018?) and we'd already switched from using nREPL in our production processes to Socket REPL (and just telnet or unrepl as a command-line client). ProtoREPL stopped being maintained, so I was glad that Chlorine appeared, and I loved that it only needed a Socket REPL, which allowed us to drop nREPL as a dev dependency as well! My team mate uses Emacs but he also doesn't like CIDER/nREPL so he uses a plain command-line REPL and inferior mode, or simple telnet into a Socket REPL...

❤️ 3
seancorfield18:07:21

(for context, we started using Clojure at work in early 2011 and went to production with Clojure 1.3 alpha 6 or 😎

❤️ 3
stopa18:07:33

Wow, awesome!!

stopa20:07:02

quick noob q: https://github.com/stopachka/jt/blob/master/deps.edn#L6 I added -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" in :jvm-opts I run clj -m jt.core telnet 127.0.0.1 5555 currently says conn refused -- Am not quite sure what I should be doing to set up the socket repl. Thoughts much appreciated : }

seancorfield20:07:54

Whitespace confuses the shell script that builds the cached files. You need to use , instead (which Clojure treats as whitespace): https://github.com/seancorfield/dot-clojure/blob/develop/deps.edn#L67

seancorfield20:07:52

Also, I think :jvm-opts only works in an alias -- not at the top-level.

👍 3
seancorfield20:07:51

So, move your :jvm-opts to a :socket alias, change the spaces for commas, then run your code with clojure -A:socket -m jt.core

👍 3
seancorfield20:07:24

You'll see that referred to as the "Corfield comma" sometimes: https://insideclojure.org/2020/02/11/custom-repl/ "Or you can pack it into your deps.edn (using the Corfield comma as bash-safe whitespace):" 🙂

❤️ 3
stopa20:07:47

haha love it! def copied that from your dot-clojure repo. Thank you : }

stopa20:07:04

update: okay, -A:socket works great!

seancorfield21:07:42

Cool. Feel free to copy as much as you want from that dot-clojure repo -- it's intended as a "starter kit" for your own ~/.clojure/deps.edn

❤️ 3
seancorfield21:07:54

(it literally is mine)

❤️ 3
stopa21:07:32

Okay, one more noob question: I am not trying to test out, connecting to a jar. https://github.com/stopachka/jt/blob/master/deps.edn#L9 I added the jvm-opts in the uberjar alias as well When I build and run:

clojure -Spom && clojure -A:uberjar jt-2020-07-01_14:20:35.jar -C -m jt.core
java -jar jt.jar 
Then
telnet 127.0.0.1 50505
Does not seem to connect. Would I need to do something different for uberjar?

seancorfield21:07:18

JVM opts affect what you are running now -- so you added them to the :uberjar alias and it would start a Socket REPL while it is building the JAR file (and then close it).

😆 3
seancorfield21:07:42

In order to affect java -jar jt.jar, you have to provide the JVM options on that java command.

stopa21:07:02

Very cool!

seancorfield21:07:16

java -Dclojure.server.repl='{:port 50505 :accept clojure.core.server/repl}' -jar jt.jar

seancorfield21:07:00

You'll need quotes (single or double) to make sure the java command sees that -D... as a single argument -- and you won't need commas here (although they would be accepted).

stopa21:07:14

ohmagad -- yess! thanks Sean! works like a charm : }

seancorfield21:07:40

Just as an FYI, because it's helpful to understand the implications of this sort of thing:

$ java -cp jt.jar clojure.main
will start a regular Clojure REPL with all of the code in your jt.jar on the classpath, which can be helpful for debugging stuff or just experimenting and
$ java -cp jt.jar clojure.main -m jt.core
will run your main program, just like java -jar jt.jar does.

seancorfield21:07:20

(because the JAR file contains all of Clojure, so it can run REPLs, run other scripts...)

❤️ 3
stopa21:07:15

great to know! will move towards using that -- i.m.o no need to specify main during build step then

seancorfield21:07:33

This can be useful for stuff like:

(! 1162)-> java -cp uberjars/worldsingles-1.0.0.jar clojure.main -e '(clojure-version)'
"1.10.2-alpha1"
figuring out which version of Clojure a particular JAR file was built with.

❤️ 3
seancorfield21:07:16

Having the default -main be from your own namespace is useful. I'm just pointing out that it isn't all you can do 🙂

❤️ 3
seancorfield21:07:46

We have multiple -main functions in a lot of our JAR files and we can run them like that.

❤️ 3
seancorfield21:07:05

You can also call individual functions inside a JAR from the command-line

(! 1173)-> java -cp uberjars/worldsingles-1.0.0.jar clojure.main -e "((requiring-resolve,'worldsingles.covalence.source/coalesce-source-name),\"Hello 1234.\")"
"Hello 0."
(`coalesce-source-name` replaces strings of digits with a single 0)

😍 3
seancorfield21:07:30

Sometimes getting the quoting right for stuff like this can be tricky tho'.

😆 3
stopa21:07:43

leveled up with this. Sending you some positive vibes Sean!

andy.fingerhut06:07:57

@bones It sounds like you perhaps want to make a mutable linked list data structure, implemented in Clojure? Clojure's built-in lists are immutable, so they don't provide you with mutable linked lists, if that is what you are thinking of. You can create mutable data structures in Clojure -- probably deftype is at least one straightforward way to do so.

Shreyanshi Bharadia07:07:18

Hey, can any one help me how to clear out data from the forms in Clojurescript?

raspasov07:07:39

Do you have a code sample? Are you using React?

jacklombard11:07:48

Please use code formatting and paste again

jacklombard11:07:08

you can do that with by typing `

bones07:07:02

I think you’re right @andy.fingerhut – a mutable linked-list data structure. Unfortunately I don’t think I explained myself well enough before, hence all the talk around lists

andy.fingerhut07:07:47

Clojure's built-in lists let you read the next pointers of an existing list, and create new lists with elements prepended to the beginning of other existing lists, but not to mutate the next pointers of an existing list, as C/C++/Java implementations of linked lists often do.

andy.fingerhut07:07:01

That is by design, not by accident.

bones09:07:07

Yeah, that's what I was trying to explain; the examples given all used various combinations of list + concat to replicate 'modifying' a list

bones09:07:53

But in the end it was still a list; not a list of nodes linked together via a 'next' ref

bones09:07:44

By "read the next pointers" are you referring to the seq operations of next/rest?

andy.fingerhut16:07:01

If you are using Clojure on the JVM, there is a library that can be used to draw pictures of the JVM objects in memory, if that is of any interest to you: https://github.com/jafingerhut/cljol

andy.fingerhut16:07:25

There is a link in the README to a gallery with some sample figures and explanatory text of how they were created, and how to read them.

andy.fingerhut16:07:37

There is a class clojure.lang.PersistentList that has object fields named first and _rest , where _rest functions as a next pointer.

andy.fingerhut16:07:42

But it is immutable.

andy.fingerhut16:07:20

Different Clojure collections provide different implementations of next/rest. The one for clojure.lang.PersistentList just returns the value of _rest when you call next, IIRC

andy.fingerhut16:07:11

If you give the cljol library a try, you can do something like this in a REPL after installing GraphViz (see cljol's README for instructions): (require '[cljol.dig9 :as d]) followed by (d/view [(list 1 2 3)])

andy.fingerhut16:07:38

I have not tested that on Windows, but have seen it working on macOS and Linux systems.

bones20:07:46

Thanks heaps @andy.fingerhut, I'll check it out

walterl10:07:26

Python 3 allows underscores in integer literals to aid in readability (https://docs.python.org/3/reference/lexical_analysis.html#integer-literals). Does Clojure support something similar?

sogaiu11:07:52

i don't think it does.

👍 3
Alex Miller (Clojure team)12:07:18

this was added to Java a few releases ago and generally Clojure has "followed Java" on parsing numbers so I think such an enhancement would be in line but doesn't seem very urgent

👍 3
Timofey Sitnikov11:07:50

Good morning Clojurians. I am getting tripped up by something simple. Looking at the code here: (model/save-user (-> req :application/component :database)))) (https://github.com/seancorfield/usermanager-example/blob/91732de94a6fffafa6c08e595a6d744a83971e46/src/usermanager/controllers/user.clj#L86). The line of code seems to be passing a single parameter where the function [db user] (https://github.com/seancorfield/usermanager-example/blob/91732de94a6fffafa6c08e595a6d744a83971e46/src/usermanager/model/user_manager.clj#L129) takes 2 parameters. How does the model get the second parameter?

walterl11:07:22

The model/save-user call is in a ->> threading macro form, so the return value of (persistent! x) is inserted as the last arg to the model/save-user call.

Timofey Sitnikov11:07:29

Oh, OK, so the argument to persistent! is the return from the previous statement reduce-kv (fn ... correct?

walterl11:07:36

That's the magic of the threading macros 🙂

Timofey Sitnikov11:07:11

Very well, it does make it look better, just have to keep it straight in the head. Probably becomes second nature after while.

Timofey Sitnikov11:07:17

Thanks for helping.

walterl11:07:27

They're very useful and (consequently) used quite often, so it would be good to make sure you understand them. I'd recommend starting with https://clojuredocs.org/clojure.core/-%3E (most common), and then also ->>, as->, some-> and cond->.

Timofey Sitnikov12:07:03

I read more about it, and at first ->> looks stupid because it keeps appending previous result to the end, but looking deeper into it, it ideally reverses the lisp form into much more readable form, very cool.

seancorfield16:07:30

A good rule of thumb is: -> for "values" and "collections" but ->> for "sequences" -- following Clojure's natural argument order. So ->> is good with map/filter/etc and the into form where you're already supplying the target collection. Try not to mix -> and ->> -- break the forms apart. Unless an expression is all about sequences, -> is usually the best place to start since you can use other threading forms inside it (`->>`, as->, even cond-> and cond->>)

✔️ 3
3
Nathan Saritzky14:07:04

Is there an equivalent to ns-refers that works in Clojurescript? I want to shrink some boilerplate around import React classes. Probably excessive for what I'm doing right now, but I'm curious.

sova-soars-the-sora14:07:06

Shrinking boilerplate is created your own domain-specific-languge Lite.

Nathan Saritzky14:07:38

That's an interesting point, but, nevertheless, I'm curious.