Fork me on GitHub

Is there a way to somehow iterate over lexical bindings inside body of a function or a let form (that should happen in the context of macro expansion of course)?


I'm pretty sure I have seen some Clojure debugger-type library thingy that might be implemented by doing that, or something similar. Let's see if I can remember which one it is...


I haven't tried this, but the code that includes a definition of 'local-bindings' in this StackOverflow Q&A might be relevant?


That code looks to me to be specific to Clojure/JVM, and wouldn't work for ClojureScript, because it is reaching into internals of Clojure/JVM compiler specific to it.


@andy.fingerhut Thanks, this is exactly what I was looking for.


Google search for clojure.lang.compiler/LOCAL_ENV turns up other places it has been used for similar purposes, too, in case you want other examples.


i was playing with alex and george's debug repl recently - fwiw, the latest version doesn't use compiler internals. still i didn't find it to work as-is for cljs -- though it seems likely it would work for clojure clr. i believe it uses &env now:


fwiw. the speculation bit there seems fairly in line with what i saw combing through past clojure irc logs 🙂


@sogaiu thanks, I just learned about &env


yes &env I think is what you're looking for


unfortunately, using &env breaks using macroexpand to test 😕


What makes you think that? It isn't mentioned on


I should have been more clear: :locals is never populated in &env when using macroexpand, so if your macro depends on it, you wont’ be able to test it that way


Ah, that's fair


Most of the time it should be okay though


yeah, edited comment. didn’t mean to say one shouldn’t use it


more just complaining on my end that I can’t use macroexpand to test my macro I’m working on <_<

Jakub Holý (HolyJak)13:11:54

Hello folks! When I run my function in the REPL I get > Execution error (StackOverflowError) at (REPL:1). but the stack trace contains nothing of my code, only many clojure.core$concat, $seq and clojure.lang.LazySeq. How do I troubleshoot this? How do I find out where in my code am I creating this??? The stack trace:

[[clojure.lang.LazySeq sval "" 42]
 [clojure.lang.LazySeq seq "" 51]
 [clojure.lang.RT seq "" 535]
 [clojure.core$seq__5402 invokeStatic "core.clj" 137]
 [clojure.core$concat$fn__5493 invoke "core.clj" 725]
 [clojure.lang.LazySeq sval "" 42]
 ... (these few repeated many times over)
 [clojure.lang.LazySeq seq "" 51]
 [clojure.lang.RT seq "" 535]
 [clojure.core$seq__5402 invokeStatic "core.clj" 137]])


can you show the code or the error message at least ? 😄


only having the stack trace is very little to go on


ah, now i get it, the stackoverflow is your error message ... any chance you built endless recursion there ?

dpsutton13:11:17 this might give you some insight. if you built up a sequence using concats, you end up with too many lazy thunks to realize the sequence later and run out of stack

Jakub Holý (HolyJak)14:11:17

Thank. I am pretty sure I do not build a sequence manually anywhere but I do operate on data using lazy sequence operations. I cannot really share the code as there is few hundred lines of code involved. I just call my (fetch-and-process-all-invoices). The error message is there ☝️ and it is useless: Execution error (StackOverflowError) at (REPL:1).

Jakub Holý (HolyJak)14:11:36

The only hypothesis is that the error actually happens after my function run, when REPL tries to display its output, that could explain why there is only clojure.* in the stacktrace.

Alex Miller (Clojure team)14:11:28

if that was true, you would see a different error message (not Execution error)

Alex Miller (Clojure team)14:11:44

Execution error indicates it happening during the E of REPL

Jakub Holý (HolyJak)14:11:56

You are right. But why doesn't the stack trace have anything of my code? How do I find where is this happening?

Alex Miller (Clojure team)14:11:15

do you use concat anywhere?

Jakub Holý (HolyJak)14:11:08

Yes, at a few places, I will look into that / alternatives. (And reread the article!) Ah, I guess I am hitting this > and the accumulated stack frames of seq prevent us from seeing where the error originated

Jakub Holý (HolyJak)14:11:59

Hm, looking through the code I do not see any suspicious use of concat with multiple arguments, mostly just (merge-with concat ...) etc. I have to re-check...

Alex Miller (Clojure team)15:11:10

are you doing that in a loop / recursion?

Jakub Holý (HolyJak)18:11:16

Thank you, no, I am not. I now deployed some (I am close to 100% sure unrelated) changes and the problem went away it seems. :hand_with_index_and_middle_fingers_crossed: it was just a fluke...

Jakub Holý (HolyJak)18:11:16

I know . I have no idea what's happening. So everything is as normally:) Thank you for your help! I'll see if it comes back..


why clojure doesn’t have a list literal or a symbol literal? > Clojure has no literal representation for Lists. If you write a List out in your program, it is interpreted as code. At first glance, it appears that quoting the List is a good solution. However, if you quote a List, the elements of the List won’t be evaluated. That means that a quoted List is only useful for Lists of literals. Clojure is data oriented, I was wondering why there is no literal for a symbol or a list of data. One supposition would be homoiconicity could not be achieved, but I could not explain why.

#inst "1985-04-12"
=> #inst"1985-04-12T00:00:00.000-00:00"
=> #{:a}
[1 2 3]
=> [1 2 3]
{:a 1}
=> {:a 1}
'(1 2 3)
=> (1 2 3)
(1 2 3) 
=> error


I don't understand the question. '(a b c) is a list literal containing 3 symbol literals


It needs quoting to avoid being evaluated in a REPL, unlike a map or set literal, that is true, because code is inside of lists, too.


Are you asking why it needs quoting at the REPL to avoid evaluation, and maps and sets do not?


Maybe a little more -- all of those expressions are being read, then evaluated, and the result of evaluation printed, at the REPL.


maps and sets are read, and during evaluation, they evaluate to themselves, as do keywords, numbers, and #inst values. All subexpressions of the map and set literals are also evaluated, recursively, too, so if a map or set literal contained a parenthesized expression, it would be evaluated, and present the same difference in behavior as if the parenthesized expression were typed as the whole expression.


If lists also always evaluated to themselves, like maps and sets did, then they could not be used to contains code for evaluation -- you would need some other syntax for that? (not sure if that makes sense)


yes that make sense! Seems like it would be handy* to have another syntax for that


but I was wondering if it could be possible


Maybe this is obvious but I didn't see it mentioned: you can use (list ...)

(list 1 2 (+ 1 2))
=> (1 2 3)


With (list ...) the values inside the list are not quoted, like they are when you quote the list itself:

'(1 2 (+ 1 2))
=> (1 2 (+ 1 2))


If you're familiar with other Lisps, you can also use the more traditional cons to build lists in Clojure:

(cons 1 (cons 2 (cons (+ 1 2) nil)))
=> (1 2 3)


I should be a little more precise, even without the quote in front (a b c) is a list literal containing 3 symbol literals. If you only do read on that, no eval, you get a list.


It is the eval that causes an error if any of a b or c are not defined with values, or if the value of a is not something that can be called as a function or macro.


user=> (read-string "(a b c)")
(a b c)
user=> (eval (read-string "(a b c)"))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: a in this context


As opposed to this, where the set evaluates to itself, and so do 1, 2, and 3:

user=> (read-string "#{1 2 3}")
#{1 3 2}
user=> (eval (read-string "#{1 2 3}"))
#{1 3 2}


In the next example, reading returns a set containing 3 symbols. Without eval, no problem, you get back such a set. Eval of the set returns a set, but also recursively calls eval on all elements, which is when it finds that a is undefined:

user=> (read-string "#{a b c}")
#{a c b}
user=> (eval (read-string "#{a b c}"))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: a in this context


Reading a list or set or map are all pretty consistent behavior, and the syntax for each are all literal syntax for those data structures. The rules for eval behavior on a list are different than the rules for eval behavior on a set or map -- the first element of the list is special.

thanks2 4

I am stuck trying to proxy an It has two different write methods, one overloaded with a byte array and one overloaded with an integer. I seem to type hint things correctly to tell the compiler how to implement the write methods.


My solution right now is to write a java class that derives from output stream and names those methods differently as abstract methods.


@chris441 you may have to test types inside the method impl


That would lead to an abstract method error as you will have only overridden one of the two methods.


Most likely the byte[] one.


that isn't true


(def x
 (proxy [] []
    ([o] (println (class o)))
    ([o s len]))))
user=> (.write ^ x 2)
user=> (.write ^ x ^bytes (byte-array 0))


I've proxy'ed outputstreams a few times, and it is always the same, type check in the write method


huh. Well, you are overriding the object method (byte[]) one I think. I wonder how that is working or if just no one calls the int method.


like, ghadi literal posted a transcript of a repl session showing you it working for both the int write method and the byte[] write method


stop saying it doesn't work


We don't know what the reflected method called.


1. it isn't reflected


2. even if it was, it prints out the type so we know


So then when you proxied did it override all methods with one signature and call your code for both?


in the output you can see that it's calling the code I provided in both cases of input


Yes for sure, agreed.


clojure's proxy is strictly name based


I think so -- overrides all by the same name


e.g. you give it a function body (which may have multiple arities) and all methods with that name will call that function


@lukaszkorecki Any chance you have some sample code? This stuff is confusing


@josh_tackett sure, let me find a version I can share


also, this works only with java 8 - because it relies on javax.xml.bind DatatypeConverter


I'm going to try to extend buddy-core to handle :aes256-cbc-hmac-sha256


so hex-pair-to-byte would have to be tweaked for java 11 I think


got it, i will circle back to this if I can't extend buddy-core


I don't think I ever used it - usually BouncyCastle does everything I needed, including PGP and the API is pretty straightforward, what's confusing is the overall cryptography stuff 😉


beware -- neither BouncyCastle nor buddy are "misuse resistant" libraries like libsodium or google's tink


pgp tends to be less straight forward once you get to multiple objects, subkeys etc. 🙂 at least if you do it with bouncy castle, there are lots of pitfalls


well it's also the fact that it's not just crypto


it's about containers and objects inside of them


way more than "this is just encrypted data" 🙂


and a big part of that in my mind is just overengineering at it's finest


then again ... it does the job ... so i don't complain 🙂


Luckily the PGP stuff I was working on was pretty light weight and in a controlled environment, implementing production-grade, customer facing features is indeed a task that needs a lot of attention and verification.


@lukaszkorecki I'm getting this error: java.lang.ClassNotFoundException: javax.crypto


@lukaszkorecki actually it can't find any of the libs I'm trying to import haha


because you aren't using the his ns verbatim, you are trying to turn it into an (import ...) call in the repl, but failed to quote the imported classes


apparently recent enough clojure versions don't require the quoting, which is wild


what a world

💯 4

(:import (javax.crypto Cipher SecretKeyFactory) (javax.crypto.spec SecretKeySpec IvParameterSpec PBEKeySpec) (javax.xml.bind DatatypeConverter) (org.bouncycastle.jce.provider BouncyCastleProvider) ( Security)))


so he does it with an import


but that isn't import


that is :import


:import is a directive to the ns macro, import is a macro you can call at the repl


(ns skyscraper.scrape.encryption
  (:import javax.crypto Cipher KeyGenerator SecretKey
           javax.crypto.spec SecretKeySpec
           org.apache.commons.codec.binary Base64))

(defn bytes [s]
  (.getBytes s "UTF-8"))

(defn base64 [b]
  (Base64/encodeBase64String b))

(defn debase64 [s]
  (Base64/decodeBase64 (bytes s)))

(defn get-raw-key [seed]
  (let [keygen (KeyGenerator/getInstance "AES")
        sr (SecureRandom/getInstance "SHA1PRNG")]
    (.setSeed sr (bytes seed))
    (.init keygen 128 sr)
    (.. keygen generateKey getEncoded)))

(defn get-cipher [mode seed]
  (let [key-spec (SecretKeySpec. (get-raw-key seed) "AES")
        cipher (Cipher/getInstance "AES")]
    (.init cipher mode key-spec)

(defn encrypt [text key]
  (let [bytes (bytes text)
        cipher (get-cipher Cipher/ENCRYPT_MODE key)]
    (base64 (.doFinal cipher bytes))))

(defn decrypt [text key]
  (let [cipher (get-cipher Cipher/DECRYPT_MODE key)]
    (String. (.doFinal cipher (debase64 text)))))


ya I'm using :import


that :import is incorrect


ok, got it. how do I fix it?




you removed all the grouping of classes in packages


(ns skyscraper.scrape.encryption
  (:import (javax.crypto Cipher KeyGenerator SecretKey)
           (javax.crypto.spec SecretKeySpec)
           ( SecureRandom)
           (org.apache.commons.codec.binary Base64)))


👍 thank you


you don't need apache commons for base64, java has a built in class for it


is it a bad idea to be using vectors like so?

(ns skyscraper.scrape.encryption
  (:import [javax.crypto Cipher KeyGenerator SecretKey]
           [javax.crypto.spec SecretKeySpec]
           [ SecureRandom]
           [org.apache.commons.codec.binary Base64]))
i see vectors used in clojure.instant, but lists elsewhere


it's fine, but it's just not common style


I think cursive defaults to [] for :require and () for :import so that's what I use

Alex Miller (Clojure team)21:11:22

I think both are common in practice


thank you both 🙂

Alex Miller (Clojure team)21:11:26

I disagree only with using () for imports :)


thanks again!

Alex Miller (Clojure team)21:11:39

and fyi, Rich thought Stuart's reasoning for lists in that article was bogus :)

👀 8
vemv21:11:02 implements that article its low star count doesn't do justice to how well it works


interesting -- may be clj-kondo does some of this already?


kondo lints, how-to ns formats (and lints; but personally I don't use the linting part)


ah i see -- the description at the top just says linter 🙂

👍 4

I’ve taken to categorising requires where they grow very large (which I bet will make some people frown). I.e., you’ll see stuff like:

;; Mutations
[ :as search-mut]

;; Components
[ :refer [app]]
[poc.components.token :refer [Token]]

;; Fulcro
[com.fulcrologic.fulcro.mutations :as mutations :refer [defmutation]]
[com.fulcrologic.fulcro.algorithms.merge :as merge]


…insde the require clause.


oh, i hadn't seen that sort of arrangement before -- thanks for sharing. by coincidence i happen to be doing things alphabetically like the stuart sierra article says, iiuc.


I’ve mostly gone alphabetically, but started getting some namespaces with quite a lot of dependencies due to breaking things up in fairly small pieces, and then it became more useful to organize them primarily by purpose.


that sounds reasonable -- haven't worked on anything that big 🙂


Though I still tend to stick standard lib stuff at the top prior to everything else. But when I’m pulling in a bunch of UI components, it seems to make more sense to group them than to sprinkle them around routing, clojure.string, third party libs, and so on.


that seems like it would be easier to maintain


My preference is whatever my editor uses when formatting my code. 🙂 I think clj-refactor.el uses vectors.


i use vectors to keep it the same as require


i also group my requireds by their "level", clojure language stuff at the top and tiny-scope app namespaces at the bottom


@jumar a compatible alternative to jasypt in clojure:


Nice, thanks!