i have not seen this done outside of me doing this...
# stdlib
j
i have not seen this done outside of me doing this, since early days of java, where -ea triggers asserts and asserts make for very cheap production debug logging, e.g. removed. the first one logDebug has been my preferred logging apparatus but i just cribbed ".also" as ".debug" is there any other selective #define mechanism extant or planned for the kotlin language?
Copy code
#!kotlin

fun logDebug(debugTxt: () -> String) {
    try {
        assert(false, debugTxt)
    } catch (a: AssertionError) {
        System.err.println(debugTxt())
    }
}

@ExperimentalContracts
inline fun <T> T.debug(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
   try { assert(false)  } catch (a: AssertionError) {
      block(this)
    }
    return this
}
e
assert
desugars to a runtime test on a static
$assertionsDisabled
field: https://www.benf.org/other/cfr/how-is-assert-implemented.html
j
no complaints here. I just find it curious that the nearest thing on the map is some (java-land) contrived log4j/spring/IOC distinctions from posix syslog levels and a relatively astronomical distance to the level of vm-wide selective execution with no lookup or documentation requirements on the part of the code maintainer.
a stdlib might get some mileage here on this set of distances by perhaps specifying POSIX has adequately differentiated and documented "levels" for noise, and logging overheads, and debugging, and so goes the stdlib with these references to same in applicable concepts.
likewise a syntactic sugar keyword for "module top-level const boolean gated execution" might be a handy way to introduce macro blocks particularly if the language finds execution-time states like debug, headless, threadless, and so on to guide things simpler than crossreferencing expect/actual pairs in distant MPP gradle project hierarchies
e
just saying that
Copy code
class Foo {
    fun logDebug(debugTxt: () -> String) {
        if (!`$assertionsDisabled`) System.err.println(debugTxt())
    }
    companion object {
        @[JvmSynthetic JvmField] private val `$assertionsDisabled` = Foo::class.java.desiredAssertionStatus()
    }
}
would be equivalent, without the
Throwable
overhead, at least on JVM
j
so in the context of the stdlib, and those of us who are not as acute as yourself on the available landscape mapping, does something like this make sense to have, to suggest? I find it agreeable, though i do grok that my particular throwable overhead are suboptimal, but in the bigger picture, are my instincts miscalibrated ?
like on multiple levels, is two "levels" of conditional execution useful? is there a goal in programming languages to utilize "fine","finer", and "finest" instead of "debug" which generally does the 80/20 for most things in most programming usecases on most languages? would there be particular unfinished areas of stdlib and kotlin language that benefit from idioms and community aptitude in contributing practical metaphors ?
e
neither Java nor Kotlin have any sort of explicitly conditional compilation. you can of course use different sourcesets to compile different variants, or swap out whole classes at runtime, but (as seen in the implementation of
assert
) it's also very typical to use a regular code condition structured in a way such that the JIT can hopefully optimize it out
for multiplatform, I do think it could have been a reasonable design choice to have platform-specific implementations intermingled with some note indicating where they should be compiled - this is one of the things Rust does with its
#[cfg(...)]
, or Go does with its
// +build
, or
#if
in C/C++, etc. - but clearly Kotlin decided to go with multiple sourcesets with various `expect`/`actual` declarations instead