Fork me on GitHub
#clojure
<
2022-01-29
>
hiredman00:01:05

in this analogy go blocks are threads and the threadpool go blocks run is the cpu

hiredman00:01:47

and go blocks are non-preemptive because the threadpool can't run another go block until the currently running go block yields by doing a channel operation

p-himik00:01:00

Thanks. That's a concept that I guess I would previously call "interrupt". Funny how a programming language-related discussion (and a look up of the etymology of "preemptive") just changed my perception of the word in a natural language.

p-himik00:01:21

Yeah, got to that article as well. :)

p-himik00:01:14

Although I guess then I'd personally use "preemptible" in the OP.

phronmophobic00:01:40

I think this golang issue has some interesting discussions about cooperative vs preemptive scheduling: https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md You can also look up various resources about Erlang's BEAM VM's scheduler.

thanks3 1
pinkfrog00:01:48

Reading the doc string of async/put

(defprotocol WritePort
  (put! [port val fn1-handler] "derefable boolean (false iff already closed) if handled, nil if put was enqueued. Must throw on nil val."))
One part I couldn’t understand is the nil if put was enqueued . I run some tests and find that (put!) either returns true or false. But never nil.

hiredman00:01:16

because you are calling clojure.core.async/put! which is not the same as that put!

pinkfrog00:01:29

Thanks. That clarifies every thing!

hiredman00:01:52

that is clojure.core.async.impl.protocols/put! or something

Drew Verlee02:01:38

Why does Clojure https://github.com/ring-clojure/ring/blob/1.9.0/ring-devel/src/ring/middleware/reload.clj#L21 need to use a Queue need to use https://clojuredocs.org/clojure.core/locking? Is it because the namespaces have to reloaded independently and in a certain order? Are their any built in clojure functions that would do the same thing? Assuming its because of order, I guess the question would then become, how does https://github.com/weavejester/ns-tracker know that order? I guess that's in the clojure NS hashmap? I'm just digging into this idea a bit

(defn- reloader [dirs retry?]
  (let [modified-namespaces (ns-tracker dirs)
        load-queue (java.util.concurrent.LinkedBlockingDeque.)]
    (fn []
      (locking load-queue
        (doseq [ns-sym (reverse (modified-namespaces))]
          (.push load-queue ns-sym))
        (loop []
          (when-let [ns-sym (.peek load-queue)]
            (if retry?
              (do (require ns-sym :reload) (.remove load-queue))
              (do (.remove load-queue) (require ns-sym :reload)))
            (recur)))))))

hiredman02:01:47

ns-tracker parses ns forms from the top of clj files

potetm02:01:52

I’m assuming it needs a lock because requests can come in simultaneously.

hiredman02:01:17

The queue thing is really odd, my guess would be it is left over from a more complicated function that has had a lot of bits deleted

potetm02:01:18

Yeah, in principle you just need to lock on something and then reload the namespaces. Not sure why they put a dequeue in there.

hiredman02:01:46

The queue is maybe to support if loading causes an error

hiredman02:01:13

You fix the error, then it can restart from the queue

potetm02:01:22

Ah, yeah good catch

Drew Verlee02:01:48

> The queue is maybe to support if loading causes an error So if the (require ns-sym :reload) hits an error while being interpenetrated, something like a missing parens, it would ... something something... let the developer fix the file and then the queue would pickup where it left off?

hiredman03:01:12

Yeah, the queue is persisted between runs, so the next time you go to reload all the old namespaces will still be in there

👍 1
Drew Verlee03:01:53

So it saves time & energy by not doing extra reloads?

hiredman03:01:53

It preserves information that would other wise be lost between runs

hiredman03:01:10

E.g. if namespaces x y and z end up in the queue to reload and y throws an error, then you fix y, z is still in the queue to be reloaded

seancorfield03:01:34

This isn't in Ring proper, is it? This is just in the weird reload middleware, I hope?

Drew Verlee03:01:48

i mean, i'm not sure what it means to be proper. 😆

seancorfield04:01:40

That's middleware, not core Ring.

seancorfield04:01:30

I recommend not using the reload middleware. Or any of these reload/refresh things. Just don't do it.

Drew Verlee04:01:48

@U04V70XH6 why not? Being able to quickly update parts of my code to see how it changes the over system is something i do a lot to great effect. I thought that this was in a similar vein or is there some drawback im missing?

Drew Verlee04:01:20

This would be for development purposes only of course.

seancorfield04:01:21

You do not need any of this reload stuff to do that.

seancorfield04:01:40

I develop live in web apps all the time. Never use any reload/refresh stuff.

seancorfield04:01:25

I think they just encourage bad habits.

seancorfield04:01:47

I also think it's a really bad idea to have a different dev stack to your QA/prod stack.

Drew Verlee04:01:48

there must be a terminology to implementation break down. Client sends a request to server Server improperly handles it and returns not the result i wanted I need to change the server handler. Currently i have to shut down my server, make my change, then restart it. i would call that a reload. My goal is just to make this faster in my dev environment.

seancorfield04:01:26

No, you don't have to do that.

Drew Verlee04:01:43

I fail to compute. Which part don't i have to do?

seancorfield04:01:50

Have you read the REPL-friendly section of the official docs with the REPL guide?

seancorfield04:01:12

You absolutely do not need to shutdown a server to make changes and you do not need "reload" stuff.

Drew Verlee04:01:44

hmmm. let me load up my little server and see what i'm doing here.

seancorfield04:01:55

I patch web apps running on remote servers via the REPL with none of this middleware.

seancorfield05:01:13

(I'm on a one-man campaign to stamp out these reload/refresh libraries and get people doing "proper" REPL-driven development!)

😅 1
👍 1
❤️ 1
Drew Verlee05:01:16

I have read those, but i might not have understood them because the context wasn't there for me

Drew Verlee05:01:55

But in my defense, people can macro and framework you into patterns that getting out of is basically useless. So you have to forgive those of us that think Up is down.

seancorfield05:01:16

Macros bad. Frameworks bad. 🙂

seancorfield05:01:28

I think we as a community are really bad at teaching beginners the "right" way to do stuff 😞

seancorfield05:01:55

Although, to be fair, http://clojure.org has really good guides. But people don't read them end-to-end, and they rely on random tutorials online and random frameworks that enshrine "bad" practices 😞

Drew Verlee05:01:02

I'm getting my little server setup here, then i'll really look at what i'm doing so vs what's in that doc and see if there is an easy win.

Drew Verlee05:01:42

I was unable to make a change that allowed me to re-eval my handler and change the reponse message. This is a cljs namespace that is watched by shadow that produces a node library that we then run using node. It's a node-library because that's useful if your creating AWS lambdas. Or at least I haven't been able to look into this notion further. I want to create a node HTTP server that we can use for local development to talk to our browser client. Currently, if i want to change the message body, the only way i know how is to kill running node and then re-eval the code, then boot up node again. I was assuming i was going to have to save the server in an atom and call some kind of stop, then reload the code, then start it again. Which is similar in nature to what i see that ring lib was doing.

(ns server.corev2
  (:require [taoensso.timbre :refer [info]]
            [macchiato.server :as http]
            [reitit.ring :as ring]
            [reitit.coercion.spec :as c]
            [reitit.swagger :as swagger]
            [macchiato.middleware.params :as params]
            [reitit.ring.coercion :as rrc]
            [macchiato.middleware.restful-format :as rf]))

(defn no-auth-routes
  []
  [""
   ["/eql" {:post {:responses {200 {:body {:message string?}}}
                   :handler   (fn [request respond _]
                                (respond {:status  200
                                          :body    {:message (str "Hello there you handsome angel" )}}))}}]])

(defn wrap-body-to-params
  [handler]
  (fn [request respond raise]
    (handler (-> request
                 (assoc-in [:params :body-params] (:body request))
                 (assoc :body-params (:body request))) respond raise)))

(defn app
  []
  (ring/ring-handler
    (ring/router
      [(no-auth-routes)]
      {:data {:middleware [params/wrap-params
                           #(rf/wrap-restful-format % {:keywordize? true})
                           wrap-body-to-params
                           rrc/coerce-request-middleware
                           rrc/coerce-response-middleware
                           ]}})
    (ring/create-default-handler)))

(defn server []
  (info "Hey I am running now!")
  (let [host "127.0.0.1"
        port 3001]
    (http/start
      {:handler    (app)
       :host       host
       :port       port
       :on-success #(info "macchiato-test started on" host ":" port)})))


(comment
  ;; start server
  (server)

  ;; ➜  server git:(server-setup) ✗ curl -X POST localhost:3001/eql
  ;; {"message" : "Hello there you handsome angel"}%


  )

Drew Verlee05:01:17

So we have • shadow server • Nprel server • node • emacs If someone can paint me a picture of whats going on here, i would be delighted.

Drew Verlee05:01:11

Emacs sends request to nrepl to start server, nrepl then forwards it to shadow server to translate to JS file. Node notices js file changed and does thing aka start server.

Drew Verlee05:01:25

So i think if i didn't want to stop my server, i would need to have it 1) have the raw uncomplied cljs code 2) have a port open listening for ast updates

seancorfield06:01:20

No idea about cljs. I thought Shadow was supposed to auto-reload all changes anyway? (never used it -- when I tried Figwheel Main it seemed to auto-reload cljs files)

seancorfield06:01:00

But the compilation step in cljs (from cljs to JS) means it's never going to be as smooth as clj -- and earlier you were talking about Ring etc which is clj, not cljs.

Drew Verlee12:01:07

Yea, i was hoping that lessons transfered, reitit has a ring middleware for cljs. The solution is likely that shadow needs to run in app mode. This will likely be very relevant https://shadow-cljs.github.io/docs/UsersGuide.html#NodeHotCodeReload I'll spin down this thread myself, just wanted to end it with a link in case someone else found it.

caleb.macdonaldblack13:01:10

Sorry if off topic for Clojure, but what are our thoughts on a data access layer api? Would it be better to go for a more general CRUD approach, or a more specific approach? For example: update-user vs change-email . I’m leaning towards a general approach for my data access layer and leave specifics to the business layer. Thoughts? Also happy to delete this and move to the appropriate channel, I’m just not sure which one would be more appropriate.

p-himik13:01:56

> I’m just not sure which one would be more appropriate. In such cases, #off-topic is a safe bet.

Max17:01:33

When you say API, do you mean something a part of and consumed by your application, or something you present to others? For the former, I find I get better composability by having functions return queries rather than execute them, and execute them elsewhere. Honeysql’s helpers help a lot with this.

shayne.koestler16:01:52

Does anyone have an example of using https://clojure.org/guides/deps_and_cli#prep_libs in this wild? I'm trying to add a java class to my project that will help facilitate better iterop. Its working if I manually put the compiled class file on the classpath, but this option looks like the proper way. Any help would be much appreciated! (Edit: I have tried adding the java class to a separate project with the :deps/prep-lib declared in deps.edn but it does seem to have any effect)

Joshua Suskalo17:01:41

Hey, so prep-lib is designed only for use in dependencies. So in your case if you're compiling a java class, you'd do that as a part of the prep-lib step so that someone depending on your library as a git dep could use it with the compiled java class.

Joshua Suskalo17:01:53

Normally you'll just have your own alias in your deps.edn that will compile the java and put it in the right place

Joshua Suskalo17:01:12

I have a library meant for doing both of these as easily as possible for simple cases with java. https://github.com/IGJoshua/americano

shayne.koestler19:01:52

Thanks! I'll check this out for sure

shayne.koestler21:01:01

Works like a charm. Really appreciate your reply and work on this project

Joshua Suskalo15:01:54

Glad it can be of some use!

Alex Miller (Clojure team)16:01:59

Did you run the prep step in the separate project?

shayne.koestler19:01:06

I think I was running it in the dependency. It looks like I'm actually supposed to be running it in the parent.

shayne.koestler19:01:17

Getting a FileNotFoundException but I think I should be able to work that one out

Alex Miller (Clojure team)19:01:57

Yes, you'll run it in the project that depends on the dep that needs prepping

shayne.koestler20:01:08

OK thanks for the help

Alex Miller (Clojure team)16:01:16

The first step is having a build alias that compiles the Java class, probably with something like https://clojure.org/guides/tools_build

mbarillier16:01:38

I'm trying to call a java superclass method in a deftype call, but it's (naturally) recursing and blowing up the stack:

(deftype ThingOne [x y])

(= (->ThingOne 1 2) (->ThingOne 1 2))
;; =>
;; false  <-- expected

(str (->ThingOne 1 2))
;; =>
;; "scratch.records_types_protocols_interfaces.ThingOne@7d195123"

(deftype ThingTwo [x y]
  Object
  (equals [this o] (or (identical? this o)
                       (and (.equals x (.x o))
                            (.equals y (.y o)))))
  (hashCode [this] (Objects/hash (into-array Object [(.x this) (.y this)])))
  (toString [this] (Objects/toString this)))

(= (->ThingTwo 1 2) (->ThingTwo 1 2))
;; =>
;; true  <-- yay!

(hash (->ThingTwo 1 2))
;; =>
;; 994  <-- also works ... so far, so good

;; stack overflow:
(str (->ThingTwo 1 2))
... so I've managed to override .equals and .hashCode OK, but I want to call the stock version of Object.toString in ThingTwo and clojure keeps calling my implementation. what's the trick I need to employ? I also gave this a shot but it fails: (toString [this] (.toString ^Object this)), btw ...

mbarillier17:01:19

well, whaddayaknow ... commenting out the toString method works. I'd tried another test and clojure barked about a missing method or something like that, must have been a poor test. in any event, this works:

(deftype ThingTwo [x y]
  Object
  (equals [this o] (or (identical? this o)
                       (and (.equals x (.x o))
                            (.equals y (.y o)))))
  (hashCode [this] (Objects/hash (into-array Object [(.x this) (.y this)])))
  #_(toString [this] #_(Objects/toString this)
                   (. Object (toString this))))

(str (->ThingTwo 1 2))
;; =>
;; "scratch.records_types_protocols_interfaces.ThingTwo@3e2"

emccue17:01:48

Objects toString just calls toString, which calls Objects toString

emccue17:01:09

and that was your loop

emccue17:01:48

If you want a toString you just need to make it

(str "ThingTwo[x=" x ", y=" y "]")

mbarillier17:01:45

point was to not have to create a custom method, but to use the superclass' implementation.

ghadi18:01:08

If you printed the stacktrace, the call loop would have been evident

emccue18:01:44

the superclass’s implementation is what you get by not writing anything

mbarillier17:01:19
replied to a thread:I'm trying to call a java superclass method in a `deftype` call, but it's (naturally) recursing and blowing up the stack: (deftype ThingOne [x y]) (= (-&gt;ThingOne 1 2) (-&gt;ThingOne 1 2)) ;; =&gt; ;; false &lt;-- expected (str (-&gt;ThingOne 1 2)) ;; =&gt; ;; "scratch.records_types_protocols_interfaces.ThingOne@7d195123" (deftype ThingTwo [x y] Object (equals [this o] (or (identical? this o) (and (.equals x (.x o)) (.equals y (.y o))))) (hashCode [this] (Objects/hash (into-array Object [(.x this) (.y this)]))) (toString [this] (Objects/toString this))) (= (-&gt;ThingTwo 1 2) (-&gt;ThingTwo 1 2)) ;; =&gt; ;; true &lt;-- yay! (hash (-&gt;ThingTwo 1 2)) ;; =&gt; ;; 994 &lt;-- also works ... so far, so good ;; stack overflow: (str (-&gt;ThingTwo 1 2)) ... so I've managed to override `.equals` and `.hashCode` OK, but I want to call the stock version of `Object.toString` in `ThingTwo` and clojure keeps calling my implementation. what's the trick I need to employ? I also gave this a shot but it fails: `(toString [this] (.toString ^Object this))`, btw ...

well, whaddayaknow ... commenting out the toString method works. I'd tried another test and clojure barked about a missing method or something like that, must have been a poor test. in any event, this works:

(deftype ThingTwo [x y]
  Object
  (equals [this o] (or (identical? this o)
                       (and (.equals x (.x o))
                            (.equals y (.y o)))))
  (hashCode [this] (Objects/hash (into-array Object [(.x this) (.y this)])))
  #_(toString [this] #_(Objects/toString this)
                   (. Object (toString this))))

(str (->ThingTwo 1 2))
;; =>
;; "scratch.records_types_protocols_interfaces.ThingTwo@3e2"

Dane Filipczak21:01:48

What explains this behavior and how do I proceed if I'd like to use a type's field for its equality identity, regardless of the type it's being compared with?

(deftype bar [value]
    Object
    (equals [_ other]
      (if (= (type other) bar)
        (= (.value other) value)
        (= value other))))

  (= (->bar 1) (->bar 1)) => true
  (= (->bar 1) 1) => true
  (= 1 (->bar 1)) => false

Dane Filipczak21:01:25

It's possible that this isn't a smart thing to attempt.

dpsutton21:01:19

I don't believe you can accomplish this. from (source =)

([x y] (clojure.lang.Util/equiv x y))
and that will dispatch off of the first argument. And integers know how to compare themselves to others, just like how your type knows how to compare itself to others

👍 1
dpsutton21:01:59

static public boolean equiv(Object k1, Object k2){
	if(k1 == k2)
		return true;
	if(k1 != null)
		{
		if(k1 instanceof Number && k2 instanceof Number)
			return Numbers.equal((Number)k1, (Number)k2);
		else if(k1 instanceof IPersistentCollection || k2 instanceof IPersistentCollection)
			return pcequiv(k1,k2);
		return k1.equals(k2);
		}
	return false;
}

👍 1