This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-07-01
Channels
- # atom-editor (11)
- # babashka (25)
- # beginners (142)
- # boot (9)
- # calva (3)
- # cider (19)
- # clara (15)
- # clj-kondo (6)
- # cljs-dev (20)
- # clojars (11)
- # clojure (164)
- # clojure-dev (9)
- # clojure-europe (6)
- # clojure-italy (17)
- # clojure-nl (3)
- # clojure-spec (19)
- # clojure-sweden (10)
- # clojure-uk (23)
- # clojurescript (34)
- # code-reviews (31)
- # conjure (20)
- # cursive (14)
- # datomic (54)
- # emacs (1)
- # fulcro (51)
- # graalvm (24)
- # graphql (6)
- # helix (3)
- # jobs (3)
- # kaocha (1)
- # malli (2)
- # meander (15)
- # off-topic (81)
- # pathom (2)
- # re-frame (43)
- # reagent (26)
- # reitit (1)
- # releases (1)
- # sci (12)
- # shadow-cljs (29)
- # sql (22)
- # timbre (3)
- # tools-deps (15)
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*
user=> (macroexpand '(-> file slurp json/read (#(select-keys % keys))))
((fn* [p1__145#] (select-keys p1__145# keys)) (json/read (slurp file)))
you don't need `#() for this
(-> file slurp json/read (select-keys keys))
good job finding why it didn't work though
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.is that ClojureScript?
yes it is
using shadow-cljs repl
wait i just got it working
reloaded the browser window. sorry for the noise.
i've got too used to never having to reload the browser window because of cljs tooling heheh
Noob question: How does clojure do
(= [{:foo 1}, {:foo 2}] [{:foo 1} {:foo 2}])
Does it in effect do a deep equality check?
@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
Thanks @raspasov, do you mean use vectors for the implementation? I was just looking to make my own linked-list as a learning experiment
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?
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
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?
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
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
in the middle would be defrecord
which both lets you define things as keys in a map, and lets you implement the interfaces
@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...
It's one of those things that is a good exercise in, say, OO languages that don't have native linked lists...
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.
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?
No, I'm not following you...
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=>
I would agree with @seancorfield; probably there are exercises with greater learning benefit in Clojure than building a linked list 🙂
@bones you…. don’t… if you need that kind of random associative behavior, you can use a sorted map
Sorry, I’m probably missing something simple here but I just don’t see how you can use a list like a linked-list
@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.
Yip I’m with you on that one @seancorfield :thumbsup:
(def a '(1 2 3))
(concat (take 2 a) (cons 7 (drop 2 a)))
We just don't really think of it as a linked list -- that's a fairly generic sequence operation.
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=>
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=>
(and don't rely on the order of keys in a hash map!)
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)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=>
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
?)
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
Yup. +1 for using a Socket REPL.
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.
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.
^ @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.
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?
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).
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).
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).
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
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...
(for context, we started using Clojure at work in early 2011 and went to production with Clojure 1.3 alpha 6 or 😎
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 : }
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
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
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):" 🙂
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
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?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).
In order to affect java -jar jt.jar
, you have to provide the JVM options on that java
command.
java -Dclojure.server.repl='{:port 50505 :accept clojure.core.server/repl}' -jar jt.jar
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).
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.(because the JAR file contains all of Clojure, so it can run REPLs, run other scripts...)
great to know! will move towards using that -- i.m.o no need to specify main during build step then
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.Having the default -main
be from your own namespace is useful. I'm just pointing out that it isn't all you can do 🙂
We have multiple -main
functions in a lot of our JAR files and we can run them like that.
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
)Any time!
@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.
Hey, can any one help me how to clear out data from the forms in Clojurescript?
Please use code formatting and paste again
you can do that with by typing
`
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
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.
That is by design, not by accident.
Yeah, that's what I was trying to explain; the examples given all used various combinations of list + concat to replicate 'modifying' a list
But in the end it was still a list; not a list of nodes linked together via a 'next' ref
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
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.
There is a class clojure.lang.PersistentList that has object fields named first
and _rest
, where _rest
functions as a next pointer.
But it is immutable.
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
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)])
I have not tested that on Windows, but have seen it working on macOS and Linux systems.
Thanks heaps @andy.fingerhut, I'll check it out
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?
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
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?
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.
Oh, OK, so the argument to persistent!
is the return from the previous statement reduce-kv (fn ...
correct?
Very well, it does make it look better, just have to keep it straight in the head. Probably becomes second nature after while.
Thanks for helping.
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->
.
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.
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->>
)
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.
Shrinking boilerplate is created your own domain-specific-languge Lite.
That's an interesting point, but, nevertheless, I'm curious.