Fork me on GitHub
#beginners
<
2020-07-15
>
Mitch00:07:07

If I am writing a cljs library to wrap a js component, should the library pull in the js component, or should I depend on the user to provide it?

Mitch00:07:02

I am worried that there might be performance implications to bundling it in my lib, since the user may want to pull in the dependency via cdn

Kenneth Cheung02:07:03

Hi all, Looking for feedback, which version of adjacencies? is more idomatic? I'm particularly wondering if it is acceptable to use a kind of state variable in my reduce ( prev ). Or is there a better way?

(defn adjacencies?
  "Returns true if there are consecutive characters that are the same in the
  given string"
  [s]
  (loop [[x1 & x-rem :as xs] (seq s)
         prev                nil]
    (cond
      (not xs)    false
      (= x1 prev) true
      :else       (recur x-rem x1))))

(defn adjacencies?
  "Returns true if there are consecutive characters that are the same in the
  given string"
  [s]
  (first
    (reduce (fn [[result prev] x]
              (if (= x prev)
                (reduced [true x])
                [result x]))
            [false nil]
            s)))

(adjacencies? "abcd") ;;=> false
(adjacencies? "abcdd") ;;=> true
(adjacencies? "") ;; => false

phronmophobic02:07:36

I would probably do something like:

(defn adjacencies? [s]
  (some identity (map = s (rest s))))

seancorfield02:07:01

I think that's nicer than my partition version. I'd probably use (some true? ...) tho' to be a bit clearer about what it's looking for?

👍 1
phronmophobic02:07:28

one note regarding loop is that the destructuring in the bindings will cause extra seq calls so I usually use the following pattern to avoid the extra seq calls:

(defn adjacencies?
  "Returns true if there are consecutive characters that are the same in the
  given string"
  [s]
  (loop [xs (seq s)
         prev nil]
    (if xs
      (let [x (first xs)]
        (if (= x prev)
          true
          (recur (next xs) x)))
      false)))

seancorfield02:07:20

Ah, yours also returns nil for the "false" case so it isn't strictly a predicate, just like mine wasn't.

Kenneth Cheung03:07:04

@U7RJTCH6J so nice (some identity (map = s (rest s))) and ty for the loop pattern. New info to me!

👍 1
Kenneth Cheung05:07:54

@U7RJTCH6J wanted to clarify the loop binding comment Will moving the seq call outside of the loop remove those extra calls. Or did you mean under the hood, destructuring in a loop bind always calls seq

(when-let [sec (seq s)]
    (loop [[x1 & x-rem :as xs] sec
           prev                nil]
      (cond
        (not xs)    false
        (= x1 prev) true
        :else       (recur x-rem x1))))

phronmophobic05:07:12

the second > destructuring in a loop bind always calls `seq`

phronmophobic05:07:37

it's not that big a deal. it will just call seq a few extra times

Kenneth Cheung04:07:56

! TY I think this is important, especially when I need to calculate big O.

seancorfield02:07:59

I'm not sure which of those I would consider more idiomatic. For comparison

(defn adjacencies? [s] (some #(apply = %) (partition 2 1 s)))

Kenneth Cheung04:07:18

this is good too!

seancorfield02:07:38

Technically that produces nil for the "false" case so it shouldn't have ? (unless it wrapped the some call in boolean to produce just true or false)

🙌 1
alexmiller02:07:29

you could also dedupe and see if the result is the same

alexmiller02:07:05

(defn adjacencies? [s] (not= (seq s) (seq (dedupe s))))

😮 1
🙌 1
Kenneth Cheung04:07:54

ty! I think this one i think uses the best existing abstraction. building on this for time performance I could use count `(apply not= (map count [s (dedupe s)]))` retracting this. count on a string and the sequence by dedupe is not constant

alexmiller12:07:11

One downside of this is it doesn’t early exit

Kevin Mungai07:07:09

Hi, hope everyone is safe. What is the purpose of the comment macro? I see that it ignores the body, but I don't understand why that would be useful. https://clojuredocs.org/clojure.core/comment

jaihindhreddy07:07:35

People tend to use the REPL for development. And instead of typing directly in the REPL, one has their editor connected to the REPL, and using keyboard, shortcuts, you run pieces of code. Let's say you have defined a function f, and you want to try (f 42). You'd type it in the file you happened to be in and try it. This call may have side effects (like printing, HTTP calls etc.), and will happen when the file is loaded. So, you can put this REPL exploration in a comment form. You can even check-in this code. These are also called "Rich Comment forms". They're present in the Clojure standard lib as well. Example: https://github.com/clojure/clojure/blob/master/src/clj/clojure/zip.clj

Kevin Mungai07:07:57

Thanks for the explanation. I have been commenting out the forms using ;; instead. Maybe I will try using comment for some of these instead.

phronmophobic07:07:43

comment is different from ;; in that it returns nil compared to ;; where everything on a line after ;; is ignored

sogaiu08:07:02

you might also find the discard form #_ to be of interest too: <https://clojure.org/guides/weird_characters#_discard|https://clojure.org/guides/weird_characters#_discard>

Kevin Mungai08:07:14

So when is the most appropriate place to use comment?

sogaiu08:07:35

personally i use it for the "rich comment form" situation - perhaps you might find this article relevant to get an idea of what some others think: https://betweentwoparens.com/rich-comment-blocks

👍 1
Kevin Mungai08:07:16

Thanks so much, I see now that comment is neat way to provide a working example or thought process in code that isn't actually part of the code.

👍 2
MorongÖa11:07:58

He everyone. I do not understand what :bundle does in the latest version of ClojureScript. Anyone care to explain?

MagnusE11:07:17

Hi everyone. It seems like clojurescript after version "1.10.597" and figwheel-sidecar "0.5.19" does not play nicely together. I upgraded my clojurescript version today and the cljs repl would never connect. It was resolved by upgrading figwheel-sidecar to 0.5.20, Anyone else encounter this issue?

bartuka14:07:43

@simon I can't think other way to destructure now, but if you have such nested data structures and need to operate on them, maybe you should consider using specter library

(ns examples.core
  (:require [com.rpl.specter :as specter]))


(def qwq {:a {:a1 {:a11 11 :a12 12} :a2 {:a21 21 :a22 22}}})

(specter/select-one [:a :a1 :a11] qwq)
;; => 11
(specter/select-one [:a :a2 :a21] qwq)
;; => 21

1
MagnusE18:07:22

How do you mute logging from java libraries? I'm currently using Tika, wrapped by the Pantomime library, and the logging output is ultra-spammy, with many debug messages etc.. the messages look like this, and take 99% of the available CPU time. 19:56:27.335 [clojure-agent-send-off-pool-6] DEBUG o.a.f.util.autodetect.FontFileFinder - checkFontfile check .... 20:33:46.076 [Finalizer thread] DEBUG .ScratchFileBuffer - ScratchFileBuffer not closed! (the calls are started in a pmap) I've tried wrapping the parsing function that calls the library with with-out-str and another macro that does the same, but only on *err* . Neither gets any strings. I've been searching the web for any solutions, but I've yet to find one that works. Also, if I add

:jvm-opts ["-Dlog4j.debug=true"]
to my project.clj I do not get any information when starting the repl. So I guess log4j is not the culprit.. Any tips on where to continue my search for a solution?

djanus19:07:08

Depends on the version of log4j, whether you also have slf4j, logback etc, but this example config file provided by clj-http might be a good starting point: https://github.com/dakrone/clj-http/blob/3.x/resources/example-log4j2.properties

djanus19:07:40

It tweaks things towards the debugging end, but setting the log level to error or warn might be just enough

MagnusE19:07:37

found a nuclear solution for this 😄 . log4j2.properties had zero effect for me, but in project.clj I added "-Dlogback.configurationFile=./resources/logcfg.xml" to :jvm-opts . Then in ./resources/logcfg.xml

<configuration>
    <root level="OFF">
    </root>
</configuration>
Which is the horrible nuke. But it is quiet now. 🔕 So I am happy. Maybe I can experiment with more granular config for this at some later point in time.

Eric Ihli19:07:48

What's the best way to turn an alias of a namespace into a keyword? Right now I have this (with model coming from a (:require [foo.model :as model])

(keyword (ns-name ('model (ns-aliases *ns*))))

seancorfield19:07:54

@ericihli Can you explain what problem you're trying to solve? Hard to tell whether there's a better way without the problem statement.

seancorfield19:07:00

(and in particular, is the alias name fixed in this case or is it a variable you're getting from somewhere?)

Audrey Mae Moncada19:07:07

Thinking of a project, what project should I do?

seancorfield19:07:23

@U017JAW40BB That's really far too open-ended a question for anyone to really answer. What sort of problems are you interested in? What sort of projects have you already done in Clojure? In other languages?

Eric Ihli19:07:23

I want to use the namespace as a key in a thing that expects keywords, not symbols or namespaces.

seancorfield19:07:48

And you're getting the alias name from...?

Eric Ihli19:07:03

A require in the ns macro.

seancorfield19:07:27

No, I mean in the code where you're using it. Is this just some hardcoded alias you're picking on?

seancorfield19:07:50

I'm asking because ::model/x will auto-expand to :foo.model/x regardless of whether x exists.

Eric Ihli20:07:38

Ah yes to resolve this confusion, my goal is to to expand ::model/x without needing the x simply because the :foo.model keyword most accurately and succinctly describes what I want. Since the keyword represents a database table, or model type, it looked ugly to have :foo.model/model or :foo.model/type or :foo.model/table-name . I just wanted :foo.model but I wanted it to be expanded from an alias because the actual name is really long to have typed out in code.

seancorfield20:07:42

(keyword (namespace ::model/x))

seancorfield20:07:20

Or whatever dummy x you want.

seancorfield20:07:06

(and of course if you are referring to a known column, just ::model/my_column would do)

Eric Ihli19:07:07

Ah yes. My thought was that this would be more convenient for refactoring and code navigation rather than having both the require + alias as well as hardcoded namespaced keywords that refer to the same thing.

Eric Ihli19:07:34

Trying to ensure the keywords always align with my directory structure + namespaces.

seancorfield19:07:39

Could you explain? Are you trying to build a code refactoring tool?

seancorfield19:07:59

(i.e., what problem are you trying to solve)

Eric Ihli19:07:17

No. This is part of some application-level schema/validation for a NoSQL database.

Eric Ihli19:07:35

I'm treating the namespaces as "tables".

seancorfield19:07:53

OK, I'm still not really following you...

seancorfield19:07:05

Qualified keywords do not have to correspond to actual namespaces.

seancorfield19:07:43

:some-table/some-column is fine without (ns some-table ..) existing anywhere in your code.

Eric Ihli19:07:10

Sorry about that. I'm trying to balance the amount of detail with responding quickly. The root of what I'm trying to do can probably be done a hundred different ways and needing to turn namespaces into keywords is completely unnecessary. It occurred as an idea, then I got curious. It's not really necessary for anything in particular. The full chain of events that led to this: I'm thinking of having a my.models.user and my.models.accounts namespaces. Each will have a set of specs for validation. The documents I'm sending to the database will need to pass a spec. The documents will look like {:my.models.core/model :my.models.accounts, :my.models.accounts/id 1, :my.models.accounts/address "foo",,,} And then the specs would be something like:

(ns my.specs
  (:require [my.models.accounts :as accounts]))

(s/def ::account (s/and (s/keys :req [:my.models.core/model ,,,])
                        (fn [{:my.models.core/keys [model]}]
                         (= model (alias-to-kw accounts))))      ;; vs (= model :my.models.accounts)
I could use namespaced keywords to represent the table rather than turning the namespace into a keyword. {:my.models.core/model :my.models.accounts} could be {my.models.core/model :my.models.accounts/model} and then I could just use the namespaced keyword resolving :: with (= model ::accounts/model) . Or a number of other things I guess. The question was mostly curiosity.

bartuka19:07:33

you can use the specs from the my.models.accounts directly to validate the document:

(ns my.specs
  (:require [my.models.accounts :as accounts]))

(s/valid ::accounts/model your-data)
You do not need to 're-specify' the spec in another namespace. As far as I understand seems you are doing this.

🙏 1
bartuka19:07:22

Ow, I see you edited and included this option as well .. o/

Tim22:07:55

Can we store and retrieve edn in postgresql ? Maybe converting it to json and then back to edn on retrieval, with a fn or by extending the db driver ?

ghadi22:07:07

you can but you will lose query leverage

ghadi22:07:30

EDN is also not the fastest ser/deser format