clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2021-03-24T08:44:04.392700Z

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?

2021-03-25T07:19:22.423100Z

👍 Thanks!

2021-03-25T09:43:12.423700Z

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.

2021-03-25T17:38:30.450800Z

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

2021-03-24T08:51:44.392900Z

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

2021-03-24T08:52:41.393100Z

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?

2021-03-24T09:14:37.393300Z

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*` .

2021-03-24T09:15:14.393500Z

Yes, logging is my exact problem 🙂

2021-03-24T09:47:18.393800Z

This reproduces it:

(pr-str
 (for [i (range 10)]
   (do
     (prn i)
     i)))

2021-03-24T09:47:32.394Z

;;=> "(0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0 1 2 3 4 5 6 7 8 9)"

emccue 2021-03-24T15:14:03.395Z

Does anyone have a snippet that enables http/2 on jetty without using info.sunng/ring-jetty9-adapter

emccue 2021-03-24T15:14:24.395500Z

Jetty's official docs are very "xml focused", which does not make it easy

emccue 2021-03-24T15:17:29.396Z

and i really don't want to stray off the "official" path to get something like this working

2021-03-24T16:52:31.396600Z

It's impossible to serialize a lazy-seq without realizing it, unless you don't want to serialize the data contained in it?

2021-03-24T16:52:47.396800Z

And so realizing it will always trigger the side effect

2021-03-24T16:54:13.397Z

But to get the serialized string without the printed stuff that should be possible. Hum... Let me check

2021-03-24T16:57:11.397200Z

Right, you can use pr-on instead

2021-03-24T16:58:52.397400Z

Though that seems to be a private fn

2021-03-24T17:00:00.397900Z

You should upvote the issue if you want it fixed, the core team does take into account votes for priorities

2021-03-24T17:04:20.398900Z

I'd also recommend upvoting this one: https://ask.clojure.org/index.php/1905/there-should-also-be-writing-in-clojure-edn

Vikas Gautam 2021-03-24T20:55:33.408800Z

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?

p-himik 2021-03-24T21:07:43.409Z

In the new attempt there's this code:

(reduce (partial map +) ls)
It doesn't look right.

p-himik 2021-03-24T21:08:24.409200Z

Oh, ls is a list of lists, I see.

Vikas Gautam 2021-03-24T21:13:26.410500Z

@p-himik Yes sir, that is list of products.

p-himik 2021-03-24T21:15:33.410700Z

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).

p-himik 2021-03-24T21:15:58.410900Z

This way, it won't create a (dec (count ls)) of lazy collections.

Vikas Gautam 2021-03-24T21:17:00.411100Z

@p-himik yes, I already tried that. That was even slower. (apply map + ls)

p-himik 2021-03-24T21:17:56.411300Z

I can't offer a more constructive advice right now other than "try profiling". Then you'll know for certain.

Vikas Gautam 2021-03-24T21:20:21.411500Z

@p-himik thank you Sir. Appreciate that. I will look into profiling.

dpsutton 2021-03-24T21:29:57.412300Z

what's the state of core.typed right now? Is anyone using it after its renaissance? is it ready?

nilern 2021-03-25T09:02:39.423500Z

It should be pretty easy to translate (lossily) between types and schemas. But inferring either is the really interesting part.

2021-03-26T03:29:27.463700Z

In theory, it can be used to type check Clojure pretty well, but the user ergonomics might annoy you

2021-03-26T03:29:59.463900Z

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

2021-03-26T03:30:37.464100Z

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

nilern 2021-03-26T10:36:11.464400Z

I'm a type theory expert and I find the types too complicated 😅 But a lot of people like Typescript so 🤷

emccue 2021-03-26T22:24:44.020600Z

I don't think its unreasonable to want static analysis of the parts of a spec that can be statically analyzed

emccue 2021-03-26T22:25:52.020800Z

> 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

emccue 2021-03-26T22:27:01.021Z

and if the whole community weren't osborne effect'ed on spec-2035 it might be possible to get some core ones on board

2021-03-27T03:17:39.022Z

I don't think you can do a lot of typing with spec no? Cause the predicates rarely map to a logical type?

2021-03-27T03:18:03.022200Z

It would have been nice if maybe spec had done something like: type + pred

2021-03-27T03:18:40.022400Z

So like the broader type + a refinement pred

2021-03-27T03:19:24.022600Z

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

2021-03-27T03:20:16.022800Z

Also personally, not sure how useful types would be without having abstract data types as the standard compound data-structure.

2021-03-27T03:21:16.023Z

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

dpsutton 2021-03-27T03:26:32.024Z

I think type script has row type polymorphism that can handle this. Not sure what it’s like in practice though

2021-03-27T03:52:30.024200Z

I think row type just means that any superset is automatically valid, even if not explicitly defined as is-a relationship.

2021-03-27T03:52:55.024400Z

But it would still require a definition of the fields on the type no?

2021-03-27T03:53:53.024600Z

Like I think you can do row polymorpism with Clojure records.

dpsutton 2021-03-27T04:09:56.025500Z

Yes that’s my understanding. Don’t know if core typed has this kind of analysis but would love it

2021-03-27T04:37:40.025700Z

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

2021-03-27T04:38:57.025900Z

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.

2021-03-24T21:35:52.412400Z

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

2021-03-24T21:38:00.412600Z

Actually the changes I'm talking about are probably just the renaissance you refer to

emccue 2021-03-24T21:45:41.413800Z

(unrelated) maybe a malli thing might work well. Like, a subset of what malli can specify might be translatable to typedclojure

emccue 2021-03-24T21:55:36.414300Z

oh wow the video here is almost along those lines, just talking about spec

Vikas Gautam 2021-03-24T22:13:52.414500Z

@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.

1👍
vemv 2021-03-24T22:31:39.414800Z

Is there as strong difference between wrapped/lookup-or-miss and wrapped/through-cache? (I observed that the former uses the non-wrapped through-cache so it might be a 'superset' of the functionality?) I tried to understand the difference but sadly in the repo wrapped/through-cache has no unit tests and a worse docstring than wrapped/lookup-or-miss so I'm confused about what the wrapped/through-cache fn actually does

dpsutton 2021-03-24T22:38:21.415100Z

ah good point. i think that's the one that can prevent needless spinning under contention

dpsutton 2021-03-24T22:38:38.415300Z

i'd do some experiements. spin up 30 threads hitting your cache and count the number of times something is computed

dpsutton 2021-03-24T22:38:45.415500Z

i think there was something like that in that article

vemv 2021-03-24T22:40:18.415700Z

Yeah I got that impression from the article and source I expect contention to be low/irrelevant so probably I'll sleep calmer with max 10 attempts

vemv 2021-03-24T22:50:26.416Z

Curious, you include clojure.core.cache.wrapped/evict under the 'footgun' category But wanting to immediately overwrite a key with a different new value seems a quite extremely common use case? (`lookup-or-miss`/`through-cache` doesn't appear to help here, not without waiting for the cache entry to naturally expire) Maybe the mistake is mine for thinking of these caches as atoms (as opposed to things that can have disparate implementations)?

dpsutton 2021-03-24T22:51:36.416200Z

i think you really shouldn't call evict yourself. the cache has strategies to call evict. but i suppose if you wanted to override you could

vemv 2021-03-24T22:55:47.416400Z

yes I suppose that eviction is essential to all these caches, they have to perform eviction sooner or later :) but I suspect depending on the use case, waiting an indeterminate amount of time (or having dropped writes) is not what one wants. OTOH, I suppose that if one used evict recklessly one could increase contention or get nil reads

vemv 2021-03-24T22:56:08.416600Z

(btw I'm just testing out my own knowledge, and not really questioning anything)