clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
alexmiller 2021-03-05T00:41:41.108700Z

I think you mentioned that at https://ask.clojure.org/index.php/9775/better-stacktraces-for-protocol-methods?show=9775#q9775

Max 2021-03-05T04:39:08.109900Z

Out of curiosity, what’s the technical reason why recur is disallowed in catch and finally? I just came across this for the first time today and my random googling hasn’t provided a great answer so far

Max 2021-03-05T20:59:32.131100Z

So what I’m hearing is that we’ll have a good solution when project loom comes out 👀 :troll:

phronmophobic 2021-03-05T04:48:22.110Z

potentially due to GC? https://clojure.atlassian.net/browse/CLJ-31

Max 2021-03-05T04:57:00.110200Z

Yeah, I found that issue in my googling. I don’t really understand it though, and strangely enough the example doesn’t even have a try in it

phronmophobic 2021-03-05T04:59:07.110400Z

from the info, I'm guessing that recuring across either binding or try was causing some objects to not be garbage collected so both of those were disallowed

2021-03-05T05:04:44.110600Z

Logically try pushes an exception handler onto a stack(popped after the try completes), so recurring across it grows that stack

2021-03-05T05:06:35.110800Z

Clj-31 was I think my first clojure patch, and it didn't get the analysis correct so it would complain if put you put a loop in a finally until someone else fixed it

1😁
emccue 2021-03-05T05:14:45.111100Z

curious if that is a hard restriction or not

1➕
emccue 2021-03-05T05:15:33.111300Z

like, in my head i feel like there is a way to rewrite the try to pop off that exception before recurring

emccue 2021-03-05T05:18:51.111600Z

like if this

(loop [x 10]
  (if (= x 0)
    "done"
    (recur (dec x))))
got translated to this
Object x = 10;
Object out;
while (true) {
  if (x == 0) {
    out = "done";
    break;
  }
  else {
    x = x - 1;
    continue;
  }
}

2021-03-05T05:18:56.111800Z

but does that make sense?

emccue 2021-03-05T05:19:15.112Z

(don't know yet, i'm noodling)

2021-03-05T05:19:25.112200Z

(it doesn't)

2021-03-05T05:19:45.112400Z

if recur is the supposed to act like a tail call in a language with tco

emccue 2021-03-05T05:21:39.112600Z

(loop [x "abc10"]
  (try (Integer/parseInt x)
    (catch NumberFormatException e
      (recur (.substring x 1 (count x))))))

2021-03-05T05:22:13.112800Z

then given tco and a fn like (fn f [x] (if x (throw (Exception. "foo")) (try (f (not x)) (catch Throwable t 1)))) , is the self call to f a tail call or not, and how does that square with the exception handling

2021-03-05T05:22:44.113Z

a catch technically could be a tail, if there is no finally

emccue 2021-03-05T05:24:13.113200Z

Object x = "abc10";
Object out;
while (true) {
  try {
    out = Integer.parseInt((String) x);
    break;
  }
  catch (NumberFormatException e) {
    x = x.substring(x, x.length());
    continue;
  }
}

emccue 2021-03-05T05:26:22.113400Z

hmm yeah...

emccue 2021-03-05T05:26:44.113600Z

unless you put the "finally stack" on the heap

2021-03-05T05:27:01.113800Z

I am not saying it isn't possible, because once you compile things down to jvm bytecode, the way exception handlers work is they are in effect for a range of instructions, say instructions 5-10 in a method

2021-03-05T05:27:12.114Z

and you can you know, jump around

2021-03-05T05:27:34.114200Z

but logically, in the expression based high level language we actually program in it doesn't make sense

2021-03-05T05:28:13.114400Z

(finallys don't actually exist at the bytecode level, the java compiler and the clojure compiler just simulate them using exception handlers)

2021-03-05T05:28:57.114600Z

recur is not a goto jump, it is intended work like tco would if we had general tco

emccue 2021-03-05T05:29:22.114800Z

I have a dusty book on jvm bytecode that I should maybe get around too i guess

2021-03-05T05:29:58.115Z

moving where the stack is kept in memory doesn't change that fact that the stack is growing

emccue 2021-03-05T05:30:11.115200Z

yeah

2021-03-05T05:35:16.115400Z

the issue isn't really unique to exception handlers, it is an issue of mixing dynaimc extents with jumps, like if you were writing asm, pushed an argument on to the stack, had several more instructions, and then popped it, but one of the intervening instructions jumped back to somewhere before the first push

2021-03-05T05:36:58.115600Z

and you can see in this masters thesis somewhat wrote on adding tail calls natively to the jvm, in his definition of what constitutes a tail call, if has an exception handler around it, or a synchronized block, it isn't a tail call https://ssw.jku.at/Research/Papers/Schwaighofer09Master/schwaighofer09master.pdf

2021-03-05T05:37:36.115800Z

(a sychronized block is a try/finally in disguise)

2021-03-05T05:40:33.116Z

I also rewrote the exception handling code for core.async's go macro a while back, and screwed that up the first time too (I forget, I didn't balance pushes/pops correctly so finally didn't work right or something)

phronmophobic 2021-03-05T05:41:04.116200Z

I always find this stuff interesting even if I probably don't understand everything.

2021-03-05T06:43:28.116500Z

The thing about core.async's go macro is it is basically a continuation passing transform(the way it is written it talks about basic blocks and whatever, but as a knight of the eastern calculus I say ni to that)

2021-03-05T06:44:32.116700Z

cps style makes tail calls extremely clear, a tail call is when a function calls another function with the same continuation

2021-03-05T07:08:29.117Z

And the way you model exceptions in cps is often as a "double barreled" continuation, basically two continuations, one for the normal case and one for exceptions

2021-03-05T07:10:06.117200Z

And for a function call inside a try/catch the exception continuation is the new catch, not the same continuation as outside of the try, so it can't be a tail call

2021-03-05T07:31:27.117400Z

http://matt.might.net/articles/oo-cesk/ matt might has a lot of great posts covering this kind of thing

raspasov 2021-03-05T08:22:19.117800Z

@gr.evocatus that’s a better one but unfortunately the real problem with Python and all other languages without value semantics is that there’s no way to know (without walking the whole data structure) if the world has changed underneath that tag/name;

borkdude 2021-03-05T13:44:40.119300Z

Why do (/ 1 0.0) and (apply / [1 0.0]) give different results in Clojure? Feel free to follow up here: https://github.com/babashka/babashka/issues/747

borkdude 2021-03-05T13:50:20.120Z

I guess the logic is in clojure.lang.Numbers somewhere

cassiel 2021-03-05T13:53:09.120400Z

Additional data point: user=> (let [f /] (f 1 0.0)) Execution error (ArithmeticException) at user/eval140 (REPL:1). Divide by zero

borkdude 2021-03-05T13:53:37.120700Z

Yeah, it's definitely related to inlining

nooga 2021-03-05T14:02:01.122Z

hm, I’m wondering if I want to emulate this in my interpreter because it handles both cases the same way as (/ 1 0.0) at the moment

borkdude 2021-03-05T14:03:31.122900Z

Maybe a case of Hyrum's law

borkdude 2021-03-05T14:03:56.123400Z

As Alex suggested in the above linked issue: it might be because of type info available as either Long or Object

2021-03-05T17:14:16.126300Z

when dealing with credentials for an api, is it a good idea/common practice to make the credentials a type rather than a map just to prevent accidental printing(/repl logging) of sensitive data? Do you typically put all credential data together in a type like this, even though only a subset of the credential data will actually be sensitive (such as username+password)? Are there any other common guidelines for dealing with credentials in clojure?

vemv 2021-03-05T17:33:54.126500Z

> when dealing with credentials for an api, is it a good idea/common practice to make the credentials a type rather than a map just to prevent accidental printing(/repl logging) of sensitive data? Might be, anyway I'd make sure to explicitly override print-method for the given defrecord so that one is not relying on a random behavior An alternative approach is walking the hashmaps and removing known-bad or suspected-sensitive keys (e.g. :password). Of course you'd have to remember to invoke this, unless abstracting it away via e.g. the logging setup

1👍
vlaaad 2021-03-05T19:26:44.127Z

see also: https://github.com/vlaaad/blanket

1👍
vlaaad 2021-03-05T19:28:04.127400Z

maybe you don't need the library, but just look at the concept — you don't even need a custom defrecord, just :type metadata

markaddleman 2021-03-05T19:47:10.128900Z

I just noticed a possible bug in the FnCache implementation from clojure.core.cache. Instead of creating a BasicCache, shouldn't miss, evict and seed create an FnCache? see https://github.com/clojure/core.cache/blob/ee699021b984df182359648312042b79d05cc506/src/main/clojure/clojure/core/cache.clj#L148

2021-03-09T14:43:22.351700Z

Yep, @seancorfield is correct in that FnCache still needs sematics. I have some notes in a notebooks somewhere about this but haven't moved them to a ticket. That said, a FnCache should come out of miss/evict/seed

markaddleman 2021-03-05T19:48:18.129100Z

@seancorfield if you agree, I'll open a jira

seancorfield 2021-03-05T20:02:03.129400Z

FnCache has never been implemented -- there's an open Jira about what its semantics are meant to be.

markaddleman 2021-03-05T20:04:19.129600Z

ah ok

seancorfield 2021-03-05T20:05:36.129800Z

There are a few open Jiras about incomplete semantics in the original lib as I inherited it...

seancorfield 2021-03-05T20:06:10.130100Z

Maybe @fogus can speak to FnCache?

Max 2021-03-05T20:59:32.131100Z

So what I’m hearing is that we’ll have a good solution when project loom comes out 👀 :troll: