This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-13
Channels
- # announcements (13)
- # beginners (52)
- # bitcoin (2)
- # calva (2)
- # cider (7)
- # clara (1)
- # clj-commons (11)
- # clj-kondo (6)
- # cljdoc (14)
- # clojure (68)
- # clojure-belgium (1)
- # clojure-denmark (6)
- # clojure-europe (57)
- # clojure-nl (2)
- # clojure-norway (10)
- # clojure-uk (3)
- # clojurescript (7)
- # code-reviews (17)
- # conjure (1)
- # cursive (5)
- # dev-tooling (11)
- # emacs (9)
- # fulcro (12)
- # hugsql (20)
- # introduce-yourself (6)
- # joyride (2)
- # leiningen (1)
- # lsp (61)
- # malli (30)
- # missionary (11)
- # nbb (6)
- # off-topic (26)
- # portal (5)
- # practicalli (5)
- # re-frame (8)
- # releases (8)
- # sci (21)
- # shadow-cljs (3)
- # sql (17)
- # squint (1)
- # xtdb (3)
Does anyone know where https://github.com/michaelklishin is up to these days https://github.com/michaelklishin/quartzite - the link on the repo was snatched and now leads to NSFW
http://clojurequartz.info yeah the docs link here is screwed too
Been thinking, to me a good code base would be something that optimizes for the following: 1. Does it work and deliver the functionality expected by the users (how often it works vs doesn't work for some users) [you want it to work most of the time] 2. Does it run where the users have to wait a lot between user actions and the next action they want to make (how much wait time the user suffers for the most common usage patterns) [you want almost no wait time] 3. Does a change to one part of the code base break other parts of the code base (how many parts are broken for any given part you change) (a part is defined as a Class in OO, and a function in FP) [you want as perfect isolation as possible] 4. How much of the functionality has a test in place which fails if you change or remove the functionality? [you want all functionality to have at least one test that would break] 5. How many tests break when you changed something that did not change or remove any of the functionality? [you want the least amount of tests to break in these cases] 6. On average, how quickly can a developer figure out what part of the code to change, and how to change it, for any given task? [you want to approach less than one day] 7. On average, how quickly can a developer with no experience on the code base, having been explained nothing of it, figure out what part of the code to change, and how to change it, for any given task? [you want to approach less than one week] 8. How many developers can change the code base simultaneously without having to change the same parts of it? [you want as many as possible] It be interesting if I could figure out how to actually measure these things, and then it be interesting if I could compare measurement of these for your average Clojure code base, vs your average "other lang" code base.
I feel list almost all of these metrics are contextual and might be good for some code bases, but might not be great goals for other code bases.
Really? Which one wouldn't be good? I can see not all of them being necessary, but I can't really see them being bad?
There's a difference between being ok/good and being a goal you want to optimize for. 1. Some software is meant to surprise and delight users 2. There's plenty of software where slow turn around time between actions is totally acceptable. eg. software on the end of a slow network connection that requires network updates to meaningfully proceed. There's also many projects where software is meant to proceed without any user input. 3. Obviously, you don't want code to break, but it's not always a priority to optimize 4. There's lots of software that wouldn't be improved with tests (eg. prototypes, experiements) 5. see above 6. https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java 7. https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java 8. There's plenty of projects where it's completely acceptable to not optimize for multiple people working on it at the same time.
1. Some software is meant to surprise and delight users a. How does that contradict the metrics? I don't think any user want to be surprised by the fact the software is buggy? 2. There's plenty of software where slow turn around time between actions is totally acceptable. eg. software on the end of a slow network connection that requires network updates to meaningfully proceed. There's also many projects where software is meant to proceed without any user input. a. Acceptable doesn't mean preferred though, it implies you tolerate a suboptimal experience because you know you can't get better. I would still argue if a competitor delivered it where it was not as slow and was still good in other dimensions they'd start stealing yours customers. b. I was using "user" quite broadly, another machine making an API call, or a worker being triggered by some cron or external event I also included as "user input" 3. Obviously, you don't want code to break, but it's not always a priority to optimize a. Ok, so I feel you're maybe trying to say that the metrics might have trade-offs between each other a little, that's okay, I didn't say how you'd aggregate the metrics together to arrive at a wholistic score. But I think relatively, if one code base for the same functionality is better at all those metrics than another code base, I feel it's clearly a better code base. 4. There's lots of software that wouldn't be improved with tests (eg. prototypes, experiements) a. I disagree. It might not matter as much, but if I came upon a prototype or an experiment that had tests, I'd consider it a better code base than a similar one without. 5. see above 6. https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java a. Low blow 😛 Are you saying RT.java is convoluted. b. I still don't see what you mean though, RT.java isn't the greatest code base ever, if you could refactor it so it worked the same, but a dev could contribute to it in under one day, how would you argue that refactored version is not just a better code base? 7. https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java 8. There's plenty of projects where it's completely acceptable to not optimize for multiple people working on it at the same time. a. Again, it's acceptable, but not preferred. If one day you want to go a bit faster, but you can't add a second contributor because the code base is all tangled up? I still feel it be a better code base if it could. I think if I understood your overall comment, it's that many code base are "good enough" and don't need to be better then that. Is that the gist? If so, I was thinking more of this as a relative measure, two code base can both be "good enough", but I still feel the one that score better in the above metrics would be the better code base no? What are some negatives of a code base that would achieve the above metrics very optimially?
It's not about being "good enough", it's about what you prioritize. There's only so many hours in a day and you have to choose what is worth spending time on. > a. Low blow 😛 Are you saying RT.java is convoluted. In my opinion, RT.java is a great counterexample to many of the metrics. It produces extremely effective software and the code is not optimized for new developers or onramping extra developers. > What are some negatives of a code base that would achieve the above metrics very optimially? Sure, if you could wave a magic wand 🪄, but until that happens, the negative is that you're not spending time on other things that might be higher priority. I'm not saying the metrics listed are categorically bad. Just that it will depend on the use case whether or not they are good metrics to optimize for.
> It's not about being "good enough", it's about what you prioritize. There's only so many hours in a day and you have to choose what is worth spending time on. Ok, but I have metrics that include that as well. Time to deliver a code change. Having the functionality work correctly. Having the software be performant, etc. The question is, if you can write code that is better at all that, how can it not be considered a better code base? If you say, well I can't write that many tests because if I did I wouldn't be able to deliver my next feature. But that's just the two metrics fighting each other: Time to deliver a code change VS time it takes to have a test for all features. But if you could have both be optimal, would it not be a better code base? Sure if you're not able to have both maximally optimal, you will need to pick which one you proritize over the other.
So like, then I'd be interested to know, if I use Clojure, can I deliver my next feature in under 1 day AND have a test for all functionality? But if I use C++ maybe I can't. As an example. It could also be a matter of programmer processes, talent, methodology, etc. And I know it's a tough target to achieve all these things. But I'm trying to find an objective way to assess the quality of a code base.
If you can find a good objective metric to guide your development, I think that's great. I'm skeptical that it's always possible and extremely skeptical that any metric can be universally applied. The idea that software running nuclear reactors, hobby projects, and commercial web apps can all be measured using the same metrics seems unlikely. Maybe a better framework is to come up with objective measures along with guidelines for when they're relevant?
I remember a related thread in #off-topic, https://clojurians.slack.com/archives/C03RZGPG3/p1649993626894209
Ya, I'm also a bit skeptical haha. But looking at the above list, I think it might be more a matter of what you can afford, if you have X time and Y money, if you're making a commercial web app, you might be okay with less tests and more bugs, but would want to have very quick developer ramp up and feature delivery, and a big parallelization factor. If you're doing a nuclear reactor you'd spend the money on less buggy and less wait times between actions, more tests, etc. So in some sense, it would be innovative to find ways to make that time and money afford you even more. So for the same time/money, are there ways to not need to trade away as much?
https://www.youtube.com/watch?v=2V1FtfBDsLU is another resource that touches on some of the same topics
That TDD vs TLD is interesting. Personally, I always struggled with TDD, because it gets in my way of experimenting with how to organize the code and how to factor your functions/classes.
How do i have an exception returned as an object (not sure thats the right term) instead of ending up as a message in my logs/buffer. For some context, the reason i want this is because I like to keep a running log what what i have done in my .clj files by only appending expressions and then evaling there results to comments right below them. Things that throw exceptions dont easily fit into this strategy because they are printed. Does anyone have any suggestions? I thought about with-out-str but that doesn't seem to work, apprently exceptions are printed. It looks like i can also use ex-info and ex-data to return a map with what ever inside it.
Just catch it, no?
(let [thing-that-throws (fn [] (throw (ex-info "I threw" {})))
caught-exception (try (thing-that-throws)
(catch java.lang.Exception e e))]
(ex-message caught-exception))
; =>
"I threw"
I agree with didibus. For example, if you're using nrepl, you could probably get the result you're looking for with some middleware. You might also be able to do it via an IDE extension with something like #C03DPCLCV9N.
Or if you're printing the results of every statement, why not use something like #C035GRLJEP8?
@U7RJTCH6J I'm going to try clerk again shortly. I like everything i see so far.
👍 , you might also be interested in #C0185BFLLSE or #CUDTFQ152
lots of good options 😄
The issue with some of those solutions, or at least how i use them, is they don't create a relationship between the input and output. Portal displays the output, but i need the input right above it when i come back to work 10 minutes later otherwise i have to recreate the context. I think clerk my allow me to have my cake and eat it to a bit. But given i'm mostly looking at clojure structures its typically ok to just produce comments e.g ;; [1 2 3]
There's a way to configure Cider to send command to the REPL, instead of evaluating them. That means the input would show up in the REPL buffer and the result along with the console output would all show up underneath it. You can also switch to inf-clojure and that's just the default behavior (but you lose out on some other Cider features, which is not as much a big deal if you're using clojure-lsp as well though) Assuming you use Emacs
It would be neat though to have a nRepl middleware that just tapped all input/output so they show up in Portal
https://cljdoc.org/d/djblue/portal/0.35.1/doc/guides/nrepl?q=middleware#nrepl
@U90R0EPHA that looks like right. Huh, i feel like i tried something like that last night and wasn't happy with the results, ill double back and look. Thanks! @U0K064KQV good to know thanks! @U7RJTCH6J that middleware option looks like it might help me. thanks a lot for mentioning it!.
I am going to add IFn or similar protocol to a deftype I have, I was wondering if there’s any value to trying to extend AFn or AFunction. What does Fn
interface do? What does public volatile MethodImplCache __methodImplCache;
in AFunction
do?
Regarding __methodImplCache
- defprotocol
uses it.
You should probably extend AFn
because it's the minimal thing that you need, given that it provides sensible default implementation for the methods of IFn
. Not sure how you'd do it with deftype
though.
You can’t extend AFn (abstract class) from a deftype, only an interface like IFn
yeah I know… I’ll just copy most of the code
Depending what the type does, you might only need to implement specific invoke methods
https://github.com/cnuernber/ham-fisted/blob/master/java/ham_fisted/IFnDef.java - an AFn you can extend with deftype 🙂
We should steal that :)
Ended up making my own AFn class. The reason for this is that I wanted a function that fits any call signature, to use as a cached version of the original function. So same as (fn [& args] …)
, the reason for all of this, is that RestFn
and apply
add a lot of stack frames to call stack and my recursive caching blew up the stack. My new version reduced stack frames overhead of a cached call from 11 to 4: https://github.com/RokLenarcic/memento/blob/master/CHANGELOG.md#1142
at memento.mount.UntaggedMountPoint.cached(mount.clj:50)
at memento.mount$bind$fn__2432.doInvoke(mount.clj:119)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
at clojure.lang.RestFn.invoke(RestFn.java:436)
was all rolled up into a single Java method in my AFn classI'm trying to reproduce the following curl behavior:
curl -X POST -H "Content-Type: multipart/form-data" -F "some-id=1" -F "[email protected]" -F "[email protected]"
which I can handle with compojure easily, where the :data
key contains a vector of maps with :filename
and :tempfile
keys.
However with clj-http
's POST I can't seem to reproduce this behavior (neither with the :multipart nor :form-params entries in the request), I'm sure I'm missing something, what would it be?How exactly did you use clj-http
? Also note that you aren't using clj-http
in your project already, it's probably better to use https://github.com/gnarroway/hato due to how few dependencies it has.
Check the clj-http multipart example:
(client/post "" {:multipart [{:name "title" :content "My Awesome Picture"}
{:name "Content/type" :content "image/jpeg"}
{:name "foo.txt" :part-name "eggplant" :content "Eggplants"}
{:name "file" :content ( "pic.jpg")}]
;; You can also optionally pass a :mime-subtype
:mime-subtype "foo"})
@U04V15CAJ I did, but :name and :content are mandatory, and the data is not passed as a :data parameter, so all the keys are wrong, which is the thing I'm trying to figure out
@U2FRKM4TW thanks, I'll check it out
> :name and :content are mandatory, and the data is not passed as a :data parameter
That's because it should be {:name "data", :content (io/file "Somefile1.csv")}
instead of {:data (io/file ...)}
.
I understand, but this is not matching the behavior of the curl
command given as example, which is what I am trying to reproduce. With this curl command, I get a :params
map with a :data
key which has a vector of of {:filename ... :tempfile ...}
keys
@UGH9KH1DF If it's easier for you, you can also use babashka.curl:
https://github.com/babashka/babashka.curl
It supports passing :raw-args
as you would pass them to curl itself
To clarify, this is an example of what I get with curl
{:some-id "1",
:data
[{:filename "File1.csv",
:content-type "application/octet-stream",
:tempfile
#object[java.io.File 0x4e358330 "/var/folders/k8/62f8ygkd3218zct7m85jxrm80000gn/T/ring-multipart-14299532028114201100.tmp"],
:size 9}
{:filename "File2.csv"
:content-type "application/octet-stream",
:tempfile
#object[java.io.File 0x59e11e41 "/var/folders/k8/62f8ygkd3218zct7m85jxrm80000gn/T/ring-multipart-11363390796113361980.tmp"],
:size 104889}]}
Ok I found out how to do it with clj-http, the :name
key is the name of the variable, not of the file, so if I do {:name "data" :content (
it works
@U2FRKM4TW Indeed, sorry I misunderstood I thought I would keep the same structure in the request 😓
Hi folks, I'd like to remove source files from my uberjars, but AIUI Clojure may rely on dynamic compilation even with AOT on. I know that decompilers exist but nevertheless. Anyone know how to navigate this please? We're using Leiningen
Generally AOT compiling everything should mean that source is not needed
With tools.build this is pretty easy to do (just not copy the sources :)) You can read how to do this in the official guide but I made a quickstart here too: https://blog.michielborkent.nl/new-clojure-project-quickstart.html
Thanks jumar! Sorry for slow response, I'm extremely sick and working infrequently. I didn't get access to the error I had as it happened in CI while I was out, but we do use spec. I wonder if allowing sources in the spec-related project would help?
If I have arguments that are separate variables like x, y, z, what’s the most performant way to construct a sequence
well, it’s faster than the list
call
let me try to do an array seq next
(let [x "A" y "C" z "Z"]
(cc/quick-bench
(let [obj-arr (object-array 3)]
(aset obj-arr 0 x)
(aset obj-arr 1 y)
(aset obj-arr 2 z)
(ArraySeq/create obj-arr))))
Evaluation count : 40904400 in 6 samples of 6817400 calls.
Execution time mean : 14,619790 ns
Execution time std-deviation : 7,393095 ns
Execution time lower quantile : 8,199884 ns ( 2,5%)
Execution time upper quantile : 23,642126 ns (97,5%)
Overhead used : 6,636199 ns
=> nil
(let [x "A" y "C" z "Z"]
(cc/quick-bench
[x y z]))
Evaluation count : 34638492 in 6 samples of 5773082 calls.
Execution time mean : 14,493786 ns
Execution time std-deviation : 6,946225 ns
Execution time lower quantile : 8,037109 ns ( 2,5%)
Execution time upper quantile : 24,292886 ns (97,5%)
Overhead used : 6,636199 ns
looks like the speed is the same
but the size of the product isn’t:
(require '[clj-memory-meter.core :as mm])
=> nil
(let [x "A" y "C" z "Z"]
(mm/measure [x y z]))
=> "400 B"
(let [x "A" y "C" z "Z"]
(let [obj-arr (object-array 3)]
(aset obj-arr 0 x)
(aset obj-arr 1 y)
(aset obj-arr 2 z)
(mm/measure (ArraySeq/create obj-arr))))
=> "208 B"
is http://clojuredocs.org missing the new core fns added in 1.11?
At the very top, it says 1.10.1
. And there's https://github.com/zk/clojuredocs/issues/238.