This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-19
Channels
- # adventofcode (7)
- # aws (6)
- # babashka (7)
- # beginners (200)
- # calva (49)
- # chlorine-clover (3)
- # cider (24)
- # clj-kondo (115)
- # cljs-dev (5)
- # cljsrn (16)
- # clojure (44)
- # clojure-australia (9)
- # clojure-czech (1)
- # clojure-dev (1)
- # clojure-europe (63)
- # clojure-france (6)
- # clojure-losangeles (1)
- # clojure-nl (2)
- # clojure-spec (27)
- # clojure-uk (77)
- # clojurescript (45)
- # clojurewerkz (3)
- # conjure (5)
- # cryogen (1)
- # cursive (2)
- # datahike (6)
- # datascript (3)
- # datomic (18)
- # fulcro (5)
- # graalvm (55)
- # jobs (3)
- # luminus (4)
- # malli (1)
- # pathom (1)
- # reagent (16)
- # shadow-cljs (67)
- # spacemacs (18)
- # sql (57)
- # testing (6)
- # tools-deps (9)
I’m going through 4clojure exercises and was asked to create palindrome checker. What I’ve done is
(defn palindrome? [v]
(loop [original v reversed (reverse v) result false]
(if (empty? original)
result
(if (= (first original) (first reversed))
(recur (rest original) (rest reversed) true)
(recur (rest original) (rest reversed) false)))))
then I went to see other’s solutions and I ran into
(defn palindrome? [v] (= (reverse v) (reverse (reverse v))))
and I felt stupid 😄There’s a bug in your code. It looks like you will report if the last and last item are the same
Ohh, great catch, thank you.
They could've substituted (reverse (reverse v))
with (seq v)
.
know what you feel. IM a little but further on 4 clojure and still feel stupid @peb.brzezinski
I mean I’ve been a developer for couple of years and I still get that feeling every day 😄
oke, so not wierd that I feel like a super beginner a lof when solving learning challenges
How does that work @jaihindhreddy? (= (seq [1 2 3]) (reverse [1 2 3]))
why are they equal? :thinking_face:
seq
returns a sequence from its argument. reverse
returns a sequence of the elements in its argument in the reverse order.
So, if we do (defn palindrome? [s] (= s (reverse s)))
, it won't do the right thing for strings, because the string "aba"
and the sequence of characters '(\a \b \a)
don't compare as equal.
So, either seq
or calling reverse
twice on the input "converts it" to a seq, in a manner of speaking.
See, https://clojure.org/reference/sequences and https://clojure.org/guides/equality in the docs for more details..
Turns out I made a mistake. seq
returns nil
when the argument doesn't have anything in it. So, my impl fails for the empty string, because (reverse "")
returns the empty list, which is not the same as nil
. So, this would be a correct implementation:
(defn palindrome? [s]
(= (seq s)
(seq (reverse s))))
That being said, if we wanted this function to work only with strings, this would be a more efficient way to do it:
(defn palindrome? [s]
(= s (clojure.string/reverse s)))
PS: Here's a weird way I found to implement the same:
(def palindrome?
(comp zero? dec count set (juxt seq (comp seq reverse))))
Nice, thanks a lot for the in detail explanation!
Point free FTW 🙂
4clojure has some amazing and very stupid examples of point free programming
(it's a fun exercise to see why they work, but they are often terrible as code per se)
anyway why not (comp zero? dec count set (juxt seq reverse) seq)
(I almost put identity in there, but seq has a nice short name)
@U051SS2EU that doesn't work on the empty string, because (reverse "")
evals to ()
, which is not the same as nil
.
it calls seq first so both are nil
oops! misread it 😅
(ins)user=> ((comp zero? dec count set (juxt seq reverse) seq) "aba")
true
(cmd)user=> ((comp zero? dec count set (juxt seq reverse) seq) "abc")
false
@jaihindhreddy actually no you are right, I had it wrong
(cmd)user=> ((comp zero? dec count set (juxt seq reverse) seq) "")
false
(cmd)user=> ((comp set (juxt seq reverse) seq) "")
#{nil ()}
because (reverse nil) evaluates to () :/@peb.brzezinski what does reverse [1 2 3]
returns ?
I know it returns (3 2 1)
But then I don’t get why (1 2 3) is equal to (3 2 1)
Seems like I need to read a bit about seqs. Anyway, thanks for making me dig deeper 🙂
lol, right
I have to repeat every character in a seq 2 times. Is this a good solution or can it be improved
(defn my-repeat2[s]
(flatten(map (fn[item](repeat 2 item)) s)))
I'd prefer mapcat to map+flatten, I also don't often use flatten at all since if your items happened to be vectors as well it would give you something unexpected (try your fn with [[1 2] [3 4]]). Also for the sake of two items, I wouldn't bother with a repeat, but that's personal preference.
(defn my-repeat2[s]
(mapcat (fn [i] [i i]) s))
What’s good or not depends on the tradeoffs you’re willing to make. I would strongly consider adding spaces to make it match more common spacing patterns:
(defn my-repeat2 [s]
(flatten (map (fn [item] (repeat 2 item)) s)
For my favorite version of the implementation I would use interleave
:
(defn my-repeat2 [s]
(interleave s s))
@UGFL22X0Q thanks . that is a lot o f shorter code
This is how I did the same thing
(seq (reduce #(conj %1 %2 %2) [] x)))
the core library is full of gems for very specific use cases, but I’d argue it’s better to have a good grasp of map
mapcat
reduce
since they’re much more versatile.
One downside to using reduce here is that you loose any laziness, so (take 5 (my-repeat2 (range)))
will never finish for example. interleave
and mapcat
don't have that limitation, but whether or not that's an issue is down to context!
flatten is very brittle (breaks on common input types) and expensive (does a non-tail-recursive tree walk on input) - it's always better to use concat (or mapcat, or for) where possible
user=> (for [el [1 2 3] e [el el]] e)
(1 1 2 2 3 3)
(for is not as nice as other options here, but often fits nicely into other contexts - eg. where you are already using for and can do this in an extra binding clause)
@roelof and @peb.brzezinski when I compare my code that of more advanced Clojure developers, I too am often humbled. I’ve still much to learn, and I since I find joy in learning, especially amongst such friendly guides, I find that exciting.
Sure thing, this is the best way. I always enjoy a good feedback/peaking at a better solution. This is one of the reasons I try to work through 4clojure and one of the reasons I’ve been going back to exercism so often 🙂
that are the two thing I do now. 4clojure for the practising and exercism for practising and the feedback of hopefully experienced developers
Hello all, does anybody have a quick reference they can point me to for setting up a networked repl connection? I'd like to remotely repl into a production app using the CLJ CLI. Thank you!
How do I make sure cloverage covers spec/keys. For some reason my test aren't covering most of them (under the forms %)
@andyfry01 socket REPL, pREPL, nREPL?
there is no "socket REPL", there is a stream-based clojure.main REPL. all of these repls operate remotely over sockets.
I am not sure, but I think the phrase "socket REPL" is in fairly common usage today, isn't it? If you are trying to change that, you might want to start by calling it something else here: https://clojure.org/reference/repl_and_main#_launching_a_socket_server
I am not claiming this is "official" terminology, just commonly used terminology, e.g. it is all over the README and deps.edn file in this repo: https://github.com/seancorfield/dot-clojure
well if you look carefully, you'll see that page says "socket server" (definitely a thing!) and "socket-based REPL"
and "socket server with a repl listener"
and then uses the shorter term near the end as shorthand (which I'm happy to un-shorthand if that makes it clearer)
the big point really being that Clojure does not provide a socket repl, it provides a generic socket server, which can run any function, one example of which is a stream-based repl. prepl is another such example, a streaming data repl.
That all makes sense. I think developers would like a short name to call the thing they use when they use a generic socket server, which runs a stream-based repl. So far it seems like "socket REPL" is that short term used most often for that thing, as much as that name might be imprecise.
solved
(defn my-range [low high]
(if (>= low high)
()
(cons low (my-range (inc low) high))))
have you had a look at some big ranges, (take 1 (my-range 1 10000))
, lazy-seq
might be useful!
is this well
(defn my-range [low high]
(lazy-seq
(if (>= low high)
()
(cons low (my-range (inc low) high)))))
Instead of if you could work with when, some people might not agree though. I prefer it because it's short
oke, I like this more. Found it on the net
(defn my-range2 [low high]
(take-while
(partial > high)
(iterate inc low)))
One last question for today (I promise :P). I tried to implement dedupe function
(defn my-dedupe [val]
(reduce #(if (= (last %1) %2)
%1
(conj %1 %2)
) val))
, but when trying to execute it like (my-dedupe "aabbcc")
I get an error Don't know how to create ISeq from: java.lang.Character
and I’m not really sure what’s wrong here.your paren placement is super weird
try using an init for the collection rather than the implicit one (first item in (seq val)
)
It was easier for me to read that way, but I get your point.
instead of (reduce f "string")
, (reduce f [] "string")
@peb.brzezinski it will be easiest in the long term if we all agree on what the layout should look like - lisp conventions don't put hanging )
on its own line, especially not as a leading character before other forms
Got it 👍
it's kind of like driving on the same side of the road - it's easier if we all stick to the agreement
Sure, I have nothing against conventions. Thanks for pointing that out.
(fn [val]
(reduce #(if (= (last %1) %2)
%1
(conj %1 %2)) [] val))
this worked indeed. Thanks once again 🙂also, not everyone agrees here, but I prefer to align args horizontally if they are siblings - I'd write that out as
(fn [val]
(reduce (fn [acc e]
(if (= (last acc) e)
acc
(conj acc e)))
[]
val))
Ah ok, so each arg to reduce on the same lvl.
I like that.
that makes it clearer to me - not everyone sticks to that of course
also, if a #()
is complex enough for line breaks, I prefer to be able to name args
but I tend to waste more vertical space than others prefer
I prefer being explicit too in that case, I’m just trying to get used to the shorthand version of anonymous fns.
(partial reduce
#(if (= (last %1) %2)
%1
(conj %1 %2))
[])
(that's not how I'd ever write anything)
I'd never specified a number myself, but that sounds about right
also I'd always replace #(do ...)
with an fn that has implicit do
and I never use #(let [... ...] ...)
this might just provoke a panic attack in that case
user=> (#(%2%1%):a{})
:a
#(%2%1%)
-> #(%2 %1 %)
-> #(or (get %2 %1) %1)
it can only get that ugly because #() uses reading rules for % that don't need whitespace
right, I also expanded the get logic (not quite accurately)
yeah, it's actually a if-not-found not a logical or, but that's messier to illustrate
thanks to the third parameter of get, and the leniency, you can replace any usage of get
with (get get get get)
user=> (= get (get (get get get get) get get))
true
it's the clojure code version of that famous "chicken" talk
semantic satiation is a hell of a drug (and it's nice to avoid it in our code if we want clarity, clojure's good at that actually)
hi:) New here - quick question on IDEs. I've been used to working with VS code for some time now, but I understood there are much stronger IDEs for Clojure. Anyone had any experience with the VS Code extensions for Clojure and can recommend?
The intellij + cursive option is very good too. I don’t write a ton of Java, but my experience is that if you do, you want a full-blown IDE.
Emacs is often praised. But the two times I tried I ended up in configuration hell and decision fatigue.
yeah, the emacs and vim integrations are quite featureful, but I wouldn't start learning emacs or vim just to use them with clojure
Calva is great out-of-the-box and you get auto-formatting and linting right along with it. I'd recommend that for a Clojure beginner since it does a lot for you without having to understand all the detail yet.
I'll +1 VS Code for Clojure development (although I use Clover, rather than Calva -- Clover requires a bit more knowledge of stuff, since it uses a Socket REPL instead of the more common nREPL that Leiningen starts up).
@ilan800 welcome! I'm the creator of Calva. It's made with people for whom VS Code is home turf. Please see Tao of Calva and see if it seems to chime with you. https://github.com/BetterThanTomorrow/calva/wiki#the-tao-of-calva
Im not really trying to change your mind, but if you want to start with a predefined emacs setup ready for clojure, I would use #practicalli spacemacs.d configuration and all instructions proposed there. It is the closest from the out of the box I know. Its emacs, out of the box and still open
Good evening, morning, night, day (depending where you live in the world!) Say I wanted add to an array this is found on a key in a map inside of a vector atom, how would I do that? Simple example: (def todos atom [{:id "1337 :desc "todo-list" :todos []} ]) How would I add todos this atom?
@georghagen (swap! todos conj ...)
@georghagen an atom is a container that manages the changes to its contents, the basic things needed are swap!
and deref
it looks like your atom has a weird structure, which means you would need to use a more complex call to swap! or use a simpler structure
for example
(ins)user=> (def todos (atom []))
#'user/todos
(ins)user=> (swap! todos conj {:what "homework" :when 0})
[{:what "homework", :when 0}]
currently your "todos" seems to contain metadata that isn't the contents, but about the contents
In JS I would just do todos[0].todos.push({id: 2, desc: "okdf"}). Yeah maybe I should just show you my real example
(defn create-checklist [] { :id (.toString (java.util.UUID/randomUUID)) :sequenceMap [] ;Map<Integer, UUID> / Map of question number vs question UUID, to create sequences for each checklist / :answerMap [] ;Map<Integer, UUID> :expectedAnswerMap [] ;Map<Integer, UUID> :isSigned false })
[] is never a map in clojure
yeah, that doesn't really look like clojure code (though it would compile)
ins)user=> (def todos (atom {:id 0 :items []}))
#'user/todos
(ins)user=> (swap! todos update-in [:items] conj {:what "homework" :when 0})
{:id 0, :items [{:what "homework", :when 0}]}
this is closer to what you initially had
but using an atom and def already implies a global / singular nature that doesn't line up with the particularity of having and id etc. at the top level
right, that's the usual role of an atom
on reflection my first example is closer to what you had, I misread it
having a vector at the top level of an atom is relatively rare, usually you want keyed rather than indexed lookup
but perhaps your db is actually a queue or stack
Nono, it was a choice made without too much thought. I am coming straigth from javascript
right, instead of [{:id 10 ... ...} {:id 20 ... ...}]
we'd usually have {10 {... ...} 20 {... ...}}
(or in your case actually the uuids you generate)
Yeah of course, makes much more sense to have the UUID as a key to your other checklist data
unlike js we only sometimes use hashes as "objects", and generally use them as databases
so more like
(ins)user=> (def todos (atom {}))
#'user/todos
(ins)user=> (swap! todos assoc (java.util.UUID/randomUUID) {:what "homework"})
{#uuid "701b9c5d-6d61-45e6-bf81-8024953aa50d" {:what "homework"}}
@georghagen a pattern you'll likely see is that most of the sort of code you'd usually write is replaced with a few combinations of collection operations in clojure code
Yeah, that's what I really love about Clojure. I just have to get more experience to know my way around the collection operations! Thanks a lot for all the input, can't wait untill I can start contributing back to the clojure community myself 😃
GN my friends. Hopefully I can work on exercism or 4clojure or my web project tomorrow
IS there a good free hosting platform for the website im building with compujure and ring ?
There are cloud providers who offer free tiers. The JVM is often supported. A simple provider would be Heroku, but bigger (and more complicated ones) like AWS, Azure and GCP provide free tiers as well as far as I know.
You could try this: https://wiki.helionet.org/features/java You'd need to switch to https://github.com/marchrock/ring-tomcat-adapter I think. And I've never tried them.
DigitalOcean, Linode, AWS, are popular options, they have things in the 5$ a month range.
@georghagen You should maybe consider looking into the set data-structure and relational operations. Your collection looks like a set (since it has unique ids). You can use index
to get a map from a unique key to each item in the collection, you can join sets arbitrarily, select on a predicate and so on.
to me it doesn't look like a set - sets are about things that only stand for themselves and comparison of membership, this looks more like a mapping to me
most of the data you'd put in a "todo list" entry doesn't stand for itself, and most todo list operations are not set operations
I’m currently diving deeper into logic (math and programming) and everything looks like a set to me…
I think I confused you with the todolist example. Its a checklist, which will have questions attaches to it. these questions will have their own id and description and an id which denotes their relation to an answer
is it an ordered thing for display?
I think thats the only thought behind having an integer tied to an UUID, to make sure everything is in the right sequence. But that could be implied in the datastructure. But if the client wants to be able to move it around, you might be in trouble? I don't know
yeah, so the best idea is probably to separate the UI layer (stuff about ordering and labeling) from the relational layer (links between questions and answers) and the storage layer (indexing by id)
clojure solutions to this sort of thing will definitely look unfamiliar coming from kotlin or js, and will often be specific to a particular UI tool (eg. reagent / re-frame)
OK, in that case we should probably go back to vector-of-maps (if there's only one global todo), or a map from session / user id to vector of maps
and that's either in an atom (in memory) or an intermediate structure used when talking to a db
If you are new to Clojure (or not!), we would love to have your feedback in the annual State of Clojure survey https://www.surveymonkey.com/r/clojure2021 - we've been doing this survey in the community for about a decade and it provides important information about trends and usage. If you're interested, last year's results can be found here: https://clojure.org/news/2020/02/20/state-of-clojure-2020
Done 🙂
The questions ask whether you use it professionally, and then veer off and assume you do
as it says at the top, just skip anything that doesn't apply
only the first 2 questions are required
I'm having trouble finding the clojure
equivalent of lein deps
, has anyone got it to hand?
i'm dockerizing something and the recommendation from the clojure image is to download deps ahead of time, but the examples are lein-based
You want an option to clojure
that will only try to load dependencies, and then stop?
A reasonably common way people deploy Clojure-based apps that avoids this question entirely is to create a JAR file that contains all of the code of your app and all of its dependencies, and then simply run java -cp ...
command on the system where the application runs. Then that system doesn't need to be able to access repositories containing libraries at all.
agreed with @U0CMVHBL2 that a jar is the simplest thing in the long run, depstar
does this task well with the clojure tool https://github.com/seancorfield/depstar
@U0E9KE222 If you're using an up-to-date clojure
CLI (1.10.1.697 or later), you can use the -P
option. See http://clojure.org/releases/tools
That will just "prepare" things, i.e., download the dependencies you need and then not run anything.
-P is the preferred way to do this