Assertions are off by default and are enabled for Kotlin/JVM the same way you would in Java. From the assert() docs:
For JVM
Throws an AssertionError if the value is false and runtime assertions have been enabled on the JVM using the -ea JVM option.
For Native
Throws an AssertionError if the value is false and runtime assertions have been enabled during compilation.
For Kotlin Native, it doesn’t mention how to enable them directly in the in-code docs. Looking at the reference page for Kotlin Compiler Arguments shows -ea under Kotlin Native. I also found it here in the Kotlin Native repo to enable assertions.
The “problem” is that for the disabling of the condition to work it would need to be provided as an (inlinable) function, not as an expression. Kotlin’s assert is just an inline function, not a language construct.
What I don’t understand is, why can’t Kotlin just compile assertions to be JVM assertions? IIRC, JVM assertions actually required a new bytecode to implement. Why not use it?
Kotlin could compile assertions to java assertions. That would require a new language feature or a significant change to the assert signature (passing a lambda insetad of a boolean) but it would be possible. The reason (as I understand it) why kotlin doesn’t do this, is that JetBrains noticed that there are a number of bugs that result from sideeffect in the assertion code no longer being executed in production code.
@Wasabi375 great idea. It sounds like a stdlib addition of assert { /* only runs if assertions are enabled */ } would fix the issue for most use cases.
Here’s a simple implementation if people want to use this now:
fun assert(block: () -> Boolean) {
if (Object::class.java.desiredAssertionStatus()) block()
}
fun main() {
println("Hello World")
assert { println("SIDE EFFECT"); false }
}
There does seem to be a real risk if assertions have side-effects: after all, you need to be testing the actual code that’s going to run in production, not something that can have significant differences.
In my experience, there are two sorts of checks:
Checks that belong in the code. If something’s really important, then the check should run in production too. Kotlin even provides nice require(), requireNotNull(), check(), and checkNotNull() to make it easy. (Your production system does handle exceptions properly, doesn’t it??)
Checks that belong in unit tests. If the test isn’t worth doing in production, then it should be in a unit test, not in the main code. If the unit test doesn’t have the necessary access, then refactor the code a bit. (Sometimes you just need to split a method up or provide an accessor.)
sigh@gidds, thank you for succinctly describing the myth.
The assertion use case is neither of these. Unit tests are great, but assert was introduced because they run at the wrong time. Though assertions should virtually never run in production, the one place they should always run is staging.
Runtime exceptions, like thrown from require are great, but those should always run, and when failing, should always be caught, hence why they throw Exceptions. Errors, as thrown by assert should never be caught, because when thrown, something is very very wrong, and the system should exit (or at least the subsystem should exit, if running in a container or framework of some kind).
Assertions can be thought of more like documentation. (And it occurs to me now: they may be misused for some of the same reasons that engineers neglect to document their code.) Assertions protect code against refactoring bugs. If code is moved out of the context it is written in, are the assumptions it was written with still true? The only way you can know is if they are there in the code.
You don’t have to use assertions. You don’t have to do test driven development, either. But those who do use assertions as designed have good reason to do so.
Yes, using assertions to generate side effects can cause bugs. Don’t do that.
Maybe, @arocnies, if we could be sure that JIT optimizations would elide those assert function calls (including arguments, and ideally also instantiation of lambdas) when assertions are disabled. See above.
Critically, assertions also need to be able to be enabled on a per-package basis without recompiling with JVM command-line args we’re all used to. Maybe that could be done in such a standard Kotlin library function.
Could such a function call just be compiled to an actual JVM assertion?
@al3c, regarding implementation of JVM assertion behavior on other platforms: on a platform without just-in-time compilation, assertions would always be a no-op. I would expect the “implementation” for kotlin-native, for instance, to be simply eliding the entire assertion. Unless explicitly enabled, assertions don’t run. They don’t run because they’re compiled out. For JVM, that’s at runtime. For native, that would be at compile time.
Personally, I don’t see this as a high priority need due to my suspicion that a very narrow group of users have this kind of assertion use case.
Luckily JetBrains doesn’t need to rely on suspicion and may be able to get these stats from Java users who apply assertions this way. So everyone opt-in to sharing user statistics!
Since performance is a deal breaker for you, it might be worth experimenting to see what the JIT is willing to optimize away. But for a more general solution, I believe you could implement this as a compiler plugin and handle compilation differently on each platform.
If my suspicion is correct and there’s a small userbase then a compiler plugin would be able to support those users needs better than an addition to the core language.
A compiler plugin would allow for quicker iteration than the stdlib and allow for a build up of real world examples before being considered for being added to the core language. Userbase would become clearer as well.
Of course the alternative is to write up a KEEP proposal but I love the idea of these kinds of requests being built as compiler plugins first to not only assess their pros/cons, but use them as production tools even if they don’t belong in the core language.
A compiler plugin sounds like a sensible starting point to me. Is there documentation on writing Kotlin compiler plugins? Are there template projects that could provide a starting point?
I think the arrow guys have a framework that allows to write compiler plugins. Not sure how mature that project is. Compiler plugins still don’t have a public api so they rely on kotlin internal functions but the arrow framework will probably still work. The kotlin team has said a few times that they want to make it public in the future, but they have not prioritized that so far. Maybe with or after the compiler rework so around 1.5 or 1.5.x.
There are a few third party production used compiler plugins. Square has Anvil and there’s the JetPack Compose plugin. Isn’t Arrow built on compiler plugins? If I remember correctly, there was a talk about compiler plugins awhile in previous years at KotlinConf–which may be out dated at this point.
JetPack Compose is a special case. After all it is build by JetBrains and they are also the ones to maintain kotlin so they have less of a problem using an internal and undocumented API that might change at any point.
So Arrow is probably the only production ready kotlin compiler plugin not developed by JetBrains. That said they provide a library to build custom compiler plugins: https://meta.arrow-kt.io/
Today I am wishing for an assertion comment syntax which would also define the message of the AssertionError on failure, something like:
/*assert
While Company X do not officially support large
media files, we will let you try, but if you need
to troubleshoot an issue let's
check contentLength <= MAX_SUPPORTED_SIZE
*/
If the check line fails, then the whole comment would go into a string including the boolean expression, and the values of contentLength and MAX_SUPPORTED_SIZE.