Fork me on GitHub
#beginners
<
2022-02-02
>
walterl00:02:12

Is clojure.tools.namespace.repl/refresh and /refresh-all (still) the "Right Way" to reload deps in a running REPL?

seancorfield00:02:42

I don't think they've ever been the "Right Way"... I've never needed to use them in over a decade of production Clojure work.

seancorfield00:02:03

It's much better to develop a good REPL workflow where you're evaluating every change you make into the running application (started in your REPL), and making sure you're using REPL-friendly techniques where they matter, put the official guide https://www.clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs

👀 1
seancorfield00:02:39

(and my REPLs run for weeks -- sometimes even months -- without ever needing refresh/reload stuff)

seancorfield00:02:56

Now, ClojureScript is a bit different but I assume you're asking about Clojure?

Alex Miller (Clojure team)00:02:03

I've never used ctns/refresh

walterl00:02:41

> I assume you're asking about Clojure? I am 👍

seancorfield00:02:58

I think I've heard both Stu H and Eric Normand say at various times that they've never used that either? Eric talks about this whole thing in his REPL-Driven Development online course (great course, BTW).

walterl00:02:20

Let me rephrase to be more specific, then: what's the "Right Way" to get a new dep, that I just added to deps.edn, loaded into my running REPL?

seancorfield00:02:46

Ah, that has nothing to do with reload/refresh really. I use the add-lib3 branch of tools.deps.alpha (hoping for a variant of it to merge to master at some point!).

🙌 1
seancorfield00:02:54

I actually have a hot key bound in VS Code (via Clover) that takes a dependency hash map and invokes add-libs on it so I can update deps.edn and then expand the paredit selection to the surrounding hash map and hit a key and have the new deps fetched and loaded.

seancorfield00:02:01

@alexmiller How far behind master is add-lib3 these days?

walterl00:02:18

Ah, that's really cool! 💪

Alex Miller (Clojure team)00:02:53

Don't remember, not at a computer

seancorfield00:02:19

Eight commits behind master. That's pretty up-to-date -- thanks for keeping it updated @alexmiller Much appreciated!

walterl00:02:48

And thank you both for the feedback 🙌

Eugen00:02:04

Can I apply a transducer to 1 element ?! I have a potential 18k authors and there is no reason to convert all of them. Data is from DB so iI beliefe it's already in memory - hence the count. Goal is to show first 3 authors then ... and then last author. This is what I got so far. I don't like the solution for last (first (into [] name-xform (take-last 1 persons)))]

(let [name-xform (comp (map :author/name)
                         (map author-name->str))]
    (if (> (count persons) 5)
      ;; lots of authors, processe few
      (let [first3 (into [] name-xform (take 3 persons))
            last (first (into [] name-xform (take-last 1 persons)))]
        (str (str/join ", " first3) " (...) " last))
      (str/join ", " (into [] name-xform persons))))

Alex Miller (Clojure team)00:02:19

Why not use a lazy seq? This is where they shine (partial consumption)

Alex Miller (Clojure team)00:02:07

Oh, missed the last too. If it's a vector, you can just peek it

hiredman00:02:41

you could also use reduce and a more complicated reducing function than conj

hiredman00:02:16

(almost certainly a bad idea, since it will just do a lot of work and then throw it away)

Eugen00:02:35

I think my main pain point is that I don't understand how to apply the result of comp. (name-xform (take-last 1 persons)) [email protected]

hiredman00:02:36

yeah, don't

hiredman00:02:23

xform is transducer that will be applied to a reducing function in some reducing context, in the case of your example code the reducing context is inside into and the reducing function is conj (more or less)

Eugen00:02:11

ok, I guess I was trying to be smarter than I should, thanks all for the help

pinkfrog07:02:06

Hi. I wonder what’s the benefit of providing two reduce siganture, the one has init and one does not. For me, it seems (reduce f init coll) is much more general.

flowthing08:02:02

I believe "forget that the non-init arity of reduce exists" is a piece of conventional wisdom in Clojure programming. In other words, there's little benefit in my opinion. I would recommend just always using the init arity.

Alex Miller (Clojure team)13:02:30

It's a carryover from Lisp

Freeze Dolphin07:02:42

Can anyone teach me how to translate this java code into clojure? Thanks! ;)

package delfeno_mirai;

import net.mamoe.mirai.console.plugin.jvm.JavaPlugin;
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescriptionBuilder;

public final class DelfenoMirai extends JavaPlugin {

    public static final DelfenoMirai INSTANCE = new DelfenoMirai();

    private DelfenoMirai() {
        super(new JvmPluginDescriptionBuilder("io.freeze_dolphin.delfeno_mirai", "0.1.0")
                .name("DelfenoMirai")
                .author("Freeze_Dolphin")
                .build());
    }

    @Override
    public void onEnable() {
        getLogger().info("DelfenoMirai Loaded.");
    }

}

pavlosmelissinos08:02:30

Well, it's difficult to reason about this code without context, let alone try to "translate" it, whatever that means. The snippet you've posted doesn't seem to be doing anything, it just extends another class and overrides one of its methods. On a higher level, in clojure you wouldn't use classes. If you had to write this from scratch you'd have your data:

{:name "DelfenoMirai"
 :author "..."}
and then you'd write one or more functions to do something with that data:
(defn on-enable [data]
  do-stuff)
Of course that doesn't work with the class hierarchy that already exists in your program (onEnable is obviously an event trigger based on its name). If you'd like to replace that snippet with Clojure code that can serve as a drop-in replacement you'd have to interop with Java (on my phone right now but check the official docs)

Benjamin08:02:13

If you don't have choice and you need to extend a java class see https://clojuredocs.org/clojure.core/gen-class

pavlosmelissinos08:02:06

If you're writing Clojure code that you don't intend to use from Java, https://clojuredocs.org/clojure.core/proxy might be a simpler solution

Freeze Dolphin09:02:43

Actually, this is a 'main class' of something like a plugin which can be loaded by a framework. The framework is written in kotlin.

Freeze Dolphin11:02:08

Thanks! I have resolved it myself.

Ben Hili08:02:31

~/dev/cl-backend
λ lein run
hey all anyone know what to do when lein just hangs here? Theres no logs or anything

Benjamin08:02:30

how do you usually poke around when you have a java object and wan't to know it's methods and such? I know bean is helpful - for properties

Alex Miller (Clojure team)13:02:44

clojure.reflect and clojure.java.javadoc namespaces can both be useful

👀 1
Benjamin19:02:07

ah yes feels good

pinealan11:02:24

I have an atom that contains a set. The set contains many substates that can come and go, and each substate is will be updated independently. With such nested structure the updates start to get very messy, i.e. swap root-atom update very-long-fn-that-walks-the-nested-structure-and-surgically-update-a-substate. I have pretty frequent updates (up to 1000s per second) so I also worried whether swap! would start have to keep retrying and cause performance issues. A solution that I thought could work is wrap the substates in atom as well. With that design, it seems substates can more easily be handled in isolation. And when it comes to addition/removal of substates from the root atom, it is also more trivial to just swap! root-atom conj-or-disj sub-aotm. Thoughts? Has anyone else tried this pattern before? Are there other solutions that I’m missing here?

nonrecursive12:02:08

Have you had a look at https://github.com/redplanetlabs/specter ? It’s a library for performing deeply nested queries and transformations. There’s a bit of a learning curve but it’s worth it. re: atom of atoms, I’ve yet to see a case where that’s been a good idea. Have you tried testing whether your case actually results in excessive retries?

nonrecursive12:02:49

https://github.com/noprompt/meander is also useful for wrangling arbitrarily nested structures

pinealan12:02:10

ya I have tried specter before, I have found it useful for situations where I have to broadcast changes that needs to be made in various part of the nested structure

pinealan12:02:10

but for my current use case I only need to be updating a single leaf value in the structure, so using specter doesn’t necessarily make it as clean as I think it can be

pinealan12:02:58

have seen meander but never dived into it, will have a look

FlorianK14:02:32

Hi all, I have a question about a clojurescript import statement. I'm trying to use the [notifee](https://notifee.app/react-native/docs/overview) library, using

(ns reminder.core
  (:require ....
      ["@notifee/react-native :as notifee]))
and this seems to work correctly, however... when I want to access one of the notifee functions, I have to first access a "default" object on this notifee. Like so:
(def n (aget notifee "default")
before I can do stuff like
(.createChannel n #js {:id .. :name ..})
Is this a correct way to have a handy way to use functions from this module? Or can I alter the import statement somewhat to import this "default" object directly and preferably under a different name than "default"?

Zayn Malik14:02:33

Hello everyone, I am facing issue while adding Shadow-cljs as a dependency library in my project. This is my deps.edn:

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/clojurescript {:mvn/version "1.10.773"}
        reagent/reagent {:mvn/version "0.10.0"}
        cljsjs/axios {:mvn/version "0.19.0-0"}
        thheller/shadow-cljs {:mvn/version "2.16.12"}}
After running clj -M:fig:build it logs this error stack. I am unable to comprehend and debug it being a beginner.

pavlosmelissinos15:02:43

Hey @U02TS96DZ7Z, this is a huge stacktrace, could you please use a text snippet to make it shorter or move it into this thread?

1
ghadi15:02:05

Ctrl-Shift-Enter to make a snippet

👍 1
Zayn Malik16:02:10

i hope it serves the purpose

pavlosmelissinos16:02:07

Much better but please delete the stacktrace from your original message as well, it takes up a lot of space in the main channel and makes it harder for other people to read/find stuff - sorry, I should have been more specific

1
Zayn Malik16:02:32

okk sorry for the disturbance

pavlosmelissinos16:02:29

No problem, seems to be a common rookie mistake. I'm not familiar with shadow-cljs but I find it weird that you're getting figwheel-main exceptions on a shadow-cljs project. Are you sure you're on the correct project? What did you run to get that exception?

Zayn Malik16:02:08

ohh okk. i ran clj -M:fig:build after adding the dependency in deps.edn in this project?

pavlosmelissinos16:02:11

Oh right, you mentioned the command you used before. Didn't notice, sorry. It's a shadow-cljs project but you're using a figwheel-main command to build it. Pick either figwheel-main or shadow-cljs (doesn't make sense to have both - not even sure if it's possible) and visit its user guide to find out how to set it up properly. tip: figwheel-main has fewer moving parts so might be simpler; shadow-cljs has more seamless interop with js (you can use js libraries directly)

❤️ 1
Zayn Malik16:02:03

ohhh okk, i was also having this guess (obviously an uneducated one). My real concern is I want to use template literals in clojure.

mbjarland15:02:37

Question about representing monetary values in clojure. I know there are libraries like bankster which support currencies, conversions etc, but if we for the moment assume that it is overkill to associate every monetary value with a currency, what are your thoughts about data types for money when building complex systems dealing with money (e-commerce, accounting, trading etc). BigDecimal? Long value of cents (i.e. $34.50 would be represented as long value 3450 etc)? ... I guess rational is an option though I have to say that intermediate rational values become unreadable fast (123454/532). Long values would probably be faster than BigDecimal...but I get the creeping feeling there might be cases where they fall short. I know a lot of systems use doubles but that is just evil...

andy.fingerhut15:02:03

I've never written software for such concerns, but my understanding is that one of the reasons Java BigDecimal exists is to have explicit control over rounding behavior, in a way that are sometimes legally mandated for financial transactions in some countries.

mbjarland15:02:48

yeah I've seen first hand a case where a major accounting software was unable to balance the books because of rounding errors at the end caused by double (IEEE 754...what all the languages use)

andy.fingerhut15:02:01

Either you have to implement and test those requirements yourself when writing such software, or rely on libraries that help you do it correctly. In either case, you probably need to do application level testing to make sure the entire system meets those financial requiements.

quoll15:02:50

BigDecimal is usually the safest approach. Long can be used, but it is error prone since you have to consider the position of the decimal point all the time, and do it consistently

quoll15:02:17

Also, double goes beyond “evil”. It’s just plain wrong. There is no accurate way to represent those numbers, and errors WILL happen

❤️ 1
mbjarland15:02:56

totally agree...and still see doubles used massively out in the wild

quoll15:02:06

This is because there is too much hubris in this industry.

andy.fingerhut15:02:27

it=BigDecimal. Or it=double?

andy.fingerhut15:02:12

If it=double, then I can imagine it being just plain ignorance of actual requirements, too, rather than hubris.

quoll15:02:53

I call it hubris because it’s hubris that leads to ignorance

mbjarland15:02:02

it as in double...I've been working with Oracle Commerce for longer than I would like to admit and the whole thing is built on top of doubles...just as an example

andy.fingerhut16:02:09

We all start life equally ignorant. We don't all start with hubris 🙂

andy.fingerhut16:02:37

I am not claiming that ignorance is a reasonable excuse for shipping a product that does not meet requirements that you ought to have been aware of, if you were doing your job well.

andy.fingerhut16:02:37

But it is completely true that some requirements are way easier to discover than others, too.

quoll16:02:33

If it were done by someone in the first few years of their career, then yes, it’s simple ignorance. This is why we want to mentor people who are starting out. The profusion of it is often from people with more experience, and it comes down to hubris since they didn’t see the need to understand what is really required for these apps.

absolutejam16:02:36

What's the norm for dependency management / injection with reitit? Partial application of my handler functions, or can I access stuff from the request :data somehow?

Casey06:02:05

I prefer partial application if the handler over stuffing things into the request

absolutejam16:02:31

looks like I can destructure it with

(defn dummy-resp
  [{{{app-data :app-data} :data} :reitit.core/match}]
    {:status 200 :body {:app-data app-data}})
(Where app-data is just a dummy key inside :data for testing)

John Bradens18:02:53

So I'm following a tutorial that says to add prismatic/dommy to the deps.edn file. however the prismatic/dommy github page only has instructions to add [prismatic/dommy "1.1.0"] to the project.clj. But in the tutorial, it uses figwheel and there is only a deps.edn instead of a project.clj. So my question is, how do I add this dependency to deps.edn? What is the format or syntax that I use?

Alex Miller (Clojure team)18:02:55

in the :deps map, prismatic/dommy {:mvn/version "1.1.0"}

John Bradens18:02:16

Ok so I have

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/clojurescript {:mvn/version "1.10.773"}
        pismatic/dommy {:mvn/version "1.1.0"}}
And then I get
Error building classpath. Could not find artifact pismatic:dommy:jar:1.1.0 in central ()

John Bradens18:02:42

What does this mean? Thanks @alexmiller

John Bradens18:02:19

Ohhhhhhh thank you!!!!

Benjamin19:02:49

(clojure.data.json/write-str
 #{1 2 3})
=> "[1,3,2]"
(clojure.data.json/write-str
 {#{1 2 3} 10})
=> "{\"#{1 3 2}\":10}"

this is different from what I expect
I thought this would be
"{[1,3,2]: 10}"

ghadi19:02:39

json doesn't support non string keys

Benjamin19:02:56

I see thanks