When a function takes a `Closeable` as a parameter...
# library-development
m
When a function takes a
Closeable
as a parameter, is there a nice way to indicate whether the function actually closes the
Closeable
or if it's the caller responsibility? Ideally looking at the signature only?
l
Name it
useXxx
if it closes it
m
? gotcha
Makes sense 👍
l
One example is
useLines
m
So I guess
Json.decodeFromStream(InputStream)
doesn't close the inputStream?
l
Good question
I'd think it does
Looks like a terminal operation to me.
m
It does look like a terminal operation
But thinking a bit more about it, there are use cases where you have Json in the middle of an otherwise text document
l
InputStream is not just a Closeable
m
In these cases, you don't want Json to close the document
l
True. Do you know if that function from kotlinx.serialization actually closes the stream?
m
Looking quickly at the source, it doesn't seem so
Back to the initial point. Naming functions
useFoo()
isn't great either because it doesn't say anything about how it's going to "use" it
l
What if you call the function twice? Does it still work?
m
Will try
l
Then just let folks wrap it with
use { … }
when it makes sense?
m
Sure but if my function closes it, there's no need
l
If the use case is that no one should need to let it non-closed, makes sense to use
use
inside no matter the naming or the signature.
m
The use cases is that given a generic
decode(InputStream)
function, I want to know if I should wrap in `use {}`or not. The current way seems to rely on the documentation or just "guess" like in the Json case
l
To me, it's more like "will I use the same instance more than once?"
m
Yea but that's more or less the same thing, right? Can I continue using the same
inputStream
after the
decode
call or not
Copy code
Unexpected JSON token at offset 68: Expected EOF after parsing, but had { instead
JSON input: kotlinx.serialization.json.internal.ArrayAsSequence@61a002b1
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 68: Expected EOF after parsing, but had { instead
JSON input: kotlinx.serialization.json.internal.ArrayAsSequence@61a002b1
Looks like
decodeFromStream
is doing yet something else 😅
It doesn't close but expects the end of file 🤔
l
Is there an overload that takes something different than an
InputStream
from
<http://java.io|java.io>
?
m
Don't think so, the
InputStream
support is pretty new IIRC
l
Maybe you can make your own InputStream backed InputStream that emits a fake EOF when the boundary you want is reached?
m
Mmmmm that sounds dangerous...
Also, that would emit the EOF after the Json object is finished parsing, which requires Json parsing
l
Isn't
InputStream
an interface?
m
Sure but it means decoding Json twice
That's super bad
l
If it's json withing another document, like a markdown file, you can detect the closing ```
m
Sure but I don't want to, I want kotlinx.serialization to do it for me 🙂
l
First time is just doing an indexOf/iteration, it's not json parsing in any way.
m
I don't think there's a point in forcing the trailing EOF. I'll file a bug
l
Which is why kotlinx.serialization doesn't do it, it's not designed to detect when the input stops looking like JSON
m
Stilli wasted CPU cycles
l
Not necessarily, the logic to find ``` is more more simple than JSON parsing itself
m
Which is why you can't resume parsing the same inputStream
the logic to find ``` is more more simple than JSON parsing itself
It is but if you have to parse json in all cases, you don't want to waste cpu cycles searching for bacticks at the same time
l
Since an InputStream can come straight from network, I don't think you can always read the same instance twice.
m
I'm not talking rewinding, I'm talking "continuing" reading the same stream
Copy code
@Serializable
  class SimpleObject(val name: String)

  @Test
  fun testDecode() {
    val inputStream  = """
    {
      "name": "Object1"
    }
    {
      "name": "Object2"
    }
    """.trimIndent().byteInputStream()
    println("1")
    println(Json.decodeFromStream<SimpleObject>(inputStream).name)
    println("2")
    println(Json.decodeFromStream<SimpleObject>(inputStream).name)
  }
l
Iterating itself is not very expensive, it's the operations done during the iterations that can cost
m
"not very expensive" is still > free
I want everything for free
😛
l
Then use C, and you will pay your calls to
free
m
Yea, I'm ok paying for my garbage collector 😛
l
I'd measure the cost of iterating once to find the backticks and a second time for json parsing vs some code that manages both. Ensure you're using custom code for parsing to measure the same thing.
m
I'll just file a bug 🙂
Because it's not only about cost, it's also about API design
l
To ask for EOF to be optional? That'd suit your needs fully?
m
I want
decodeFromStream
to either: • parse a complete Json stream and close it • or allow parsing partial Json streams and leaving them open
The current behaviour has the drawbacks of both approaches
• Caller has to remember to wrap in
use {}
or else leaks happens • And at the same time it's not possible to decode partial streams
👍🏼 1
l
Waiting for the GitHub issue link like a hedgehog

https://c.tenor.com/ODCQZW2FncEAAAAM/sonic-hedgehog.gif

💙 1