This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-18
Channels
- # announcements (18)
- # babashka (34)
- # beginners (47)
- # biff (1)
- # calva (99)
- # cider (1)
- # clojure (99)
- # clojure-austin (13)
- # clojure-australia (1)
- # clojure-dev (53)
- # clojure-europe (30)
- # clojure-nl (1)
- # clojure-norway (7)
- # clojure-spec (7)
- # clojure-uk (1)
- # clojurescript (50)
- # cursive (11)
- # datahike (20)
- # datomic (10)
- # fulcro (7)
- # graalvm (8)
- # gratitude (1)
- # introduce-yourself (7)
- # jackdaw (1)
- # joyride (1)
- # lambdaisland (7)
- # lsp (2)
- # malli (7)
- # nbb (1)
- # off-topic (1)
- # portal (24)
- # re-frame (8)
- # reagent (13)
- # reitit (7)
- # releases (2)
- # remote-jobs (8)
- # rewrite-clj (3)
- # sci (1)
- # scittle (21)
- # shadow-cljs (2)
- # testing (3)
- # tools-deps (22)
- # web-security (19)
I’d like to create a custom version of TTL-based memoization where the cache is not invalidated in case the function throws an exception.
1. Is it possible to do so with clojure.core.memoize
?
2. Is there another library that provides this functionality?
Remark: The function I want to memoize is a function that fetches data from an API that from time to time is unavailable.
Haven't checked myself but I'm 80% certain that this library either has that kind of functionality or has all the necessary building blocks to implement it on your own: https://github.com/clojure/core.cache
Looking at it
Stumbled on this while searching to learn what you mean by "TTL" (time to live). https://github.com/clojure/core.memoize/blob/master/docs/TTL.md
https://securityonline.info/cve-2022-42889-apache-commons-text-code-execution-vulnerability/ I wonder if Clojure uses the vulnerable functions internally
Hello. A #babashka related question. I'm trying to write a release script (for a ClojureScript library), that prepares & uploads JAR file to Clojars
The rest of the scripts in the project are 100% babashka :star-struck:
But I had some trouble getting some dependencies (e.g. slipset/deps-deploy
) working under babashka. It's quite standard stuff (actually no Clojure compilation takes place, just CLJS files copied to JAR + POM generated), in other words straight https://clojure.github.io/tools.build/clojure.tools.build.api.html + https://github.com/slipset/deps-deploy
Due to the dependencies issue, I started shelling out via (clojure ...)
in babashka tasks, e.g.
release {:doc "Try to shell out to clojure"
:task (clojure "-X:release :version 22.10.10 :library server/cljs-web3-next")}
> But this would need its own deps.edn
, is slower and can't use the nice babashka helpers
Now the question:
1. Do you have examples or recommendations of how to achieve it (prepare JAR & publish to Clojars) with just Babashka?
2. If not, any other recommendations how to make babashka + Clojure (JVM) work smoothly, reduce duplication (between bb.edn
and deps.edn
for example) etc.
Thanks in advance,
Madisdeps-deploy depends on some Java libraries which aren't available in bb. But I think clojars / maven also support REST apis that support releasing artifacts which you could use, if you want to do it all in bb - I've never tried
Great idea, thanks for suggesting! I'll see if I can publish over HTTP
> From first glance looks like Clojars doesn't have (publicly documented) endpoint for uploading JAR-s
> https://github.com/clojars/clojars-web/wiki/Data
In case I go with publishing over HTTP, do you recommend babashka.curl
or babashka/clj-http-lite
or some other library for it? If I had to guess, one POST request should do it
Thanks borkdude, you're awesome. I hope your YouTube channel gets more views. Viewed bunch of your demos there!
@U6P6QJTUZ if you find the api for clojars publishing can you doodle it down?
i found that zip upload route but i don't get the sense that its really the intended path
hey, is there a way to force/shortcircuit cached/memoized functions returns? (consider having a function that is wrapped in ttl cache, is there a way for me to adjust the contents of this cache - like have a future running in parallel updating values)
> like have a future running in parallel updating values Why not just make that future call that function periodically?
could work, but... What if you are trying to avoid unwanted latency, so your cache is sort of: gimmie the best value for this at this time, doesnt have to be exact as it changes slightly. However during the cache window I want to run at least one update on the data. So for processing the cache is needed in order to provide latency. However the mechanics of the cache are not really required - well at least as long as there is backdoor to cached values.
Sounds like https://github.com/clojure/core.memoize should have what you need.
Workers + Queues Hey team, How would you think about the following problem? I have a bunch of in-memory queues — potentially thousands of them:
app-a-q [a1, a2, a3]
app-b-q [b1, b2, b3]
...
And then I have a pool of workers:
worker-1, worker-2, worker-3
I want my workers to constantly take from the queues. But I want to ensure that all items in queues are handled serially.
i.e if worker-1
is currently processing a1
, then no worker can take a2
. Buut they can b1
.
For queues right now I am using LinkedBlockingDeque
. For workers I spin up a few (future (loop []…)
blocks.
Is there some async construct that can help me achieve my goal?async/alts!
sounds nearly perfect for what you want, unsure if it will serially take from a
if you had each worker using async/alts!
on the same source channels/queues
each queue gets a go-loop taking from it and submitting to any number of executors would do it. You can balance the number of executors as you like and reports that it is done. The executors just get jobs submitted to them, they don’t care. And the many go loops just wait for the previous work to be done before adding the next elements in
I’ve been using multimethods for a while, but I’m trying dispatch functions that return a vector. I’m having issues getting [:default :default]
to work, and combinations of :default
as one of the terms. Calling the multimethod doesn’t seem to find a match other than a simple :default
result, no vector at all. Any tips or tricks?
Hmm. Can I achieve that result, without the ‘implement this manually’ solution in the docs?
it depends, people often forget that multimethods dispatch using isa? not =, which means you can do stuff with dispatch hierarchies
I’ve done very little with the hierarchies, but I had my heart set on a simple decision matrix with a vector of 2 elements…
Which just led me to a better, simpler way. 👍:skin-tone-2:
Looks like https://github.com/camsaul/methodical#advanced-customization can do what you originally wanted.
:thinking_face:
I’ll check it out
Hi, I'm using docker to deploy a few applications. To use layers as efficiently as possible, I need to make reproducible builds. By this, I mean having the same jar archive at the end if no modification. Right now, I can't because zip archives include timestamp related information. Is there an option somewhere to get around this? I have seen a software to strip time related data but this seems like an adhoc solution…
> because zip archives include timestamp related information. it can mean many things, show the code or cmd line output what you mean
assuming you are aot compiling, there are a number of things that cause the compiler to generate slightly different bytecode every time it runs
I think the last thing I saw was the compiler has a map in it somewhere that uses object identity for keys, and then iterates over the map, so the order things come out of the map depends on System/identityHashCode, which is basically a pointer
there are people interested in this, I think also for things like nix packaging, but I am not sure if there is like a channel or something devoted to it
I see, at least with the last point, it's would be reproducible on my machine but I understand the issue here
System/identityHashCode is basically a pointer, so the order certain things are emitted in the bytecode will depend on where the memory allocator puts them
oh ok, if the address itself matters…
https://ask.clojure.org/index.php/12249/bytecode-not-100-deterministic-given-identical-inputs is the discussion of the hashcode issue
the thread I linked to above links to it, and apparently the person who opened that ask did some patching of clojure and claims to have reproducible builds
thank you, I'm digging into this!
while we are in the topic here is my trick for cache to not repeat downloading dependencies after each commit:
# environment + clojure dependencies
FROM env as env-with-clj-deps
COPY deps.edn .
RUN clojure -A:uberjar:tests-deps:check-syntax-and-reflections:run-tests -Stree
# full image ready to compile and tests
FROM env-with-clj-deps as deps-with-code
COPY . .
Good afternoon - I’m having a bit of a time doing a very simple Java -> Clojure call, based on [the example here](https://clojure.org/reference/java_interop#_calling_clojure_from_java) — it works if I use clojure.core/+
as in the example, but not if I use my own ns
and function.
Clojure code:
(ns example.oesa
(:gen-class))
(defn hello
"I don't do a whole lot."
[x]
(str "Hello, " x "!"))
Java (main) code:
package us.example.oesa;
import clojure.java.api.Clojure;
import clojure.lang.IFn;
/** Main class. */
public class Main {
/**
* Main entry point, prints `Hello World!`.
*
* @param args cli arguments (unused)
*/
public static void main(String[] args) {
IFn hello = Clojure.var("example.oesa", "hello");
System.out.println(hello.invoke("World"));
}
}
I started down this path using the Gradle + clojurephant
plugin, along with the built-in [Gradle Application Plugin](https://docs.gradle.org/current/userguide/application_plugin.html) - but I’ve also tried to eliminate that as a factor and just try to run java
directly from the CLI with the right -cp
, but I get the same error as when I use my trusty gradle run
java -cp build/classes/java/main:build/clojure/main:/Users/bruth/.gradle/caches/modules-2/files-2.1/org.clojure/clojure/1.11.1/2896bc72c90da8125026c0e61df0470a084f9ec3/clojure-1.11.1.jar:/Users/bruth/.gradle/caches/modules-2/files-2.1/org.clojure/spec.alpha/0.3.218/a7dad492f8d6cf657d82dcd6b31bda0899f1ac0e/spec.alpha-0.3.218.jar:/Users/bruth/.gradle/caches/modules-2/files-2.1/org.clojure/core.specs.alpha/0.2.62/a2a7ea21a695561924bc8506f3feb5d8c8f894d5/core.specs.alpha-0.2.62.jar us.example.oesa.Main
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'example.oesa/hello
at clojure.lang.Var$Unbound.throwArity(Var.java:45)
at clojure.lang.AFn.invoke(AFn.java:32)
at clojure.lang.Var.invoke(Var.java:384)
at us.example.oesa.Main.main(Main.java:16)
I feel like I’m missing something very basic 😢
the infra (ns, vars, classes) created by your example.oesa namespace need to be loaded (unless you're doing something like AOT compiling your clojure, which I'm going to assume you're not doing if you are new)
IFn REQUIRE = Clojure.var("clojure.core", "require");
REQUIRE.invoke(Clojure.read("example.oesa")); // this creates a clojure symbol from inside Java
ohhh … there is something in the clojurephant gradle docs that I added, related to AOT - but I just copy/pasted it, I’m not sure what it does or what else I need to do
I’ll try this, thank you!
that totally worked, I wish I had asked sooner
the snippet of java code I pasted is exactly equivalent to calling this from clojure:
(require 'example.oesa)
I guess I didn’t understand what this part in the linked docs actually meant, 😂 > Functions in clojure.core are automatically loaded. Other namespaces can be loaded via require
Am learning something I did not expect from as->
(-> {:a 1}
prn
with-out-str)
"{:a 1}\n"
vs.
(as-> {:a 1} x
(prn x)
(with-out-str x))
{:a 1}
""
the docs for as-> do say it binds the name to the result, not that it replaces the name
I am learning that yes
Years after using it
When unsure on what threading macros will do, I always call macroexpand-1. I rarely use as-> so I sometimes have to pause and think about what's going on
I paused and thought about what was going on
I've seen the swiss arrows library before, and I want to say as->
feels a lot like -<>
from swiss arrows, but this is just my best guess on where it could've come from 😄
rich had some personal project somwhere that he ended up doing a lot of repeated let bindings in (let [a (f) a (g a) a (h a)] (i a))
I played with swiss arrows right around 1.7 when I started with Clojure, and noticed as-> a bit later.
so if you read it as a let binding like that, what it does is pretty clear (and in fact that is how it macroexpands)
I use as->
infrequently when most of the forms need ->>
but there is an outlier or two
It's also worth mentioning that as->
is designed to be used inside a ->
pipeline:
(-> expression
(f)
(as-> x (g 1 x 2))
(h))

Is there a known and perhaps well-discussed rationale for not having a corresponding as->>
macro?
What would that even mean @U0E2268BY? as->
uses a named symbol -- it's not "first" or "last" in anything.
Well, if as-> is only for use inside -> (I don't agree with that) then as->> would only be for use in ->>, would take the initial value last
Exactly
I didn't say "only for use inside" -- I only needed that part of its design is for that use case and that's why the arguments are expr
, sym
in that order.
The swiss-arrows -<>>
isn't exactly what I had in mind, but is very promising for handling the situation I find myself in occasionally.. contrived example:
(let [h [:a :b :c :d :e :f :g]]
(-<>> [1 2 3 4 5 6 7]
(map inc)
(zipmap h)
(select-keys <> [:a :b :c])
prn
with-out-str))
"{:a 2, :b 3, :c 4}\n"
I'll just leave this here: https://stuartsierra.com/2018/07/06/threading-with-style 🙂
And https://stuartsierra.com/2018/07/15/clojure-donts-thread-as which specifically talks about "Don’t use as->
by itself"
Oh not that cringe-worthy article again 😁
Stuart was a policeman in a former life
Sometimes there doesn't need to be a rule