The only REPLs now are browser and node
> Given Closure Compiler & Library’s recent pace of change, we’ve decided to focus our energies on the essential browser and Node.js REPLs. We’ve also begun enrichening the CLI & REPL APIs to ease third party efforts to target these JavaScript environments.
(defn save-create-order
[{:keys [external-id service-company-id elevator-id
contract-id description status due-date
actual-start-time actual-end-time checklist-id}]
(swap! orders conj (create-order
external-id service-company-id elevator-id
contract-id description status due-date
actual-start-time actual-end-time checklist-id)))
You can also used named parameters if you prefer over passing a map, then it would be:
(defn save-create-order
[& {:keys [external-id service-company-id elevator-id
contract-id description status due-date
actual-start-time actual-end-time checklist-id}]
...)
My main point was that we generally need to know and choose whether "concurrency is applied automatically behind the scenes" to write correct programs -- whether parallel map or sequential map is the default one is less crucial in this regard.
Btw, it looks like pmap is superceded by clojure.core.reducers, which might be the closest to what you were looking for.
Last, automatic parallelization in general is a hard problem afaik (not sure about theoretical computability/limits thereof atm) -- orchestration of parallel tasks incurs additional costs, one has to avoid congestion etc.. I made a little experiment:
(ns my.scratchpad
(:require [clojure.core.reducers :as r]))
(defn mod-pow [b e m]
(int
(.modPow (BigInteger/valueOf b)
(BigInteger/valueOf e)
(BigInteger/valueOf m))))
(for [[name size tr] [["A" 10000000 inc]
["B" 1000000 #(mod-pow 1234 (- 0 5 (* % %)) 10001)]]]
(let [d (vec (range size))
_ (println "--" name "--")
v1 (time (->> d (mapv tr))) ; sequential evaluation
v2 (time (->> d (r/map tr) r/foldcat (into []))) ; reducers, default granularity
v3 (time (->> d (r/map tr) (r/fold 1 into conj))) ; reducers, granularity 1
v4 (time (->> d (pmap tr) (into [])))] ; pmap
(= v1 v2 v3 v4)))
;; -- A --
;; "Elapsed time: 233.065434 msecs"
;; "Elapsed time: 1506.866011 msecs"
;; "Elapsed time: 6245.779895 msecs"
;; "Elapsed time: 11999.532676 msecs"
;; -- B --
;; "Elapsed time: 2189.691216 msecs"
;; "Elapsed time: 1185.890224 msecs"
;; "Elapsed time: 1663.882219 msecs"
;; "Elapsed time: 2980.890022 msecs"
;; => (true true)
Here we simply map two functions (one very cheap, one slightly less so) over a collection. And it might very well be that I've missed something.
But assuming that r/map and r/fold implement parallelization which is reasonably effective and general, the observed computation time would mean that depending on e.g. the size of the collection and how effectively we can compute our function parallelization can be either notably beneficial or notably detrimental to performance. And we don't necessarily know anything about those factors up until computation time. So which method of computation should one choose in this case? I would expect that in general much more information is needed than what is available to the compiler (or we'd need to place stricter constraints on the language) to automatically parallelize computations with predictable performance.Thank you, that was very interesting for me.
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 😄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
How does that work @jaihindhreddy? (= (seq [1 2 3]) (reverse [1 2 3]))
why are they equal? :thinking_face:
I mean I’ve been a developer for couple of years and I still get that feeling every day 😄
@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)
oke
Seems like I need to read a bit about seqs. Anyway, thanks for making me dig deeper 🙂
YW
@peb.brzezinski It’s not, because (1 2 3)
is not a palindrome
lol, right 🤦
haha
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 🙂
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)))
@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.
@lee I agree totally with you. The community is very helpfull here
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
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))
@hobosarefriends 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!
as always it get down to context 🙂
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 %)
oke, so not wierd that I feel like a super beginner a lof when solving learning challenges
@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.
What do you experts think of my solution of the re-implmentation of range
(defn my-range [low high]
(if (> low high)
()
(cons low (my-range (inc low) high))))
check (my-range 1 1)
vs (range 1 1)
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!
oke, i have to think how to use that one
is this well
(defn my-range [low high]
(lazy-seq
(if (>= low high)
()
(cons low (my-range (inc low) high)))))
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)
There’s a bug in your code. It looks like you will report if the last and last item are the same
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
Ohh, great catch, thank you.
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)
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
your anonymous function gets the first two items of val
the first time it’s called
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 think roughly 3 forms is a sensible limit for shorthand lambdas
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 [... ...] ...)
Both of those give me anxiety. Good suggestion.
this might just provoke a panic attack in that case
user=> (#(%2%1%):a{})
:a
YIKES
why isn’t it nil?
#(%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
ohhh I didn’t see the last %
Wait isn’t it (get {} :a :a)
right, I also expanded the get logic (not quite accurately)
Ahh right!
illustrative
the third parameter of get is quite handy I think
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
Doesn’t ring a bell
https://www.youtube.com/watch?v=yL_-1d9OSdk&feature=emb_title
Thank you so much, I’m in tears, had to watch it twice. What a genius!
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)
still wonders if this is good written clojure ?
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.
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?
I use Calva for some weeks and very very happy with it
VS Code with Calva gives you all the essential features
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
Oh, also Paredit :)
Thank you all for the answers!:) I'll definitely start using Calva
Will take a look. Thank you!
Nice! Please feel welcome to #calva for assistance.
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
Any suggestions for 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
Instead of if you could work with when, some people might not agree though. I prefer it because it's short
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
Im using a template from a kotlin programmer
yeah, that doesn't really look like clojure code (though it would compile)
oke, I like this more. Found it on the net
(defn my-range2 [low high]
(take-while
(partial > high)
(iterate inc low)))
you can combine swap!
with update-in
to do nested operations
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
Aha! I see.. I am using to simulate a database
because I am creating mock data for a frontend
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
straight*
I have not chosen a db yet actually
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
Nice!
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 😃
@noisesmith 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 () :/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 ?
thanks all
@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
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.
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 don’t vouch for any of these, but it might be worth checking them out.
Ah right its a todo list. A simple map makes more sense there probably; KISS.
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?
yes
there are specialized data structures for that
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)
the frontend is being build with ionic in react
built*
I am creating the backend as a rest api
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
That's right!
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
Did already a few days ago
Done 🙂
I'm having trouble finding the clojure
equivalent of lein deps
, has anyone got it to hand?
thanks folks!
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?
clojure -Spath
should do the trick
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 @andy.fingerhut 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