beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
piyer 2021-04-26T00:41:15.104700Z

that is a good idea.

piyer 2021-04-26T00:41:20.104900Z

will try that.

piyer 2021-04-26T06:22:51.131500Z

Is there a better way to write this?

(def urls {:yahoo "<https://www.yahoo.com>"
           :google "<https://www.google.com>"})

(defn getter [url]
  (future (slurp url)))

(reduce-kv (fn [acc name url] (assoc acc name (getter url))) {} URL)
;; write a another reduce to deref the futures.

pithyless 2021-04-26T07:07:56.131900Z

That reduce-kv is fairly idiomatic to update all the map values; some projects end up with a helper that is named ala map-vals (see e.g. https://weavejester.github.io/medley/medley.core.html#var-map-vals for a more "verbose" version with support for transients)

pithyless 2021-04-26T07:08:42.132100Z

When you're going to reduce over a second time to deref the futures, you're going to be blocked by the slowest request; which I guess is OK, since you want all the return values together in one map

pithyless 2021-04-26T07:12:14.132300Z

Which brings me to the elephant in the room - if you're writing some kind of "web scraper", slurp is a really small hammer; most of the http client libraries (e.g. clj-http , aleph , etc.) support things like persistent connections, threadpooling, retries, better support for headers, etc. Things you'll probably need. The way it is written, if a single http request fails, the entire operation will fail and even if you try/catch the result, you will only get back a partial result (as far as you reduced; perhaps other requests finished but you didn't know about it)

piyer 2021-04-26T14:27:18.138100Z

Thank you. clj-http looks a lot better.

piyer 2021-04-26T06:23:35.131600Z

I am trying to get this format output {:yahoo "...." :google "...."}

pithyless 2021-04-26T07:07:56.131900Z

That reduce-kv is fairly idiomatic to update all the map values; some projects end up with a helper that is named ala map-vals (see e.g. https://weavejester.github.io/medley/medley.core.html#var-map-vals for a more "verbose" version with support for transients)

pithyless 2021-04-26T07:08:42.132100Z

When you're going to reduce over a second time to deref the futures, you're going to be blocked by the slowest request; which I guess is OK, since you want all the return values together in one map

pithyless 2021-04-26T07:12:14.132300Z

Which brings me to the elephant in the room - if you're writing some kind of "web scraper", slurp is a really small hammer; most of the http client libraries (e.g. clj-http , aleph , etc.) support things like persistent connections, threadpooling, retries, better support for headers, etc. Things you'll probably need. The way it is written, if a single http request fails, the entire operation will fail and even if you try/catch the result, you will only get back a partial result (as far as you reduced; perhaps other requests finished but you didn't know about it)

practicalli-john 2021-04-26T10:08:56.132800Z

This may be of interest too https://practicalli.github.io/clojure-webapps/

1
Aron 2021-04-26T10:17:06.133500Z

what is the clojure way to implement a function that does slightly different things based on a single argument to it being either a keyword or a function?

raspasov 2021-04-27T08:37:38.160200Z

If your use case is simple, perhaps (if (keyword? x) (do …) (do …)) can work as well; Before jumping to things like defmulti I would question if I really need that fancy polymorphism in the first place.

raspasov 2021-04-27T08:39:07.160400Z

I usually like to do the simplest thing that works, until it doesn’t.

Aron 2021-04-27T08:48:25.160600Z

Yeah, I ended up removing it later yesterday 🙂 But it was good that it worked for a while.

1👍
Aron 2021-04-27T08:48:38.160800Z

Helped me with refactoring something.

Aron 2021-04-27T08:49:48.161Z

This is a thing I always wanted to do, when refactoring spaghetti code or callback hells, that i keep the old function as long as possible.

2021-04-26T10:18:03.133600Z

defmulti

Aron 2021-04-26T10:19:58.133800Z

thank you

2021-04-26T10:20:11.134Z

(defmulti function (fn [arg]
                     (cond
                       (keyword? arg) :keyword
                       ((some-fn ifn? fn?) arg) :function)))

(defmethod function :keyword [arg] )

(defmethod function :function [arg] )

(defmethod function :default [arg]
  (ex-info "Unexpected argument" {:arg arg}))
not the best solution though, but might be handy

Aron 2021-04-26T10:21:00.134200Z

That's very helpful, thank you! I am reading the docs now and will experiment with it.

Aron 2021-04-26T10:58:05.134400Z

fwiw, I knew defmulti should be but I didn't realize I can't just ask type on the argument but have to explicitly create another function to categorize the arguments and dispatch based on that other function instead of type

Aron 2021-04-26T10:58:22.134600Z

so I almost got sidetracked before your example cleared it up, thanks again

2021-04-26T12:04:14.135Z

there is also nice addition to defmulti for repl-driven development

(defn dispatch-fn [&amp; args])

(defmulti function #'dispatch-fn)

...
that give you ability to adjust dispatch function during development because implementation of defmulti looks like defonce from a distance.

2021-04-26T12:05:33.135200Z

without it, changes in dispatch function of that form (defmulti function (fn … will not be taken in account unless you explicitly eval something that overwrite function var.

Aron 2021-04-26T12:31:07.135500Z

can't use repl driven development yet

Aron 2021-04-26T12:35:08.135700Z

so I am not sure what the whole thing is about since I have never seen anyone do what I do using repl driven development and I haven't done anything backend side with clojure. Are you saying that using the above pattern, if I change the defmulti form, that will update how the function works? I understand at least why I would have to eval something that overwrites function usually.

2021-04-26T14:26:37.137900Z

How do people layout cond ? Do do you:

(cond foo?
      stuff
      bar?
      other-stuff
      :else
      do-else-thing)
Or:
(cond foo? stuff
      bar? other-stuff
      :else do-else-thing)
I find it gets messy when the code being executed for a predicate is multiple lines and can't finda way to lay it out that looks nice and is easy to see what code belongs to what predicate

piyer 2021-04-26T14:27:18.138100Z

Thank you. clj-http looks a lot better.

piyer 2021-04-26T14:28:37.138600Z

https://guide.clojure.style/#else-keyword-in-cond

2021-04-26T14:29:21.138800Z

thanks, i wasn't aware of that guide

piyer 2021-04-26T14:29:52.139Z

you should also get a lint/formatter for you editor which can do this for you.

2021-04-26T14:35:13.139700Z

Actually reading that, it seems like my cond should be a condp

sova-soars-the-sora 2021-04-26T18:51:57.141900Z

Hi, I'm trying to create a selmer filter that can take multiple params.

sova-soars-the-sora 2021-04-26T18:53:13.142300Z

maybe use split and pass them in with commas? or semicolons? kinda what i'm thinking

2021-04-26T19:15:36.142400Z

If it gets really large and unwieldy, I sometimes do:

(cond
  pred?
  stuff

  bar?
  other-stuff

  :else
  do-else-thing)

2021-04-26T19:20:32.143600Z

Is their a nicer way to do this?

(defn get-source-line-numbers [source]
  (:line-nos (reduce (fn [{:keys [cur line-nos] :as acc} i]
                       (if (or (= "" i) (clojure.string/starts-with? i ";"))
                         (assoc acc :line-nos (str line-nos "\n"))
                         (-&gt; acc
                             (assoc :line-nos (str line-nos cur "\n"))
                             (update :cur inc))))
                     {:cur 0 :line-nos ""}
                     (str/split-lines source))))
I have a control that has a multi-line string, I want a number allocated for each line that doesnt start with ; or is blank. It's so I can number lines like this:

2021-04-26T19:24:37.143700Z

Guess what I'm asking is if there is a way to do reduce but with a counter rather than create a map like I've done

2021-04-26T19:49:04.145100Z

In clojurescript, if I have a string stored in a variable, is it possible to get an element from that object based on that what's in that string? Like, say say:

(let [x "color"]
  (. js/window -x))
But having -x be replaced with -color?

2021-04-26T19:49:26.145600Z

I know I could use something like eval to make it work, but it seems like that's way overkill in this situation.

2021-04-26T19:54:27.145900Z

Ah, oops, aget is probably what I want.

2021-04-26T19:54:54.146100Z

(And aset)

sova-soars-the-sora 2021-04-26T19:57:25.146200Z

hmmm have you checked out reduce-kv ? not sure if it's helpful

Lu 2021-04-26T20:00:19.146800Z

If you’re accessing an object, use goog.object/get instead.

Lu 2021-04-26T20:00:41.147400Z

aget happens to work on objects too but should only be used on arrays

2021-04-26T20:05:22.147800Z

Oh interesting, okay. I wonder why that is.

2021-04-26T20:05:29.148100Z

Is there also goog.object/set?

Lu 2021-04-26T20:08:28.148500Z

You bet there is one :) https://google.github.io/closure-library/api/goog.object.html

2021-04-26T20:12:13.148700Z

Ah, there's the api, thanks. 🙂

2021-04-26T20:12:26.149Z

May I ask why this is preferred over aget/`aset`?

2021-04-26T20:13:00.149700Z

I mean, I see that the docs say its for arrays. But I'm kind of curious about what differences there are (other than just style).

phronmophobic 2021-04-26T20:34:20.150Z

One problem that this creates is a challenge in further evolving aget to match its intended purpose. A few examples that come to mind include:In Clojure, if you pass a non-integer array index to aget, it will round down to the nearest integer. It would be nice to make ClojureScript’s aget match this behavior. This is easily achievable by employing int in the implementation, causing the emitted JavaScript to look like array[ndx|0]. But this would break existing code that uses aget for object property access.In Clojure, if you pass a negative array index, or one that is otherwise out-of-bounds, you’ll get an exception. It would be nice to consider adding such safety mechanisms to ClojureScript’s aget. But again, any attempt to blindly treat the indices as numbers would run afoul of aget being passed string indices.In the future, perhaps core library functions will have specs written for them. The same issues arise: The indices passed to aget should satisfy the number? predicate, but if that were done, lots of code in the wild would be deemed non-conformant.

2021-04-26T21:01:46.150200Z

Ah, that makes sense, thanks. 🙂