Fork me on GitHub
#beginners
<
2021-05-25
>
Justin Grahn03:05:40

Would someone be able to recommend a good walkthrough on testing in Clojurescript? I can’t for the life of me figure out how to require and run even the simplest tests from the REPL.

Rob Haisfield03:05:34

I’m really struggling to grasp reduce but every single example I see is just addition. Are there any really good, simple practical examples?

seancorfield04:05:50

How about this:

user=> (reduce (fn [s w] (if (re-find #"[aeiou]" w) (conj s (first w)) s)) #{} ["the" "dry" "wind"])
#{\t \w}
Given a sequence of words, it produces a set of the first letters of any words that contain vowels.

seancorfield04:05:58

To do that "long hand", you'd filter the words to just those that contain vowels, then map first across the result to get all the letters, then into #{} or similar to produce just the unique set of first letters.

Rob Haisfield15:05:55

Thank you for the string example!

Rob Haisfield04:05:29

So I have a function (check-code some-vector) that checks a vector against a bunch of rules. If they all return true, then the function returns true The screenshot I sent is code to generate x number of vectors of 5 digits. Then at the end, I want to filter it down to find just the results that are true Only problem is that this isn’t super useful How would I set it up to find the code vectors that returned the true value when they were passed in as an argument to check-code?

Lance Erickson04:05:40

I think you just want to take out the (map check-code call. Use (filter check-code (... instead. check-code can act as the predicate function for filter.

Lance Erickson04:05:22

When you map with check-code, you are applying a transformation and creating a new vector of booleans, where using filter will just admit any values that pass the predicate function unchanged.

Rob Haisfield04:05:52

That’s perfect, thank you

dabrazhe10:05:17

What is the canonical way to write this condition chain, without chaining if ?

(zero? ask-price)  then preMarketPrice, but if preMarketPrice is zero again then lastprice

delaguardo11:05:21

try some-> or some

delaguardo11:05:49

it can “short-circuit out” if result is not nil

delaguardo11:05:54

but you will need to make few separate functions to obtain corresponding price and return nil if result is zero

Ivan Koz11:05:58

create a seq of all prices, choose first which is not 0 (some #(when (pos? %) %) prices)

delaguardo11:05:30

but that will make (potentially) unnecessary side effects

delaguardo11:05:05

(defn get-price []
  (http/request ...))

(def prices 
  [(get-price)
   (get-price)
   (get-price)])

(some #(when (pos? %) %) prices)
like this

Ivan Koz11:05:13

wrap requests in a delay, make it lazy

Ivan Koz11:05:58

or fetch all prices at once, not even a problem is prices are parsed or queried from database

delaguardo11:05:33

(defn get-price-1 [args] ...)

(defn get-price-2 [args]
  (http/request ...))

(defn get-price-3 [args] ...)

(some-> args (get-price-1) (get-price-2) (get-price-3))
this one ^ will not make http request if the result of get-price-1 is not nil

Ivan Koz11:05:13

why would anyone need 3 http requests to get 3 numbers

delaguardo11:05:42

it depends, sometimes it might be required. I don’t think you understand completely the problem, do you?

delaguardo11:05:12

also http request is just for illustrative purposes. It can be anything else - executing of db statement, fetching from file, asking user to enter price from stdin

delaguardo11:05:40

anything that falls into huge space of IO operations

Ivan Koz11:05:09

again if laziness is a concern there is delay

delaguardo11:05:59

the laziness is not a concern

Ivan Koz11:05:45

you just said: this one ^ will not make http request if the result of get-price-1 is not nil that is exactly about laziness and delayed evaluation

delaguardo11:05:25

nono, it is about executing or not of some IO operations.

Ivan Koz11:05:14

Lazy evaluation, is an evaluation strategy which delays the evaluation of an expression until its value is needed.
So since some is a short-circuit operation, making source of lazy calls will not evaluate more than needed.

delaguardo11:05:45

try this expression (first (map prn [1 2 3 4 5 6 7])) map returns lazy sequence, prn is doing side effects I expect it to print only one number but in reality it will print entire collection

delaguardo11:05:18

only some-> can stop the chain (some zero? (map (fn [i] (prn i) i) [1 2 3 0 5 6 7]))

Ivan Koz11:05:45

(some #(when (pos? @%) @%) [(delay (side-effect 0))
                            (delay (side-effect 1))
                            (delay (side-effect 2))])
0
1
=> 1

delaguardo11:05:02

right, but that implies a certain interface for every element in a sequence

dabrazhe09:05:48

Thanks guy. I enjoyed both your solutions

dabrazhe09:05:27

Cool trick with some-> @U04V4KLKC !

Aron10:05:20

pattern matching : )

jkxyz10:05:09

Personally I’d write a function like (defn non-zero [n] (when-not (zero? n) n)) and then it’s just (or (non-zero ask-price) (non-zero preMarketPrice) lastprice)

jkxyz10:05:51

But for an isolated case I don’t think there’s anything wrong with nested ifs, either

Aron10:05:38

And it looks like something that might be applicable in different situations, so it might worth speccing it and that opens up possibilities with regard to default value handling

dabrazhe11:05:38

Pattern matching feels like overkill and not overly functional : )

Aron11:05:13

but it's extensible : D you can add further conditionals later : D

gibi11:05:28

hi, how would you refactor this handler function to be more idiomatic?

(fn [request]
             (let [todo-list (:body-params request)
                   conn (db/db-connection!)
                   saved-todo-list (db/insert-todo-list! conn todo-list)
                   _ (db/close! conn)]
               {:status 201
                :body   saved-todo-list}
               ))

delaguardo11:05:37

you could create a wrapper similar to with-open to open connection, do something with it and close at the end.

(defmacro with-open-conn [[conn open-expr] & body]
  `(let [~conn ~open-expr]
     (try
       (do [email protected])
       (finally (db/close! ~conn)))))

👍 3
Joni Hiltunen12:05:22

New to everything... Is it possible to use spec to say that if one thing is X then other thing must be Y or else etc... Hard to explain, I have a gist https://gist.github.com/Sose/6df4962cf02c5a10aba43559ad9f5519#file-spec-cljs-L11

Joni Hiltunen12:05:38

Hmm, that might be it.. Just need to figure out how to use it 😄

delaguardo12:05:45

(defmulti by-command (fn [[command _]] command))

(defmethod by-command :move [_]
  (s/tuple ::command ::move-arguments))

(defmethod by-command :turn [_]
  (s/tuple ::command ::turn-arguments))

(defmethod by-command :repeat [_]
  (s/tuple ::command ::repeat-arguments))

(s/def ::instruction (s/multi-spec by-command :instruction))
something like this should work

Joni Hiltunen12:05:13

thank you for the example!

Joni Hiltunen12:05:38

tbh I didn't fully understand the multi-spec thing but I managed to do what I wanted with s/or... (I think) https://gist.github.com/Sose/b4b319baacc219ba03b919f5b5ad0014

Joni Hiltunen13:05:00

I wonder if there's already a function that does this?

(defn run-script [turtle [instr & script]]
  (if instr
    (recur (step-turtle turtle instr) script)
    turtle))

Joni Hiltunen13:05:11

ahh.. that's reduce I think

☝️ 3
Audrius15:05:33

Hi, what would be the Wordpress of Clojure?

Jeff Evans16:05:16

Suppose I want to use partition-by, but I want the logic to depend on some already seen values (not only the “next” value). Do I need to implement my own via reduce?

noisesmith16:05:49

that would be the simplest thing - either reduce or a function using lazy-seq surrounding a self call to build the values

noisesmith16:05:19

(defn filter-weird
  ([coll]
   (filter-weird #{} coll))
  ([seen [x & xs :as coll]]
   (lazy-seq
    (cond (empty? coll)
          nil
          (contains? seen (inc x))
          (filter-weird seen xs)
          :else
          (cons x
                (filter-weird (conj seen x)
                              xs))))))
user=> (filter-weird (range 10 0 -1))
(10 8 6 4 2)
@jeffrey.wayne.evans - I totally spaced the "partition" thing, but it's a simple demo of having a state accumulator (as you would in reduce) inside a lazy function

Jeff Evans16:05:28

could also do it as a stateful transducer, I suppose

noisesmith16:05:51

yeah - these recursive lazy-seq building idioms are straightforward to build as a transducer - the main question I guess is whether laziness is useful in your use case

Jeff Evans16:05:03

yeah not so much

Jeff Evans16:05:09

the “real application” is for tiny structures

Jeff Evans16:05:40

and just have the fn passed to partition-by accept two args

Joni Hiltunen16:05:20

This isn't quite reduce is it? I wonder if there's a function for this

(defn- repeat-turtle
  [turtle [n subscript]]
  (loop [i 0
         res turtle]
    (if (>= i n)
      res
      (recur (inc i) (run-script res subscript)))))

noisesmith16:05:28

it's iterate plus a side effect (which means clojure.core/iterate isn't safe for this kind of usage I think)

Joni Hiltunen16:05:26

there isn't a side effect. run-scriptis a function of type turtle -> script -> turtle

Joni Hiltunen16:05:46

(defn- repeat-turtle
  [turtle [n subscript]]
  (last (take (inc n) (iterate #(run-script % subscript) turtle))))
this seems to be equivalent... is there a better way than (last (take ..))?

noisesmith17:05:44

(-> turtle
    (->> (iterate (fn [step]
                    (run-script step subscript))))
    (nth (inc i))) 
maybe?

Joni Hiltunen17:05:44

right.. nthlol 😄 thanks

noisesmith17:05:23

if you flip the args to subscript you can use partial:

(-> turtle
    (->> (iterate (partial flipped-run-scropt subscript)))
    (nth (inc i)))

haywood17:05:11

(read-string "(rf/dispatch [::events/some-event 1])")

=> Invalid token: ::events/some-event
Anyone know what I need to provide so read-string doesn’t throw here? I’m not sure which opts read-string takes, some Java Object opts down in the clojure.lang.RT class. I’m thinking I need to do something with a wrapping (bindings form?

hiredman17:05:12

there isn't an option you can provide to read string

hiredman17:05:17

when reading keywords with '::' the namespace part is resolved against the value of *ns*

hiredman17:05:08

so what you would have to do is create namespace, add an alias to that namespace for events, then bind *ns* to that namespace, and call read-string

haywood17:05:12

dang, ok. just to expand on what I’m trying to do, ::events is imported at the top but read-string doesn’t ‘ingest’ that so to speak.. fuller example:

(read-string (str "[(ns  (:require [my.app.events :as events]))\n\n"
                    "(rf/dispatch [::events/some-event 1])]"))
so I guess I’d have to like, use regexes to read the imports and construct the lookup map with aliases for the reader?

haywood17:05:17

ok, this worked

(do
    (create-ns 'my.app.events)
    (alias 'events 'my.app.events)
    (read-string (str "[(ns  (:require [my.app.events :as events]))\n\n"
                      "(rf/dispatch [::events/some-event 1])]")))

dpsutton17:05:17

what are you trying to do? why problem are you solving?

haywood17:05:50

I have a build step that generates a routes.cljs file based on what is in the pages directory of the app

haywood17:05:18

part of that is reading in those files and parsing out a few things

dpsutton17:05:10

if you ditch the reader expanded aliases it might be easier. how much benefit do you get from :my.app.events/some-event vs :event/some-event?

haywood17:05:26

I was looking for something like that

haywood17:05:35

I actually want it to not care

dpsutton17:05:51

the reader will always care if you ask it for "some-event from the thing called events". but if you are using read-string why not just put those files on the classpath and require them?

haywood17:05:47

great question….

haywood18:05:52

well, the script is clojure but the files are clojurescript

haywood18:05:27

the whole thing is pretty messy, I don’t want to hold y’all up and I really appreciate the tips, I’m going to work on adding aliases from the (ns form in each file iteration

dpsutton18:05:10

Scratch that :)

Erik B Good19:05:34

Hello again, I am once again hitting a wall with what seems like a pretty easy situation. Given an arbitrary nested sequence, I want to recreate it with the same nested structure. I am able to do it fairly easily with normal recursion, but I was wondering whether anyone could think of an answer using (recur). I am surprised how hard it can be to convert a recursive function to the loop/recur construct in some context.. (= [1 2 [3 [4 5] 6] 7] ((*fn* recreate-struct [ds recreated-ds] (*if-not* (instance? clojure.lang.Seqable ds) (conj recreated-ds ds) (into recreated-ds (map #(*if-not* (instance? clojure.lang.Seqable %) % (recreate-struct % recreated-ds)) ds)))) [1 2 [3 [4 5] 6] 7] []))

haywood19:05:36

sorry, that’s not super helpful but if you weren’t aware of that namespace it’s awesome

phronmophobic19:05:52

for nested sequences, I'll often use clojure.zip. A common starting point is:

(defn clj-zip [obj]
  (z/zipper #(and (seqable? %)
                  (not (string? %)))
            identity
            (fn [node children]
              (if (map-entry? node)
                (vec children)
                (into (empty node) children)))
            obj))
you can emulate prewalk with:
(defn zip-walk
  "Depth first walk of zip. edit each loc with f"
  [zip f]
  (loop [zip zip]
    (if (z/end? zip)
      (z/root zip)
      (recur (-> (z/edit zip f)
                 z/next)))))
it's pretty easy to also emulate postwalk and I find it a little more flexible than clojure.walk example usage:
(-> {:a 2
     :b [3 4 5]
     :c {:d 10}
     11 12}
    clj-zip
    (zip-walk (fn [x]
                (if (number? x)
                  (inc x)
                  x))))
;; {:a 3, :b [4 5 6], :c {:d 11}, 12 13} 

Erik B Good19:05:25

Alright I will talk a look into these two namespaces at once. Thanks both of you dearly for your help

phronmophobic19:05:21

you may also be interested in something like specter, https://github.com/redplanetlabs/specter

Matheus Silva19:05:33

Does anyone use clojure in wsl with intellij can help me with REPL?

manutter5119:05:47

You might try the #cursive channel.

Ian Fernandez19:05:44

Hi, where can I ask about Compojure

Ian Fernandez20:05:58

there's some default middleware to assoc the request to a system-configuration ?

Ian Fernandez20:05:07

or something like this?

Ian Fernandez20:05:25

to make something like a context that interceptors have

seancorfield20:05:37

Can you explain in a bit more detail what problem you’re trying to solve?

seancorfield20:05:47

What do you mean by “system-configuration”?

Ian Fernandez20:05:05

I have a default configuration for the things that are external from my system, then I need to start connections and etc, this "started-config" is what I call a system. I want to make my handlers dependent from my system

seancorfield20:05:10

(defn wrap-config [handler config]
  (fn [req]
    (handler (assoc req :system/configuration config))))
That would be all you’d need to add a data structure to the request.

Ian Fernandez20:05:40

that's what I thought when I was talking here hahahah

Ian Fernandez20:05:57

sorry about ruber-ducking here

gibi22:05:57

hi, I'm using monger and specifically the function find-maps this way

(let [conn (connect-with-credentials <.....>)]
               (try
                 (find-maps (mg/get-db conn "todo-lists") "collection")
                 (finally (disconnect conn))
                 )
               )
it seems find-maps already closes the connection so I get an error in the finally

gibi22:05:03

this seems to be inconsistent with the other function find-one-as-map that doesn't close the connection. It seems this feature has been introduced with https://github.com/michaelklishin/monger/pull/47 not sure why