Fork me on GitHub

I'm getting a bizarre error in 1.8 "UnsupportedOperationException nth not supported on this type:" but no type is actually printed, and the source lines in the stack trace don't make a whole lot of sense with respect to this error. Anyone seen this?


are you writing a macro?


i've seen it when i get my syntax wrong in a macro that i'm writing


I found the problem. A call to nth on a Java set. I understand why nth doesn't work on this, but what I don't understand is why the Clojure error failed to show the type, and why the stack trace pointed at the wrong line of code.


Minimal example: (nth (.keySet {1 2}) 0)


The type is printable: `=> (type (.keySet {1 2})) clojure.lang.APersistentMap$5`


So it should be able to print this in the error message.


So, seems like a bug in the error-throwing code associated with nth.

Alex Miller (Clojure team)04:08:04

nth spec would catch this before


puzzaler: if you look carefully, you'll see two exceptions being thrown in the repl, one is the nth one, the other is

CompilerException java.lang.RuntimeException: Unable to resolve symbol: ⁠ in this context, compiling:(NO_SOURCE_PATH:0:0) 


the reason you see a blank space in both exceptions where something should be is because the compiler calls getSimpleName on the class name, and for the anonymous inner class the simple name is ""


the second exception, which in my testing is the exception that actually bubbles out (the nth one has the error message printed and that is it), is the really weird one, because it is coming out of the compiler, not the runtime


what I am seeing is(with the clojure.main repl), for that exception, for some reason, the next time the repl passes through its loop "repl-read" is returning a symbol with the name "", which of course explains the unable to resolve symbol error message from the compiler


Hah, the second exception is just me chasing my own tail, because I copied and pasted the (nth (.keySet {1 2}) 0) from slack and it included trailing weird unicode non-breaking space characters


Okay, I'm producing hiccup-style structures from inataparse. I need help figuring out how to re-parse specific items within the structure.


Solo strings (unmatched with a tag) are one of the types I need to re- parse in place




meta-question: how do you judge the quality of Clojure books without buying them? B/c I've bought , was very dissatisfied with the purchase and somehow there are three 5-star reviews now (there were none when I bought it)


"dissatisfied" as in "wtf, how this can have "professional" in the title". like when they recommended to "slurp" a bytestream in Ring middleware without ever mentioning memory consumption concerns. or when they claimed that vector lookup is O(1)


haha @si14, vector lookup being O(1), you're going to have fun debating that one with the channel


Ok, I’ll be that guy… vector retrieval isn’t O(1)? Unless the book was ambiguous between lookup and search, since search is obviously O(n)


log32 is taken as effectively constant for lookup


@reitzensteinm vector is a tree, so it's O(log(n)). we can argue that the tree is shallow, but that log may show up anyway (e.g. as an abrupt change in access times when first "layer" is filled up)


I mean it's unprofessional to claim O(1) index lookup without mentioning the underlying assumptions for the claim (like log32 being "effectively" constant)


no arguments from me @si14. it's something that comes up often, and I don't personally think it's helpful to claim O(1)


But the purpose of using big-oh is for comparison of structures and algorithms.


i'm looking for the claim. I found this in chapter 7


> Clojure is data oriented. Choosing the right datastructure is the prerequisite for writing fast code. Clojure encourages usage of O(1) and O(log n) data structures by providing the map, the set, and the vector literal notation.


what is O(1) in that list though?


> Vectors have O(1) lookup by index. Sets and hashmaps have O(logn) lookup by key. Sequences by contrast are O(n). Choosing the right data structure usually boils down to not doing combinatorial sequential lookups O(n∧2) or higher. Exponential complexity is the primary performance assassin for algorithms.


ah here it is


i'm ok with that statement. It could have included information about 32-way tries but for an intro book its ok


but why not just say logn for everything?


the performance implications are subtle, and not really captured by the big O notation. so 8 branches deep (assuming balance), you're in the objects-that-take-terabytes-of-ram. so effectively, your cap is going to be 6-7


@credulous O(1) claim makes an impression that Clojure's persistence is a free lunch. It isn't, both in data locality and memory, so I would expect that the book that has the word "professional" in its title would mention this


if an algorithm were to slow itself down so it always took 8 branches, it would be considered O(1)


but it would be nice to discuss the unfriendlyness to cache of making those jumps around memory


like, if you're going to pull out the big O notation, you're going deep enough that you might as well just explain the lot


I don't have a problem with effectively constant though


just my 2 cents


@reitzensteinm yeah, right! I wish there is a Clojure book that really dives in, like from the basics (HAMT and stuff) to how you compose large systems in Clojure (do you even DDD?)


or maybe I just don't know where to look


i'm trying to make my way through the functional data structures book for my talk, it's a pretty harsh introduction though


the one on the clojure bookshelf


tangential: is there a guide or book anywhere on how to build "enterprise-y" Clojure software? It feels like everything we have in a public discourse is a mix of "maps flowing through the system", "Datomic is awesome" and "data is king". While I completely agree with all of it, it doesn't help much to structure the code around complex "business logic". Java world has buzzwords like CQRS and DDD and a ton of people writing about the stuff; it feels like Clojure has almost none


@si14 I would recommend 2 books to do that. The first is “Programming Clojure” by Stuart Halloway and Aaron Bedra. That takes you from the basics, but covers much more of them and much better than most other books. Then I recommend “Clojure Applied” by Ben Vandgrift and Alex Miller (Hi Alex!), to get you up to composing large systems


how would you execute a transaction that involves a few external services and a database? would you reuse Java's pattern of lifting a graph of objects from SQL DB and relying on some ORM to save it back, or should it be ad-hock selecting and updating? questions like this are hard to get right, or maybe I'm just too ignorant :(


@quoll thanks a lot, definitely will check them out!


@reitzensteinm may I ask where that talk is going to be presented? :)


You’ll also find that many of these design frameworks are applied to Java to simplify complex flows and so on. While Clojure is not a silver bullet, many of the things that those patterns are trying to help with in Java are much more tractable in Clojure. This either makes those things unnecessary, or much simplified. Try looking at Bobby Calderwood’s Clojure/conj talk from last year where he looks at CQRS with Clojure:


I am working on an experimental clojure->C compiler, that uses clojure.spec generators to specialize code


so you don't have to check is-it-a-float a trillion times at runtime


@quoll I see what you mean, thank you. I believe there is an inherent complexity in some things, like distributed transactions (someone clicks a button, perform a charge, record its result to DB, send an email, every step can fail at any point), so it's not Java quirkiness at fault but an underlying world's messiness. Java people came up with some solutions, sometimes I think it can be helpful to look at how they do stuff, but it's hard to disentangle Java from the underlying ideas


@reitzensteinm sounds extremely interesting! Hope I will be able to attend :)


@si14 there's a thingie I uploaded here


you can drag the slider to see the compiler optimizations being applied


and the eventual C output turning from a naive implementation of clojure's features that work at runtime, to eliding it all after specializing it


wow! "Drag slider to show optimizations being applied." is just plain awesome!


@si14 I haven't found a lot of books that are that great or up to date. But DDD and CQRS are not Java specific, so they should apply to Clojure as is. Obviously, you won't be able to benefit from the OO pattern they describe to help you implement them. The basic idea behind DDD is that you should model things as they are in your organization. The same boundary should exist. Clojure records work well for the model part, combined with protocols to implement the service layer. You can use namespaces as bounded contexts. An ATOM as your repository. And events can just be functions. There's many other way I guess you could model it. I'd recommend looking at ClojureScript actually, there's more examples of enterprise software in it, with details of the frontend and the backend.


Does anyone know how a macro behaves within a special-form?


I'm trying to figure out if I can make a macro called (catch-any) that would take a vector of exceptions and return multiple (catch) blocks from it. And if it would be possible to use it within the try special-form


didibus: if you want special catches you need to write a try macro, not a catch macro


@gfredericks I was thinking that's the case, but I'd like to understand why that is?


because (catch ...) isn't really its own special form, it is part of the syntax of the try special form's dsl


So its something with the transformation order. If the (catch-any) macro would expand first, wouldn't it be able to return a list with the first element being (catch) which would then be processed by (try) as if I had written that?


no because try isn't looking for a list with the first element that is a catch clause


a macro expands to a single form, it cannot expand to multiple forms and some how splice them in to the surrounding form


Hum, that part I don't understand. Shouldn't the macro just pre-process the AST into one form to another?


"to another" not "to several"


a macro takes and returns forms


(catch ...) is a form, and is specifically the kind of form the try special form is looking for as catch clauses


((catch ...) ...)) is another form, and is not the kind that the try special form is looking for


the "expand to one form" vs "expand to many" isn't even the main limitation though; somehow try is processing the forms below it before any macroexpansion gets done


Ok, I see, so a macro can't produce a list of forms, which means it wouldn't be able to return (catch...) (catch...) etc.


e.g., you can't even do this: (defmacro rescue [& args] (cons 'catch args))


Thanks, I hadn't think of the output one form vs many issue, but I'd like to understand about the processing order


a list of forms like X, is different from a bunch of forms like X embedded in a surrounding form


(try ... (catch ...) (catch ....)) is two catches in the surrounding list that starts with the symbol try


Why does try happens before macroexpanssion, say I just wanted to have a macro that expands to a single catch form


@didibus: that's how it was written; not sure if there's a good reason


(try ... ((catch ...) (catch ...))) is a list of catch forms in the list that starts with try


Do every special-form have their own execution order rules like that? Or do all special-form follow a pattern?


@didibus: most of them don't have this nested syntax aspect, so the question doesn't really apply


I guess that's true. What about macro inside macro? which macro expands first?


I suppose there are parallel things like "You can't write a macro that expands to an arglist and use it inside of defn"


its like if you had a macro that you wanted to expand in to a binding pair inside the bindings given to let


@didibus: the outer macro runs first, so gets the ultimate say in everything


I see, so maybe the same rule is applied to special-form


although you a macro can't expand its body, which is pretty annoying


every macro is basically its own dsl and can do whatever, and unless a lot of care and consideration is given, that whatever is unlikely to be arbitrarily pluggable


So macros only compose if the outer most macro designed specifically for it.


basically never


I suppose "a macro can't expand its args" means you couldn't even reasonably write your own defn that supported macros expanding to arglists


do any other lisps have that capability?


I could imagine another implicit argument to macros that is a macro expand function that the compiler passes in


Clojure is my first Lisp, but I remember reading that some Lisp can in fact compose macros


there are many many kinds of macro systems


I wonder how easy that would be to add to the clojure compiler


and they are sort of a sub domain of rewrite systems


very, would be my guess


I keep running into uses for it


it would break everyone elses macroexpanders


oh because it'd be a 3rd positional arg?


let's pass it in as metadata on the &form


a well-known key in &env


(-> &form meta :clojure.core/macroexpand)






rewrite systems in general are very neat


I'm guessing its a compromise between allowing people to do too many unpredictable things


I wrote a lisp -> go compiler using something like macroexpansion, but the expansion happened outside in, then inside out, and could pass information up and down the expansion


I've never heard of any downsides to something like this


I think it is an issue of scope


@hiredman I bet "Unable to resolve symbol" error messages would be more complex


the compiler would have to expand down and back up and only then start looking for problems


you want local transformations, to have an understandable system


what does that mean?


I think I am off thinking about general rewrite systems, not and particular changes to clojure's macro system