Fork me on GitHub

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


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


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


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


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

😁 3

curious if that is a hard restriction or not


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


like if this

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


but does that make sense?


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


(it doesn't)


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


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


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


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


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


hmm yeah...


unless you put the "finally stack" on the heap


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


and you can you know, jump around


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


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


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


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


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


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


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


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


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)


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


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)


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


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


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

hiredman07:03:27 matt might has a lot of great posts covering this kind of thing


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


Why do (/ 1 0.0) and (apply / [1 0.0]) give different results in Clojure? Feel free to follow up here:


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


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


Yeah, it's definitely related to inlining


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


Maybe a case of Hyrum's law


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


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?


> 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

👍 3

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


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


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


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


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


Maybe @U050WRF8X can speak to FnCache?

fogus (Clojure Team)14:03:22

Yep, @U04V70XH6 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