What’s the recommended way to turn Clojure into EDN? I ran into some issues with pr-str
. It works fine with pure data, but less so if data contains lazy seqs. I tried to make naive edn-response middleware for ring. I gave it some lazy seqs (a map containing something that had passed through for
) and this resulted in a string of log messages bundled up with the stringified data :thinking_face: Interestingly, the ring-json-middleware does not have this problem, as cheshire/generate-string does not use printing. Is there a way to produce EDN data that isn’t based on printing, or am I barking up the wrong tree?
👍 Thanks!
FYI you can work around this by first realising the sequence, whilst holding onto its head. This will apply all its effects. Then simply rewalk the structure printing it. Clojure caches the returned values of lazy seqs, so this should work. Obviously it might not be viable for huge datastructures though.
If you go that route, you need to deep-walk it, probably an easy hack around is to actually print it first, and then call pr-str again on it
https://clojuredocs.org/clojure.core/print-method there is a way to adjust printing for some data based on type in general there is nothing wrong with using pr-str to produce edn. cheshire is not using printing because the serialized form of data not generaly compatible with json specification
I just tried to reproduce and it works just fine for the case like that:
(def x (for [i (range 100)] i))
(type x)
(pr-str x)
could you share a sample that give you a headache?The problem is lazy sequences that do side effects, like logging. When creating a generic tool (like an edn-middleware for Ring), you have no control over what crazy shenanigans the lazy data structures you’re passed will do. So, using pr-str
to generate EDN, might include log statements printed to `*out*` .
Yes, logging is my exact problem 🙂
This reproduces it:
(pr-str
(for [i (range 10)]
(do
(prn i)
i)))
;;=> "(0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0 1 2 3 4 5 6 7 8 9)"
Does anyone have a snippet that enables http/2 on jetty without using info.sunng/ring-jetty9-adapter
Jetty's official docs are very "xml focused", which does not make it easy
and i really don't want to stray off the "official" path to get something like this working
It's impossible to serialize a lazy-seq without realizing it, unless you don't want to serialize the data contained in it?
And so realizing it will always trigger the side effect
But to get the serialized string without the printed stuff that should be possible. Hum... Let me check
Right, you can use pr-on
instead
Though that seems to be a private fn
There's an issue here: https://ask.clojure.org/index.php/2791/captures-stdout-printing-effects-lazily-evaluated-expressions
You should upvote the issue if you want it fixed, the core team does take into account votes for priorities
I'd also recommend upvoting this one: https://ask.clojure.org/index.php/1905/there-should-also-be-writing-in-clojure-edn
Hi Clojurians, I wrote a list multiplication program which multiplies two numbers represented as lists using standard multiplication algorithm taught in school. https://www.mycompiler.io/view/E5DB2XN is the first program I wrote. This program helped me pass a bunch of problems on Hackerrank, until it started giving me timeouts at https://www.hackerrank.com/contests/projecteuler/challenges/euler016/problem project euler problem. So, I tried improving the functions with https://www.mycompiler.io/view/4owO1yk shorter but slower attempt. I am not able to figure out why the latter program is slower than the first one. Can anyone help?
In the new attempt there's this code:
(reduce (partial map +) ls)
It doesn't look right.Oh, ls
is a list of lists, I see.
@p-himik Yes sir, that is list of products.
It's almost night here, so I'm rather lazy to go through the code right now. But at the very least, that particular part should be written as (apply map + ls)
.
This way, it won't create a (dec (count ls))
of lazy collections.
@p-himik yes, I already tried that. That was even slower. (apply map + ls)
I can't offer a more constructive advice right now other than "try profiling". Then you'll know for certain.
@p-himik thank you Sir. Appreciate that. I will look into profiling.
what's the state of core.typed right now? Is anyone using it after its renaissance? is it ready?
It should be pretty easy to translate (lossily) between types and schemas. But inferring either is the really interesting part.
In theory, it can be used to type check Clojure pretty well, but the user ergonomics might annoy you
Since it doesn't have global inference, it just does local inference, and typing Clojure functions often involve complex type defs, since Clojure functions tend to be quite powerful in input
And the core isn't typed, and all libs won't be typed either, so you've always got this type/untyped boundary as well
I'm a type theory expert and I find the types too complicated 😅 But a lot of people like Typescript so 🤷
I don't think its unreasonable to want static analysis of the parts of a spec that can be statically analyzed
> And the core isn't typed, and all libs won't be typed either, so you've always got this type/untyped boundary as well Yeah, but core will be spec'd and typescript balances that fairly well so there is some way forward
and if the whole community weren't osborne effect'ed on spec-2035 it might be possible to get some core ones on board
I don't think you can do a lot of typing with spec no? Cause the predicates rarely map to a logical type?
It would have been nice if maybe spec had done something like: type + pred
So like the broader type + a refinement pred
And if I understand Typescript properly, a hord of devs jumped on it and retrofitted types for a ton of existing JavaScript. I mean this could happen for core.type as well but did not
Also personally, not sure how useful types would be without having abstract data types as the standard compound data-structure.
All maps would still just be of type map. So the issue of like what keys are in this? Am I using a key that doesn't exist? Etc. Would still be there
I think type script has row type polymorphism that can handle this. Not sure what it’s like in practice though
I think row type just means that any superset is automatically valid, even if not explicitly defined as is-a relationship.
But it would still require a definition of the fields on the type no?
Like I think you can do row polymorpism with Clojure records.
Yes that’s my understanding. Don’t know if core typed has this kind of analysis but would love it
Ya I think that's correct. Basically it just let's you say this function works on any type which have at minimum these keys of some given types. But it still requires the type checker to know what keys of what types the object has. So for it to work with maps, it would need that somehow it could infer for any given map at any point in the program what are the keys it would now contain and what types those keys would be of. Which seems like really hard to do. And that ignores that it's possible to have a dynamic key too, like where the key name is coming at run-time from external input
But records would work well with it. Cause records already force you to define the mandatory keys, you'd only need to also define their types and you could then do row polymorpism type checking, if your variables and function arguments specific row type constraints.
The author is interviewed fairly recently here: https://youtu.be/8A8EKTBlFRM which I watched when it came out but unfortunately I somehow don't remember what the summary of the current state is. I know he talks about what he's been working on and I believe has been actively working on core.typed but I'm not sure if those changes are available yet
Actually the changes I'm talking about are probably just the renaissance you refer to
(unrelated) maybe a malli thing might work well. Like, a subset of what malli can specify might be translatable to typedclojure
oh wow the video here is almost along those lines, just talking about spec
@p-himik I found the cause. It was map
function retuning lazy sequences which delays the execution. I used mapv
at two places and it made it twice faster. Thanks for putting me on the path and letting me explore this lazy sequence angle.