Fork me on GitHub
#clojure
<
2023-01-20
>
Steph Crown07:01:28

Hi guys. With functions, we can add docstrings like this:

(defn foo
  "I add 1 to my argument."
  [x]
  (+ x 1))
Is it possible to do something like this with multimethods?
(defmulti foo #(do %))

(defmethod foo :bar [x] (baz x))

(defmethod foo :bar
  [x]
  (baz x))

phronmophobic07:01:57

There's an example of docstrings in the examples part of the docs: https://clojuredocs.org/clojure.core/defmulti#example-561b3b1ae4b0b41dac04c957

phronmophobic07:01:33

Anyway, defmulti allows an optional docstring after the function name (similar to defn)

Steph Crown07:01:10

Thanks @U7RJTCH6J. This was helpful.

👍 2
Joshua Suskalo18:01:15

An important thing here: defmulti allows a docstring, but defmethod does not.

Joshua Suskalo18:01:15

This is because in general all multimethod implementations are supposed to follow the same interface, they aren't supposed to do radically different things, so if you need to add documentation specific to a particular multimethod implementation that can mean either that the abstraction doesn't fully fit, or that the documentation needs to be implementation comments that aren't important to callers.

Casey11:01:22

Does anyone know of any opensource tooling for translating tempura's edn string files? Something that non programmers could use to enter translations?

Casey10:01:15

thanks for the tip, pottery would be an alternative to tempura (https://github.com/ptaoussanis/tempura). tempur uses its own edn based format for storing strings, which isn't compatible with gettext / po edit.

Casey10:01:36

I was hoping someone might know of a tempura edn <-> po file converter or something to re-use existing i18n tooling

pavlosmelissinos13:01:01

(case x
  0   true
  nil false)
> Performance warning, /home/pavlos/Workspace/... - hash collision of some case test constants; if selected, those entries will be tested sequentially This was unexpected, could someone please explain to me why it's happening? (On Clojure CLI version 1.11.1.1149)

chrisn13:01:16

user> (def zero 0)
#'user/zero
user> (.hashCode zero)
0
user> (hash nil)
0

chrisn13:01:53

I wouldn't use case statements with targets that are heterogeneous datatypes.

pavlosmelissinos13:01:34

Very interesting, I didn't know that. I should have thought to use hash for debugging 🙂 > I wouldn't use case statements with targets that are heterogeneous datatypes. Yeah, doesn't seem to be a good idea... Thanks!

👍 2
emccue15:01:14

What are folks using to process background/delayed jobs?

emccue15:01:24

we have a horrible home rolled thing. Looking for something that will work out of the box w/ whatever debugging dashboards are needed

dpsutton15:01:52

what are debugging dashboards?

dpsutton15:01:35

happy with it

emccue15:01:47

> what are debugging dashboards? Like, what jobs are running, if any failed, etc

dpsutton15:01:14

oh like a full UI and such? or just a bit of introspection?

emccue15:01:39

cop out answer - whatever we need to debug

dpsutton15:01:00

then you can get stats from quartz. no dashboard though unless you make it yourself

emccue15:01:03

i'm trying to set up quartz on my end and stuff like this

(defn- load-class ^Class [^String class-name]
  (Class/forName class-name true (classloader/the-classloader)))

(defrecord ^:private ClassLoadHelper []
  org.quartz.spi.ClassLoadHelper
  (initialize [_])
  (getClassLoader [_]
    (classloader/the-classloader))
  (loadClass [_ class-name]
    (load-class class-name))
  (loadClass [_ class-name _]
    (load-class class-name)))

(when-not *compile-files*
  (System/setProperty "org.quartz.scheduler.classLoadHelper.class" (.getName ClassLoadHelper)))
is what i'm trying to figure out

emccue15:01:52

other than quarzite's docs straight up just redirecting to pornography if i don't use the wayback machine, the bootstrapping is the annoying part so this is a great resource

dpsutton15:01:07

yeah. use us as a guide

emccue15:01:46

like, okay say a job fails

emccue15:01:53

how can I know it fails and retry it

dpsutton15:01:28

(cron/with-misfire-handling-instruction-fire-and-proceed) i think it’s configured on the trigger

dpsutton15:01:36

i have to conduct an interview. i can be back in an hour

dpsutton15:01:37

but i see cron/with-misfire-handling-instruction-do-nothing, ignore-misfires, so i would check that namespace and see what in Quartz it is doing, and then check Quartz for how all of that works

emccue15:01:05

yeah i mean more manual retries/visibility but i'll read more rn

dpsutton15:01:40

i’d have to read code to give more answers so we’re about the same level then 🙂

msolli16:01:16

I’ve made https://github.com/msolli/proletarian, which we use at work. It’s good if you need persistence and certain guarantees - details in readme.

👀 2
emccue18:01:17

Why does proletarian need its own schema?

emccue18:01:37

(like, im not pg-savy enough to know the implications of CREATE SCHEMA)

msolli19:01:12

It’s configurable, so it doesn’t strictly need its own schema. I like to keep its two tables separate from other tables in the application/system. There’s not much to PG schemas - they are essentially namespaces.

lread19:01:09

https://github.com/nilenso/goose might also be worth a peek, it's a relatively new addition to the space.

2
emccue00:01:56

Is there a way to make proletarian do a recurring job? Or is that a bit too much hacking

emccue00:01:42

I guess if I don't need that....

emccue00:01:08

any horrible implications if I run the worker in every web instance?

msolli07:01:48

You can have one (or several) workers on every instance. Each worker can have a thread pool with one or many threads. You choose the concurrency levels based on your resources and expected load.

msolli07:01:23

For recurring jobs I do one of two things: 1. Schedule the next job from the current one. 2. Schedule from an external signal like AWS Eventbridge scheduled event.

msolli07:01:55

BTW, there’s #C01UG9GMVBJ if you want to dive deeper. Happy to help you along!

Eric Scott18:01:53

I think I'm confused about how to program the P in my REPL. I have a defrecord for something called JenaGraph which when using the defaults will render its entire contents, which pretty much chokes my REPL buffer.

(defmethod print-method JenaGraph
  [x writer]
  (.write writer (str "<JenaGraph hash=" (hash x)">")))
When I calll the usual print methods, this works fine:
> (print g)
<JenaGraph hash=-1751499775>
But when I create a new instance of this record, I get the original REPL-choking output. Is there something I can do to direct my REPL to do the short version?

Alex Miller (Clojure team)18:01:24

you probably need to re-evaluate the print-method implementation to pick up a new record definition

Eric Scott18:01:20

I've restarted a fresh REPL with the new defmethod, and I'm still having this problem.

hiredman18:01:23

sounds like the code that defines the record is being loaded twice, with the print-method extension happening between them

Eric Scott18:01:35

Why then does println still have the desired output?

hiredman18:01:04

I guess it isn't clear from your description but you say printing stops working "when I create a new instance" so presumably you have an "old instance" and that old instance is maybe created between the two loads

hiredman18:01:52

a quick diagnostic you can do is a stick a (println 'A) above the defrecord, and a (println 'B) above the defmethod, and then load up a new repl. if you see A printed out twice you having a loading issue, something is causing code to be loaded more than once which can be problematic in many different ways. If you ever see A without a B after it, then you know a print-method was never installed for the most recent definition of the record

hiredman18:01:37

if you get B output before you ever get an A output, then you also have something interesting going on(likely also a loading issue, but manifesting in a more interesting way)

Eric Scott18:01:52

So in a fresh REPL, here is the whole output:

About to define JenaGraph
Just defined JenaGraph
About to define print-method for JenaGraph
user> (in-ns 'ont-app.igraph-jena.core)
#namespace[ont-app.igraph-jena.core]
ont-app.igraph-jena.core> (make-jena-graph)
{:model
 #object[org.apache.jena.rdf.model.impl.ModelCom 0x1467a240 "<ModelCom   {} | >"]}
ont-app.igraph-jena.core> (def g *1)
#'ont-app.igraph-jena.core/g
ont-app.igraph-jena.core> g
{:model
 #object[org.apache.jena.rdf.model.impl.ModelCom 0x1467a240 "<ModelCom   {} | >"]}
ont-app.igraph-jena.core> (println g)
<JenaGraph hash=112519425>
nil

hiredman18:01:41

Oh, sure, the repl uses prn to print things, not println

Eric Scott18:01:25

(prn g)
<JenaGraph hash=112519425>

hiredman18:01:30

you have some kind of pretty printer extension thing to make the repl output print nicer and it doesn't print records correctly

hiredman18:01:57

you can tell because the record is printing as a just a map

hiredman18:01:25

which is something, clojure.pprint does or did at one point

Eric Scott18:01:10

So this is something cider or emacs might be doing?

hiredman18:01:44

% clj
Clojure 1.11.1
user=> (defrecord F [])
user.F
user=> (prn (->F))
#user.F{}
nil
user=> (require 'clojure.pprint)
nil
user=> (clojure.pprint/pprint (->F))
{}
nil
user=>

Eric Scott18:01:42

You're right, I don't have this problem when I clj --repl I gotta run now, but you've given me a good direction to investigate. Thanks!

Eric Scott14:01:16

Looks like setting the value (in emacs) of cider-print-fn to nil defers to nrepl.middleware.print/print-fn, for all pretty-printing which looks like it solved the issue for me. https://docs.cider.mx/cider/usage/pretty_printing.html

pavlosmelissinos23:01:05

Is this a good way to vet candidate dependencies if I care about keeping my library/app lean/small? clj -X:deps tree :project '{:deps {org.jsoup/jsoup {:mvn/version "1.15.2"}}}' My thoughts: 1. I usually look at deps.edn which doesn't say anything about transitive deps, this seems nicer I think. 2. It will be a bit unforgiving to some libraries because it doesn't take into account common deps but I can live with that (and I could always use :extra).

Alex Miller (Clojure team)23:01:01

you can also use list instead of tree (also shows licenses)

👌 2
Alex Miller (Clojure team)23:01:45

and https://github.com/clojure/tools.deps.graph can be helpful if you want a diagram (optionally with lib sizes!)

👍 2
hiredman23:01:13

https://github.com/phronmophobic/snowball is a neat tool, haven't used it a ton

🙏 2
2
dpsutton23:01:40

track your build sizes. we don’t do that yet but it’s on my list

2
dpsutton23:01:12

we glance ad-hoc of course but i mean a dashboard, a report, a chart showing release vs size, etc

dpsutton23:01:25

a nice feature to tools deps would be to list jar sizes, with an option to see transitive deps rolled up on their top level dep or the full tree annotated with size

hiredman23:01:10

checkout the snowball tool above, you get size per dep, transitive size per dep

dpsutton23:01:26

oh i will then. thanks

phronmophobic23:01:40

suggestions and feedback welcome!

dpsutton23:01:50

@U7RJTCH6J some feedback: that’s outstanding! I was just dreaming of it and there is is for me 🙂

😄 2
2
pavlosmelissinos00:01:57

Very nice indeed 🙂