Fork me on GitHub
#beginners
<
2021-03-29
>
raspasov00:03:55

@sova if you have the data ready in a vector, and it’s a lot of data, subvec might be worth it since will be constant time (i.e. faster than take/drop, etc)

raspasov00:03:27

But for most UI cases, and if you have a few hunded items, etc; linear will most likely be good enough

andarp05:03:35

@sova highly dependent on context, but you could also partition the sequence into indexable blocks of fifty each and keep that in memory

👌 1
sova-soars-the-sora19:03:26

(memoize ...) ? or some other way

andarp08:03:31

What's the general go-to approach of handling errors? I have recently been writing a lot of go so my head is stuck in the return value, error way of doing things. The two things I can think of are: • just return nil (loses error context) • throw exception (loses nice control flow, easy ability to aggregate errors before reporting them, etc)

gon09:03:22

Also a pattern used is [error-code-or-true/false related-value] exceptions are not very common

Lennart Buit09:03:40

Not sure how idiomatic: You can return a map of (possibly) result, and (possibly) list-of-errors

Lennart Buit09:03:31

(What gon says, but then a map instead of a tuple ^^)

andarp09:03:56

I'm happy to hear exceptions aren't that common

andarp09:03:19

I don't mind returning a vec of errcode/value, I just don't want to transplant my recent go experience onto Clojure if there's a more idiomatic way

solf09:03:52

@anders152 As a data point, aws-api is often cited as a great lib. Here’s how it handles errors:

Barring client side exceptions, every operation on every service will return a map. If the operation is successful, the map will be in the shape described by (-> client aws/ops op :response). If AWS indicates failure with an HTTP status code >= 400, the map will include a :cognitect.anomalies/category key, so you can check for the absence/presence of that key to determine success/failure.
https://github.com/cognitect-labs/aws-api#responses-success-failure

🙌 1
clojure-spin 1
rakyi09:03:35

AFAIK both exceptions and values representing errors are idiomatic, depends on context when to use one or the other

Kenneth Gitere13:03:32

What would you guys recommend as a good Clojure backend framework that also has support for websockets?

KJO13:03:31

I’ve found Immutant and Pedestal to work perfectly well for me. Different approaches, but both have served me well. http://immutant.org/documentation/2.1.9/apidoc/guide-web.html#h3380 & https://github.com/pedestal/pedestal/tree/master/samples/jetty-web-sockets

KJO13:03:30

There’s also a slightly outdated blog post that might be helpful at https://heykieran.github.io/post/using-sse-and-websockets/

hindol13:03:49

I have used Pedestal but it required quite a bit of setup. Ring with the Jetty9 adapter is far simpler for hobby projects. https://github.com/sunng87/ring-jetty9-adapter#websocket

Kenneth Gitere13:03:33

Thanks for these suggestions. I had seen Luminus after a simple Google search. Is it a suitable alternative?

hindol13:03:18

I have not used Luminus myself. But everyone suggests that as a good beginner framework. Definitely worth trying.

Kenneth Gitere13:03:31

Beginner friendly would be exactly what I need 😅

KJO13:03:49

Yup, Luminus is a good option; it uses immutant as the web server, so you’ll be fine. Good luck.

Kenneth Gitere13:03:39

I'm trying to make a turn based game server in Clojure so there's certainly going to be a lot of "fun" problems to solve

sova-soars-the-sora15:03:31

Depending on how "real-time" it has to be, you might find success with ajax and REST endpoints and keeping track of "last-seen-timestamp" for each client on the server, so when you get a /what-is-up POST from an ajax (cljs) client you can simply send the delta of new events since :last-seen-timestamp. I'm using this approach in a social-ish network thing right now.

Surya Poojary15:03:00

I'm a hobbyist , learning clojure ..

Surya Poojary15:03:25

Clojure is my first functional programming language ..I know a bit of python

Lennart Buit15:03:16

Welcome! If you like map and filter and reduce of Python’s stdlib, or the entirety of functools or itertools you will feel right at home here ;)!

blak3mill3r16:03:53

and if you like python's list comprehensions, you are going to love clojure's for /`doseq` macros

Stuart22:03:37

(let [foo {:people [{:name "Bob" :id 1} {:name "Alice" :id 0} {:name "Eve" :id 2}]}
      current-name "Bob2"
      selected-id 1]
          
  
  )
How would I update the item in :people that has :id equal to selected-id to change the :name to current-name ? Is their a better way than find the item in :people, and update it, then remove the old item from people, add the new one and then reassoc that list to people?

Stuart22:03:03

i.e. like this

(let [db {:people [{:name "Bob" :id 1} {:name "Alice" :id 0} {:name "Eve" :id 2}]
          :current-name "Bob2"
          :selected-id 1}]
  (let [new-item (-> (filter #(= (:id %) (db :selected-id)) (:people db))
                      (first)
                      (assoc :name (db :current-name)))
        updated-people (-> (remove #(= (:id %) (db :selected-id)) (:people db))
                           (conj new-item))]
    (assoc db :people updated-people)
    ))
There must be a better way!

pavlosmelissinos22:03:50

(let [foo {:people [{:name "Bob" :id 1} {:name "Alice" :id 0} {:name "Eve" :id 2}]}
      current-name "Bob2"
      selected-id 1]
  (->> (:people foo)
       (map (fn [m]
              (if (= (:id m) selected-id)
                (assoc m :name current-name)
                m)))
       (assoc foo :people)))
something like this? edit: else was missing and you want to have the full map in the end, fixed edit2: meh, unbalanced parens, now it's really fixed

blak3mill3r22:03:10

there are a lot of ways... how about this with specter:

(let [db {:people [{:name "Bob" :id 1} {:name "Alice" :id 0} {:name "Eve" :id 2}]
          :current-name "Bob2"
          :selected-id 1}]
  (setval [:people ALL #(= (:id %) (:selected-id db)) :name] (:current-name db) db))

👍 1
Stuart22:03:04

oh nice! I've never used Spectre before. I'll look into it! That seems pretty slick

blak3mill3r22:03:19

powerful, maybe a rather steep learning curve

blak3mill3r22:03:30

but it's not too hard to get started with things like this

dpsutton22:03:34

(let [update-if (fn [pred f] (fn [x] (if (pred x) (f x) x)))]
  (let [foo {:people [{:name "Bob" :id 1} {:name "Alice" :id 0} {:name "Eve" :id 2}]}
        current-name "Bob2"
        selected-id 1
        maybe-correct (update-if (comp #{selected-id} :id) #(assoc % :name current-name))]
    (update foo :people #(mapv maybe-correct %))))
{:people [{:name "Bob2", :id 1} {:name "Alice", :id 0} {:name "Eve", :id 2}]}

👍 1
dpsutton22:03:36

with a bit more tasteful naming it could be quite nice. maybe-correct named apply-edit might make it a bit more clear. update-if could use a better name but i put it in a separate let as it feels very library-ish

Stuart22:03:24

Thanks! Yeah that's nicer than what I had.

jkrasnay23:03:33

I would do something like this

2
Stuart23:03:49

Yeah, i should definitely learn specter

raspasov23:03:14

For updating deeply nested immutable data, Specter does it in a very concise and quite performant way

raspasov23:03:24

One important note though: I always try to de-nest my data as much as possible; more often than not, you can use more shallow data structures which makes your life way easier;

raspasov23:03:58

Specter can be super useful if you’re dealing with incoming data that you don’t have initial control over.

👀 1
raspasov23:03:32

Say, you have some value, 10 levels deep in some JSON, and you need to just (inc some-value), while preserving the whole shape.

raspasov23:03:40

Specter is the best for that.