Kotlin needs try-with-resources

And the reason is that Kotlin only targets Java 6 and AutoClosable did not exist in Java 6. If you are only targeting later Java it is trivial to add to your code

For those who do not want to implement the utility themselves and while Kotlin developers think whether to add it or not to stdlib, you may use a basic implementation (as described above) already available in Maven Central

<dependency>
    <groupId>net.offecka</groupId>
    <artifactId>kutils</artifactId>
    <version>0.0.1</version>
</dependency>

Targeted JRE 8, ancient stuff is not going to be supported…
Ever!

I completely agree with the point that new language construct should be avoided. But the lack of the try-with-resources (a feature that both Java and C# have) is a step backwards. It’s a fundamental concept which is a must-have in language without destructors. It deserves a proper syntax and a language construct which would make it fool-proof, as safe as possible and autocomplete-, fixit- and diagnostcs- friendly. It’s 10 times as important as any other candidate for a language construct.

@cubuspl42 That’s right, but given that all of that could be achieved with the help of standard use function, is there still a reason to introduce special language construct for that?

I completely agree with @alexd457. I took the idea presented here and extended it a little further, please let me know what you think.

Here is the implementation.

And usage examples via unit tests.

But seriously, none of this should be remotely necessary. This is something that should absolutely be a language construct. It makes sense for it to be, and would solve a whole host of problems.

This + overload is the exactly reason why many people don’t like operator overloading. + is about numbers, not about resource management. I would suggest just to use some function name. Otherwise great idea, I didn’t know that you could “overload” catch like that. I made similar library for myself. Honestly Jetbrains should just add something like that to standard library along with use.

I agree about + overloading. Better to use something like use or even resource to avoid name clashes. As for the whole discussion, I think it is a great idea to move a lot of features from language into library, it simplifies understanding of the code and gives a lot of additional freedom.

I also think that try-with-resources should be in library functions.
On the other hand, I think that a seperate project “Kotlin-EE” like “Java-EE” should be maintained, where things like this should be standardized.
But maybe it’s to early for that, yet?

1 Like

I agree with both of you, the + overload was bad and I’ve switched it out for a standard extension method, .autoClose(). Due to the overhead of the ResourceManager, and the messiness of requiring the finally {} block else all exceptions would be eaten (which is what people would inevitably do), I stand by my position that this is too important to just be a library function. If someone comes up with some cool new syntax for dealing with this that is natural, efficient, and safe, then I’ll be happy to use it. But so far no one has offered up anything which satisfies all three of those requirements.

The natural part is hard to satisfy. I would say that Kotlin property delegates were not natural to me when I first read about them, but now I use them like I never knew any better.

But I think the standard use function satisfies efficiency and safety (otherwise it probably would not be part of the standard library). The only downside of use is that it only works with Closeable on the JVM target, but that is solvable: Generic use function

Assuming a generic version of use, the remaining question is: Is it really that unnatural to nest a few uses?

I am a method extraction fanatic, so when I am bothered by a deeply nested structure, I simply extract additional methods.

Why did you used this design with catch-finally things? I don’t really understand it. Here’s topic with my code: Is there standard way to use multiple resources? I don’t really see any problems with exceptions. May be I missed something?

There’s also use for AutoCloseable, it is available in kotlin-stdlib-jre7 library: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/use.html

In my opinion, yeah it’s pretty unnatural. Well, at the worst maybe just messy and ugly. Sure, those aren’t deal breakers, I’m not saying Kotlin is a bad language because of this, but for me it’s a major pain point. Everywhere else Kotlin is clean and accomplishes it’s goals with simplicity, but here it falls flat on it’s face. Even with additional methods you still have the issue that to get any kind of exception handling built in you always have to nest it inside another try-catch.

To me this speaks of a poorly designed resource management system. As @alexd457 said, when a language uses exceptions to handle error states, resource management should be closely designed with it. Simply throwing the exception in the .use() block and catching that in a subsequent try-catch block underneath it is just dirty.

I’m not saying that it doesn’t work. .use() certainly works, and you can use it perfectly safely. I am saying the current system is not very good and needs to be improved, and as far as I’ve seen extending the language itself is far and away the best option for doing that. I kind of understand the idea that accomplishing it with the stdlib is easier to work with and undersatnd, but I disagree. Complex stdlib methods are not easy to work with, language constructs built specifically around these goals are typically much better suited. .use() is not a complex method by any means, but it’s simplicity brings it’s drawbacks.

2 Likes

Are there any plans for common (non JVM specific) AutoCloseable and/or Closeable interfaces, and use extension function?

I’m writing mostly multiplatform libraries and I find it inconvenient to define my own use() extensions for the lib users. I believe it’s nicer if they can simply use the stdlib functions (and I wouldn’t need to test it :stuck_out_tongue:) .

Why not defining some sort of deconstructor for each class?

Like the unusable (so meaningless) finalize() function in java.
Kolin could call this function 100% before beginning the object deallocation.

So a nice place for putting the expected close() into.

No it can’t. Deallocation is done by the JVM garbage collector and the control you have over that is quite low and depends on the jvm implementation. Maybe this can be done on native, but I don’t see this working on the JVM.

I know. Therefore I wrote

Kotlin native would have the potential.

Perhaps there is a possibility tweaking the compiler so that all objects are somehow soft-referenced by the “kotlin runtime” and then deconstructed and after that released (for java deallocation).

Just as the title says. Kotlin needs try-with-resources or using, or whatever.

Look at this simplified example in Java.

List<String> fileNames = Arrays.asList("file1", "mustBeEmpty", "file2");
for (String fileName : fileNames) {
    OutputStream out = Files.newOutputStream(fileSystem.getPath(fileName));
    try {
        if (fileName.equals("mustBeEmpty"))
            continue;
        out.write(new byte[] {1, 2, 3});
    } finally {
        out.close();
    }
}

Suppose I’d like to convert it to try-with-resources (just as I should, because of a better syntax and exception suppression). Now it looks like this:

List<String> fileNames = Arrays.asList("file1", "mustBeEmpty", "file2");
for (String fileName : fileNames) {
    try (OutputStream out = Files.newOutputStream(fileSystem.getPath(fileName))) {
        if (fileName.equals("mustBeEmpty"))
            continue;
        out.write(new byte[]{1, 2, 3});
    }
}

Nice, isn’t it? OK, let’s do the same in Kotlin… here’s the original code, converted to Kotlin by IDEA:

val fileNames = listOf("file1", "mustBeEmpty", "file2")
for (fileName in fileNames) {
    val out = Files.newOutputStream(fileSystem.getPath(fileName))
    try {
        if (fileName == "mustBeEmpty")
            continue
        out.write(byteArrayOf(1, 2, 3))
    } finally {
        out.close()
    }
}

So far so good, but I’d really like to use use, no pun intended. So here’s the try-with-resources version, again, converted by IDEA:

val fileNames = listOf("file1", "mustBeEmpty", "file2")
for (fileName in fileNames) {
    Files.newOutputStream(fileSystem.getPath(fileName)).use { out ->
        if (fileName == "mustBeEmpty")
            continue
        out.write(byteArrayOf(1, 2, 3))
    }
}

Looks sort of nice too… except it doesn’t compile now! Because use is not a regular statement, but instead is an inline function with a lambda expression as an actual parameter, and therefore continue doesn’t work here. So IDEA failed to convert Java code properly, and we must do it by hand:

val fileNames = listOf("file1", "mustBeEmpty", "file2")
for (fileName in fileNames) {
    Files.newOutputStream(fileSystem.getPath(fileName)).use { out ->
        if (fileName == "mustBeEmpty")
            return@use
        out.write(byteArrayOf(1, 2, 3))
    }
}

Now it suddenly doesn’t look nice at all. Worse, return@use will get you a well-deserved warning from IDEA when there are multiple nested use calls (not to mention those are ugly enough by themselves), so in order for it to be context-independent, we’ll have to do this:

val fileNames = listOf("file1", "mustBeEmpty", "file2")
for (fileName in fileNames) {
    Files.newOutputStream(fileSystem.getPath(fileName)).use useFile@ { out ->
        if (fileName == "mustBeEmpty")
            return@useFile
        out.write(byteArrayOf(1, 2, 3))
    }
}

For real? I love Kotlin, and this is certainly not a game-breaking failure, but…

I admit, if we ever get to use non-local break and continue similar to non-local returns, it won’t be such an ugly thing anymore. But still, try-with-resources and using are excellent examples of good language design! I can understand that some language constructs can easily be replaced by function calls or other language constructs, but this is one example of a language construct that is very simple to use, always does its job and doesn’t really make the language overly complicated or anything like that.

Most of the time there is a better way of doing things than using continue or break with labels:

    fileNames
    .filterNot { it == "mustBeEmpty" }
    .forEach { fileName ->
        Files.newOutputStream(Paths.get(fileName)).use { outStream ->
            outStream.write(byteArrayOf(1, 2, 3))
        }
    }

or with for each:

for (fileName in fileNames) {
    if (fileName == "mustBeEmpty") {
        continue
    }
    Files.newOutputStream(Paths.get(fileName)).use { outStream ->
        outStream.write(byteArrayOf(1, 2, 3))
    }
}

You can use this :wink:

for (fileName in fileNames) {
	if (fileName != "mustBeEmpty") {
		File(fileName).appendBytes(byteArrayOf(1, 2, 3))
	}
}