Fork me on GitHub
#clojure
<
2024-01-03
>
Bingen Galartza Iparragirre09:01:15

Hello! Is it possible to skip the caching of certain values when using clojure.core.cache.wrapped/lookup-or-miss ? Specifically I want to skip caching when value-fn is not able to compute the value. I don't want to cache failures. I was thinking on throwing an exception, but I don't know if it's idiomatic.

p-himik09:01:45

What does "not able to compute the value" mean, if not throwing?

Bingen Galartza Iparragirre10:01:47

In my case value-fn is not a pure function. It depends on some external calls. So not getting the right answer from those systems for example

p-himik10:01:59

I meant, how would anything external to value-fn determine that it has failed? Throwing an exception has the failure semantics built in, and judging by the code, doing so should be fine. Returning some sort of status is another approach, but then you have to make the caching functionality be aware of it. Seems like one way (the only way?) to do it is to make a cache wrapper with defcache that delegates all functions to the wrapped instance but has an extra check in miss.

p-himik10:01:31

I'd just throw.

Bingen Galartza Iparragirre10:01:32

Ok, I wil throw then. I guess is the simplest approach. Thanks!

Jakub Šťastný11:01:53

Hey guys. I have troubles with tools.build. When I run the uberjar with java -jar, I get:

Could not find or load main class secret_sauce.server.main
Caused by: java.lang.ClassNotFoundException: secret_sauce.server.main
All the details are at https://gist.github.com/jakub-stastny/103ed2fc5f3f021312bb7c5e474dd612 Thank you 🙏

delaguardo11:01:42

try this :main 'secret-sauce.server in args for uber

delaguardo11:01:41

it should point to the namespace where you define -main function

Jakub Šťastný11:01:20

That did it. Thank you!

Niki14:01:17

Probably a better fit in #C03S1KBA2

👎 2
👍 2
💯 1
thheller15:01:13

what I have done many times when I want to avoid many bindings is (loop [{:keys [x y z] :as state} init-state] ... (-> state (update :x inc) (recur))) and the like. i.e. use a map

☝️ 3
1
👍 1
👎 1
delaguardo15:01:21

what it suppose to return? [10 10 10]?

Niki16:01:10

@U05224H0W nice, but feels more busy visually, harder to see what’s going on

Niki16:01:18

Plus the performance overhead

delaguardo16:01:10

maybe instead of a vector for recur you can use some sort of placeholder as "unchanged value" something like (recur _ (inc y) 0) or (recur _ (inc y)) but at the end this is hardly different from (recur x (inc y) 0)

isak16:01:32

I love the idea, it would have been helpful for me many times

1
delaguardo16:01:02

@U08JKUHA9 do you have some example in mind?

isak16:01:32

Well any time you have more than 2-3 variables in the loop @U04V4KLKC

isak16:01:32

I don't agree with just using a map. A lot of the time when loop is used, performance is important, so you wouldn't want to just leave speed on the table.

delaguardo16:01:33

if there are more than 2-3 variables it is just a question of typing a bit more, filling recur forms with variable names. However, I can see one specific usecase where loop+ form might help — refactoring by adding one more variable into the loop bindings. In case all recur forms know loop context they do not require any changes.

👍 1
isak16:01:53

I agree with the second part there, though not with the premise that 'it is just a bit more typing' is a good reason to not add it. That is the case with so many features that are already in Clojure that you probably think are worth it.

delaguardo17:01:20

I simply can't justify implicit scope in that case. To me it is very similar to implicit function arguments:

(def y 2)

(defn foo [x y]
  (+ x y))

(foo 1)
;; => 3
this is also some sort of shorten form of
(defn foo
  ([x] (foo x 3))
  ([x y] (+ x y)))

isak17:01:23

Hmm I don't see the connection there.

isak17:01:20

The scope of the variables is the first vector to loop+, as before

delaguardo17:01:38

loop and its binding vector can be seen as an anonymous function declaration and recurs in its body "just" calling that function with potentially different arguments. I know that in reality it is a bit more complex than that. but this mnemonic doesn't let me down yet) now let's consider loop+ where recur can be "called" with less arguments than required by loop bindings. that means those calls evaluate in the scope of arguments passed to the previous call to "the same function". this scope is implicit and most likely hard to manage. this is what I tried to explain by my last example where I consider a single iteration with scope coming from the namespace.

Niki17:01:24

The use-case of “refactoring loop by adding one more member and now I have to change all recurs” is exactly what I had in mind (and in practice)

Niki17:01:57

I also kind of like that arguments to recur now have names, so it’s easier to see what is bound to what

Niki17:01:24

@U04V4KLKC you can think of it as a let expression that is evaluated before going into the loop

Niki17:01:44

This is where I used it yesterday

delaguardo17:01:51

this is exactly as I was thinking about it 😜

delaguardo17:01:06

btw, refactoring use cases are worth reporting to clojure-lsp folks

Niki17:01:34

Half of recurs here only change 3 out of 5 arguments, so saved me some space

😎 2
Niki17:01:51

It has the same problem: if I add one more loop argument I’ll have to change all recurs. Also, positional binding becomes unmanageable really quick (4+ arguments are already too much)

delaguardo17:01:40

in this form you can omit rest unchanged args (recur x) instead of (recur x _)

oyakushev16:01:13

I implemented exactly this in one project, but that was the only time I needed it.

Noah Bogart15:01:11

i wish we had access to local macros. i know there's tools.macro, but it would be nice to have them built in. https://github.com/clojure/clojure/commit/00f9c7b50c1b17af85fa58d1d8ac4f4027e88177

dpsutton16:01:24

macrolet has some issues though

(letfn [(foo [x] (inc x))]
  (clojure.tools.macro/macrolet [(m [x] `(foo ~x))]
    (m 2)))
Syntax error compiling at (REPL:196:3).
No such var: user/foo

Noah Bogart16:01:08

that implementation has issues, yeah. if it was built in, those issues would be possible to avoid

Noah Bogart16:01:15

objective: i want a macro that closes over local variables and isn't available outside of the current scope. Same goals as writing (let [foo (fn ...)] (foo ...))

Noah Bogart16:01:49

Not every function needs to be a generally available function with a name. "Anonymous" functions are useful. Macros are similarly useful, and letting them be locally scoped would make it easier to write one-off macros without polluting the current namespace

dpsutton16:01:27

I agree with that. I like really expressive macros in tests and I don’t want to invite others to use it. It’s a local that makes the code (almost always tests) more expressive. But it’s not fully general and no other namespaces should use it

👍 2
Alex Miller (Clojure team)16:01:26

so another available solution is a private macro that takes args for your local vars, right?

Noah Bogart16:01:21

there are many solutions, and that is one i've used before. it's more cumbersome than a locally defined macro and requiring a "unique" name leads to not using a macro at all, more often than not

dpsutton16:01:48

yes i suppose so. And there is probably some aesthetics to my preference.

Noah Bogart16:01:17

heh I'm reminded of Guido Van Rossum's comments (poorly remembered) about lambdas in Python: "if you need more than one line, define a function"

Alex Miller (Clojure team)16:01:32

feel free to post on ask clojure (but please try to highlight the problem, rather than "local macros", which is one possible solution)

Noah Bogart16:01:55

i'm just chatting lol, i know to ask for anything specific on ask

Noah Bogart16:01:49

clojure's design philosophy is "macros as tool of last resort", and not including local macros or symbol macros aligns with that pretty firmly

Noah Bogart16:01:47

this came from wondering if could i could just define a let-bound macro and see if it would work. then when it didn't, i looked at the code to see why isMacro wasn't finding the metadata. the linked line above explains it