pr-str captures stdout from printing side-effects of lazily evaluated expressions.

Description

Because clojure.core/pr-str uses with-out-str to capture the output of pr (and pr cannot be parsed a writable thing - just uses out).

If you pr-str the result of something lazy you can get side-effects written to stdout with println interspersed with the output. For example in my case I was extracting benchmarks from the library criterium and trying to print the data structure to the file. The solution would be to provide an overload of pr/pr-str that takes a writer. I note that pr-on provides some of the functionality but it is private.

This is an ugly bug when you're trying to persist program output in EDN, because the randomly interspersed stdout messages make it invalid for read-string. We shouldn't need our functions to be pure for pr-str to work as expected.

I've omitted a patch because although I think a fix is straight-forward I'm not sure quite where it should go (e.g. make pr-on public, change pr, change pr-str)

Environment

Linux

Activity

Show:

Oliver Caldwell April 22, 2019 at 2:07 PM

How about doall on any lazy-seq passed to a p...-str function? They need to be fully resolved regardless, this way we can resolve them before they're wrapped in with-out-str. I think that means the inner printing functions can remain as they are and the -str variants can simply ensure the sequences are resolved before they get printed. I don't think this'd change the characteristics of the functions too much but would fix all of these issues.

At the very least, consumers can use (pr-str (doall my-seq)) to ensure that all laziness is resolved outside of the with-out-str call within pr-str. This could be used as a temporary workaround.

Thoughts?

Oliver Caldwell April 22, 2019 at 11:11 AM

I've now realised all p...-str functions have this same issue, currently having a think about how I could solve it for all of them. So any printing lazy-seq wouldn't have it's output sent to out. Maybe I could split this so that it doesn't get sent to out but something else that can be rebound separately without using with-out-str.

Oliver Caldwell April 21, 2019 at 3:18 PM

So I think a work around for this would be to change pr-str to use pr-on with it's own string writer. This way things intended for out will still get there, the user can then wrap the call in with-out-str if they really want to.

I can't think of why anyone would've relied on this functionality in the past, but there is the risk that this would break someone's workflow. I propose (and will submit a patch for if there's no good reason not to) that we change pr-str to use pr-on directly with a string writer instead of pr which defaults to out. This does mean that pr-str will have to reimplement the var args processing but that's okay I think.

The main motivator for this is that prepl uses pr-str to encode data to send down the socket. This means my prepl tooling will capture out from all lazy-seq results since pr-str is used upstream. The other fix for this would be to configure my prepl to not use pr-str but my own function, but I think that's side stepping the issue.

I think the right thing to do here is to fix pr-str, I don't think the behaviour is correct or expected and will only catch more people out in the future. Again, I'd love to have a go at fixing this but I wanted to run my thoughts past you first.

Stuart Halloway July 19, 2015 at 1:48 PM

as a workound for this, use print-dup or print-method

Details

Assignee

Reporter

Labels

Priority

Affects versions

Created September 23, 2014 at 3:45 PM
Updated October 13, 2023 at 4:58 AM