beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-01-19T00:03:00.096200Z

The only REPLs now are browser and node

2021-01-19T00:08:04.096600Z

> 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.

2021-01-19T00:10:45.096800Z

(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)))

2021-01-19T00:13:44.097100Z

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}]
   ...)

2021-01-19T02:37:38.101300Z

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.

2021-01-19T02:39:22.101500Z

Btw, it looks like pmap is superceded by clojure.core.reducers, which might be the closest to what you were looking for.

2021-01-19T03:08:40.101900Z

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.

👀 1
Christian 2021-01-19T07:54:13.105200Z

Thank you, that was very interesting for me.

👍 1
Piotr Brzeziński 2021-01-19T11:28:09.108400Z

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 😄

😀 3
jaihindhreddy 2021-01-19T11:29:50.109600Z

They could've substituted (reverse (reverse v)) with (seq v).

roelof 2021-01-19T11:30:05.109900Z

know what you feel. IM a little but further on 4 clojure and still feel stupid @peb.brzezinski

Piotr Brzeziński 2021-01-19T11:32:47.111600Z

How does that work @jaihindhreddy? (= (seq [1 2 3]) (reverse [1 2 3])) why are they equal? :thinking_face:

Piotr Brzeziński 2021-01-19T11:33:17.111800Z

I mean I’ve been a developer for couple of years and I still get that feeling every day 😄

roelof 2021-01-19T11:33:35.112400Z

@peb.brzezinski what does reverse [1 2 3] returns ?

Piotr Brzeziński 2021-01-19T11:34:54.113Z

I know it returns (3 2 1)

Piotr Brzeziński 2021-01-19T11:35:04.113500Z

But then I don’t get why (1 2 3) is equal to (3 2 1)

roelof 2021-01-19T11:35:11.113800Z

oke

Piotr Brzeziński 2021-01-19T11:36:06.114400Z

Seems like I need to read a bit about seqs. Anyway, thanks for making me dig deeper 🙂

roelof 2021-01-19T11:36:25.115100Z

YW

2021-01-19T11:36:36.115400Z

@peb.brzezinski It’s not, because (1 2 3) is not a palindrome

👍 1
Piotr Brzeziński 2021-01-19T11:37:07.115600Z

lol, right 🤦

Piotr Brzeziński 2021-01-19T11:37:14.115800Z

haha

jaihindhreddy 2021-01-19T11:52:20.116Z

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))))

Piotr Brzeziński 2021-01-19T11:53:31.116200Z

Nice, thanks a lot for the in detail explanation!

Piotr Brzeziński 2021-01-19T11:53:46.116400Z

Point free FTW 🙂

roelof 2021-01-19T13:16:17.126300Z

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)))

lread 2021-01-19T13:16:39.126400Z

@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.

➕ 1
roelof 2021-01-19T13:17:29.127100Z

@lee I agree totally with you. The community is very helpfull here

Piotr Brzeziński 2021-01-19T13:18:59.128Z

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 🙂

roelof 2021-01-19T13:20:50.129100Z

that are the two thing I do now. 4clojure for the practising and exercism for practising and the feedback of hopefully experienced developers

Daniel Stephens 2021-01-19T13:21:47.129200Z

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))

Mno 2021-01-19T13:22:52.129600Z

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))

roelof 2021-01-19T13:27:17.130100Z

@hobosarefriends thanks . that is a lot o f shorter code

Piotr Brzeziński 2021-01-19T13:27:22.130300Z

This is how I did the same thing

(seq (reduce #(conj %1 %2 %2) [] x)))

Mno 2021-01-19T13:28:53.130500Z

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.

👍 1
Daniel Stephens 2021-01-19T13:33:29.131100Z

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!

👍 1
roelof 2021-01-19T13:46:13.131500Z

as always it get down to context 🙂

2021-01-19T13:51:22.132600Z

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!

zackteo 2021-01-19T14:04:32.135300Z

How do I make sure cloverage covers spec/keys. For some reason my test aren't covering most of them (under the forms %)

roelof 2021-01-19T14:06:25.135400Z

oke, so not wierd that I feel like a super beginner a lof when solving learning challenges

borkdude 2021-01-19T14:09:03.135800Z

@andyfry01 socket REPL, pREPL, nREPL?

alexmiller 2021-01-19T14:12:24.137700Z

there is no "socket REPL", there is a stream-based clojure.main REPL. all of these repls operate remotely over sockets.

roelof 2021-01-19T15:06:35.139200Z

What do you experts think of my solution of the re-implmentation of range

roelof 2021-01-19T15:06:39.139500Z

(defn my-range [low high]
  (if (> low  high)
    ()
    (cons low (my-range (inc low) high))))

bronsa 2021-01-19T15:10:19.139700Z

check (my-range 1 1) vs (range 1 1)

roelof 2021-01-19T15:12:48.140200Z

solved

(defn my-range [low high]
  (if (>= low  high)
    ()
    (cons low (my-range (inc low) high))))

Daniel Stephens 2021-01-19T15:16:39.140600Z

have you had a look at some big ranges, (take 1 (my-range 1 10000)), lazy-seq might be useful!

roelof 2021-01-19T15:19:32.140900Z

oke, i have to think how to use that one

roelof 2021-01-19T15:21:21.141200Z

is this well

(defn my-range [low high]
  (lazy-seq
  (if (>= low  high)
    ()
    (cons low (my-range (inc low) high)))))

2021-01-19T15:33:04.142400Z

4clojure has some amazing and very stupid examples of point free programming

2021-01-19T15:33:30.142600Z

(it's a fun exercise to see why they work, but they are often terrible as code per se)

💯 1
2021-01-19T15:34:48.142800Z

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)

💯 1
dpsutton 2021-01-19T15:36:27.144200Z

There’s a bug in your code. It looks like you will report if the last and last item are the same

👍 1
2021-01-19T15:36:50.144400Z

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

Piotr Brzeziński 2021-01-19T15:37:37.145500Z

Ohh, great catch, thank you.

2021-01-19T15:37:59.145800Z

user=> (for [el [1 2 3] e [el el]] e)
(1 1 2 2 3 3)

2021-01-19T15:38:56.146100Z

(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)

Piotr Brzeziński 2021-01-19T16:02:35.150700Z

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.

2021-01-19T16:07:49.152200Z

your paren placement is super weird

Thomas 2021-01-19T16:07:52.152400Z

your anonymous function gets the first two items of val the first time it’s called

👌 1
2021-01-19T16:08:05.152800Z

try using an init for the collection rather than the implicit one (first item in (seq val) )

Piotr Brzeziński 2021-01-19T16:08:36.153400Z

It was easier for me to read that way, but I get your point.

2021-01-19T16:08:51.153800Z

instead of (reduce f "string") , (reduce f [] "string")

2021-01-19T16:09:43.154800Z

@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

Piotr Brzeziński 2021-01-19T16:10:12.155400Z

Got it 👍

2021-01-19T16:10:19.155600Z

it's kind of like driving on the same side of the road - it's easier if we all stick to the agreement

Piotr Brzeziński 2021-01-19T16:11:03.156100Z

Sure, I have nothing against conventions. Thanks for pointing that out.

Piotr Brzeziński 2021-01-19T16:14:08.156700Z

(fn [val]
  (reduce #(if (= (last %1) %2) 
             %1
             (conj %1 %2)) [] val))
this worked indeed. Thanks once again 🙂

2021-01-19T16:16:35.158300Z

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))

Piotr Brzeziński 2021-01-19T16:17:05.158800Z

Ah ok, so each arg to reduce on the same lvl.

Piotr Brzeziński 2021-01-19T16:17:22.159200Z

I like that.

2021-01-19T16:17:24.159300Z

that makes it clearer to me - not everyone sticks to that of course

2021-01-19T16:17:51.159800Z

also, if a #() is complex enough for line breaks, I prefer to be able to name args

2021-01-19T16:18:13.160100Z

but I tend to waste more vertical space than others prefer

Piotr Brzeziński 2021-01-19T16:19:04.160700Z

I prefer being explicit too in that case, I’m just trying to get used to the shorthand version of anonymous fns.

2021-01-19T16:20:27.161800Z

(partial reduce
         #(if (= (last %1) %2)
             %1
             (conj %1 %2))
         [])

2021-01-19T16:21:17.162600Z

(that's not how I'd ever write anything)

dgb23 2021-01-19T16:21:18.162700Z

I think roughly 3 forms is a sensible limit for shorthand lambdas

2021-01-19T16:22:40.163Z

I'd never specified a number myself, but that sounds about right

2021-01-19T16:23:07.163600Z

also I'd always replace #(do ...) with an fn that has implicit do

2021-01-19T16:23:25.163900Z

and I never use #(let [... ...] ...)

dgb23 2021-01-19T16:23:45.164400Z

Both of those give me anxiety. Good suggestion.

2021-01-19T16:25:15.164800Z

this might just provoke a panic attack in that case

user=> (#(%2%1%):a{})
:a

dgb23 2021-01-19T16:26:59.165700Z

YIKES

dgb23 2021-01-19T16:27:55.166700Z

why isn’t it nil?

2021-01-19T16:28:47.167500Z

#(%2%1%) -> #(%2 %1 %) -> #(or (get %2 %1) %1)

2021-01-19T16:30:36.168700Z

it can only get that ugly because #() uses reading rules for % that don't need whitespace

dgb23 2021-01-19T16:31:17.169100Z

ohhh I didn’t see the last %

dgb23 2021-01-19T16:33:34.169600Z

Wait isn’t it (get {} :a :a)

2021-01-19T16:34:24.169800Z

right, I also expanded the get logic (not quite accurately)

dgb23 2021-01-19T16:34:34.170Z

Ahh right!

dgb23 2021-01-19T16:34:39.170200Z

illustrative

dgb23 2021-01-19T16:35:17.170400Z

the third parameter of get is quite handy I think

2021-01-19T16:35:20.170600Z

yeah, it's actually a if-not-found not a logical or, but that's messier to illustrate

👍 2
2021-01-19T16:35:46.170900Z

thanks to the third parameter of get, and the leniency, you can replace any usage of get with (get get get get)

2021-01-19T16:36:32.171100Z

user=> (= get (get (get get get get) get get))
true

dgb23 2021-01-19T16:36:59.171400Z

😂

2021-01-19T16:37:30.171600Z

it's the clojure code version of that famous "chicken" talk

dgb23 2021-01-19T16:37:43.171800Z

Doesn’t ring a bell

2021-01-19T16:39:30.172400Z

https://isotropic.org/papers/chicken.pdf

dgb23 2021-01-19T16:47:22.172600Z

Thank you so much, I’m in tears, had to watch it twice. What a genius!

2021-01-19T16:48:09.172800Z

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)

roelof 2021-01-19T16:54:25.173Z

still wonders if this is good written clojure ?

2021-01-19T17:00:49.173400Z

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

2021-01-19T17:04:21.173600Z

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

alexmiller 2021-01-19T17:34:28.174400Z

well if you look carefully, you'll see that page says "socket server" (definitely a thing!) and "socket-based REPL"

alexmiller 2021-01-19T17:34:48.174700Z

and "socket server with a repl listener"

alexmiller 2021-01-19T17:35:40.174900Z

and then uses the shorter term near the end as shorthand (which I'm happy to un-shorthand if that makes it clearer)

alexmiller 2021-01-19T17:37:21.175100Z

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.

2021-01-19T17:46:03.175300Z

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.

Ilan Uzan 2021-01-19T17:54:22.179Z

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?

roelof 2021-01-19T17:55:39.179400Z

I use Calva for some weeks and very very happy with it

🙌 1
dgb23 2021-01-19T17:56:48.179800Z

VS Code with Calva gives you all the essential features

dgb23 2021-01-19T17:58:25.180900Z

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.

dgb23 2021-01-19T17:59:48.182Z

Emacs is often praised. But the two times I tried I ended up in configuration hell and decision fatigue.

2021-01-19T18:00:31.182600Z

yeah, the emacs and vim integrations are quite featureful, but I wouldn't start learning emacs or vim just to use them with clojure

Joe 2021-01-19T18:05:30.184900Z

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.

seancorfield 2021-01-19T18:08:05.186300Z

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).

pez 2021-01-19T18:13:22.189800Z

@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

🎉 3
Joe 2021-01-19T18:13:26.189900Z

Oh, also Paredit :)

Ilan Uzan 2021-01-19T18:15:00.190700Z

Thank you all for the answers!:) I'll definitely start using Calva

Ilan Uzan 2021-01-19T18:15:22.190800Z

Will take a look. Thank you!

pez 2021-01-19T18:21:42.191800Z

Nice! Please feel welcome to #calva for assistance.

caumond 2021-01-19T18:51:17.198Z

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

Hagenek 2021-01-19T18:55:51.200500Z

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?

2021-01-19T18:56:57.201Z

@georghagen (swap! todos conj ...)

2021-01-19T18:57:43.201900Z

@georghagen an atom is a container that manages the changes to its contents, the basic things needed are swap! and deref

2021-01-19T18:58:46.202800Z

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

Hagenek 2021-01-19T18:58:56.203Z

Any suggestions for a simpler structure?

2021-01-19T19:00:01.204Z

for example

(ins)user=>  (def todos (atom []))
#'user/todos
(ins)user=> (swap! todos conj {:what "homework" :when 0})
[{:what "homework", :when 0}]

2021-01-19T19:00:17.204500Z

currently your "todos" seems to contain metadata that isn't the contents, but about the contents

Christian 2021-01-19T19:00:28.204900Z

Instead of if you could work with when, some people might not agree though. I prefer it because it's short

Hagenek 2021-01-19T19:00:57.205400Z

In JS I would just do todos[0].todos.push({id: 2, desc: "okdf"}). Yeah maybe I should just show you my real example

Hagenek 2021-01-19T19:01:24.205800Z

(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 })

2021-01-19T19:02:28.206200Z

[] is never a map in clojure

Hagenek 2021-01-19T19:03:18.207100Z

Im using a template from a kotlin programmer

2021-01-19T19:03:54.207700Z

yeah, that doesn't really look like clojure code (though it would compile)

roelof 2021-01-19T19:03:57.207800Z

oke, I like this more. Found it on the net

(defn my-range2 [low high]
  (take-while
    (partial &gt; high)
    (iterate inc low)))

2021-01-19T19:04:26.208400Z

you can combine swap! with update-in to do nested operations

👍 1
2021-01-19T19:05:15.208800Z

ins)user=&gt;  (def todos (atom {:id 0 :items []})) 
#'user/todos
(ins)user=&gt; (swap! todos update-in [:items] conj {:what "homework" :when 0})
{:id 0, :items [{:what "homework", :when 0}]}

2021-01-19T19:05:26.209100Z

this is closer to what you initially had

2021-01-19T19:05:59.209800Z

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

Hagenek 2021-01-19T19:09:17.211Z

Aha! I see.. I am using to simulate a database

Hagenek 2021-01-19T19:09:26.211400Z

because I am creating mock data for a frontend

2021-01-19T19:09:33.211800Z

right, that's the usual role of an atom

2021-01-19T19:10:00.212200Z

on reflection my first example is closer to what you had, I misread it

2021-01-19T19:10:22.212700Z

having a vector at the top level of an atom is relatively rare, usually you want keyed rather than indexed lookup

2021-01-19T19:10:33.213Z

but perhaps your db is actually a queue or stack

Hagenek 2021-01-19T19:10:54.213400Z

Nono, it was a choice made without too much thought. I am coming straigth from javascript

Hagenek 2021-01-19T19:11:01.213800Z

straight*

Hagenek 2021-01-19T19:11:45.215300Z

I have not chosen a db yet actually

2021-01-19T19:12:08.215700Z

right, instead of [{:id 10 ... ...} {:id 20 ... ...}] we'd usually have {10 {... ...} 20 {... ...}}

2021-01-19T19:12:26.216Z

(or in your case actually the uuids you generate)

Hagenek 2021-01-19T19:14:37.217Z

Yeah of course, makes much more sense to have the UUID as a key to your other checklist data

Hagenek 2021-01-19T19:14:48.217300Z

Nice!

2021-01-19T19:14:52.217500Z

unlike js we only sometimes use hashes as "objects", and generally use them as databases

2021-01-19T19:16:38.218Z

so more like

(ins)user=&gt;  (def todos (atom {})) 
#'user/todos
(ins)user=&gt; (swap! todos assoc (java.util.UUID/randomUUID) {:what "homework"})
{#uuid "701b9c5d-6d61-45e6-bf81-8024953aa50d" {:what "homework"}}

2021-01-19T19:19:13.220200Z

@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

Hagenek 2021-01-19T19:25:32.221600Z

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 😃

🎉 1
jaihindhreddy 2021-01-19T19:35:18.221900Z

@noisesmith that doesn't work on the empty string, because (reverse "") evals to (), which is not the same as nil.

2021-01-19T19:35:47.222200Z

it calls seq first so both are nil

jaihindhreddy 2021-01-19T19:36:10.222400Z

oops! misread it 😅

2021-01-19T19:36:29.222700Z

(ins)user=&gt; ((comp zero? dec count set (juxt seq reverse) seq) "aba")
true
(cmd)user=&gt; ((comp zero? dec count set (juxt seq reverse) seq) "abc")
false

2021-01-19T19:43:07.224100Z

@jaihindhreddy actually no you are right, I had it wrong

(cmd)user=&gt; ((comp zero? dec count set (juxt seq reverse) seq) "")
false
(cmd)user=&gt; ((comp set (juxt seq reverse) seq) "")
#{nil ()}
because (reverse nil) evaluates to () :/

roelof 2021-01-19T19:43:39.224900Z

GN my friends. Hopefully I can work on exercism or 4clojure or my web project tomorrow

👋 3
roelof 2021-01-19T19:47:29.226900Z

IS there a good free hosting platform for the website im building with compujure and ring ?

roelof 2021-01-20T09:41:54.281Z

thanks all

dgb23 2021-01-19T19:49:05.228400Z

@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.

2021-01-19T19:52:52.228700Z

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

dgb23 2021-01-19T19:53:20.228900Z

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.

2021-01-19T19:53:39.229100Z

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

dgb23 2021-01-19T19:53:44.229300Z

I don’t vouch for any of these, but it might be worth checking them out.

dgb23 2021-01-19T19:55:38.229600Z

Ah right its a todo list. A simple map makes more sense there probably; KISS.

dgb23 2021-01-19T19:56:29.230500Z

I’m currently diving deeper into logic (math and programming) and everything looks like a set to me…

Hagenek 2021-01-19T19:57:20.232Z

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

2021-01-19T20:01:25.232200Z

is it an ordered thing for display?

Hagenek 2021-01-19T20:01:31.232400Z

yes

dgb23 2021-01-19T20:01:47.232600Z

there are specialized data structures for that

Hagenek 2021-01-19T20:02:29.232800Z

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

2021-01-19T20:03:45.233Z

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)

2021-01-19T20:04:36.233200Z

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)

Hagenek 2021-01-19T20:04:58.233400Z

the frontend is being build with ionic in react

Hagenek 2021-01-19T20:05:00.233600Z

built*

Hagenek 2021-01-19T20:05:08.233800Z

I am creating the backend as a rest api

2021-01-19T20:07:23.234Z

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

2021-01-19T20:07:50.234200Z

and that's either in an atom (in memory) or an intermediate structure used when talking to a db

Hagenek 2021-01-19T20:08:32.234400Z

That's right!

alexmiller 2021-01-19T20:24:48.236500Z

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

👍 1
🏁 4
🤘 1
roelof 2021-01-19T20:45:51.236900Z

Did already a few days ago

✅ 1
Piotr Brzeziński 2021-01-19T21:01:09.237500Z

Done 🙂

mathpunk 2021-01-19T23:35:36.239Z

I'm having trouble finding the clojure equivalent of lein deps, has anyone got it to hand?

mathpunk 2021-01-20T16:12:16.305200Z

thanks folks!

mathpunk 2021-01-19T23:38:19.239100Z

i'm dockerizing something and the recommendation from the clojure image is to download deps ahead of time, but the examples are lein-based

2021-01-19T23:39:45.239300Z

You want an option to clojure that will only try to load dependencies, and then stop?

dpsutton 2021-01-19T23:41:37.239500Z

clojure -Spath should do the trick

2021-01-19T23:41:47.239700Z

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.

2021-01-19T23:43:43.239900Z

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