:wave: It seems Kotlin Serialization (and probabl...
# arrow
t
👋 It seems Kotlin Serialization (and probably other Kotlin compiler plugins) can by pass
refined-type
plugin. here is an example:
Copy code
@JvmInline
@Serializable
value class GameId private constructor(val value: String) {
    companion object : Refined<String, GameId>(::GameId, {
        ensure((it.length == 8) to "Expected $it has 8 characters")
    })
}

@Serializable
data class Game(
    val gameId: GameId,
    val color: String,
)

fun main() {
    val json = """{"gameId":"abcd1234","color":"white"}"""
    println(Json.decodeFromString<Game>(json))
    val json2 = """{"gameId":"","color":"white"}"""
    println(Json.decodeFromString<Game>(json2))
}
Output:
Copy code
Game(gameId=GameId(value=abcd1234), color=white)
Game(gameId=GameId(value=), color=white)
Is this something we have to accept or should we put some require in
init
block?
r
Hi @thanh, and thanks for trying the refined types stuff, i think you are the first user LOL. I would create an issue in arrow-meta to explore automating this for the user:
Copy code
value class GameId private constructor(val value: String) {
  init {
    GameId.require(value)
  }
  companion object : Refined<String, GameId>(::GameId, {
    ensure((it.length == 8) to "Expected $it has 8 characters")
  })
}
t
If we put
GameId.require(value)
inside
init
like that it would cause StackOverFlow like this:
Copy code
xception in thread "main" java.lang.StackOverflowError
	at se.thanh.refined.GameId$Companion$2.invoke(Main.kt)
	at se.thanh.refined.GameId$Companion$2.invoke(Main.kt:27)
	at se.thanh.refined.GameId.constructor-impl(Main.kt:65)
	at se.thanh.refined.GameId.access$constructor-impl(Main.kt:17)
	at se.thanh.refined.GameId$Companion$1.invoke-4eQqh7M-4eQqh7M(Main.kt:27)
	at se.thanh.refined.GameId$Companion$1.invoke(Main.kt:27)
	at se.thanh.refined.GameId.constructor-impl(Main.kt:66)
	at se.thanh.refined.GameId.access$constructor-impl(Main.kt:17)
	at se.thanh.refined.GameId$Companion$1.invoke-4eQqh7M-4eQqh7M(Main.kt:27)
Better solution would be something like:
Copy code
init {
    val results = constraints(value)
    if (!results.allValid())
        throw IllegalArgumentException(renderMessages(results))
}
Also it would be an honor if I really were the first user, Thanks @raulraja for your hard work. I really like this plugin so far.
Another thing I notice is: If I put a break point in Refined.kt and debug, the program will not do anything, the IDE even frozen sometime. Not sure it likes this for all compiler plugin or just
refined-type
plugin.
r
mmm have not seen that freeziing behavior myself, yes regarding the init block makes sense
t
cool, I'll create an issue for that
Another issue I have is:
Copy code
listOf("").map { GameId(it) }.forEach(::println)
The code above is compiled successfully and an
IllegalStateException
is thrown. I thought
refined-type
should not allow this kind of behavior?
r
The invoke still behaves like require, but if you had the plugin active it should have bailed with an error in that case because
it
is not a constant value. https://github.com/arrow-kt/arrow-meta/blob/main/refined-types-plugin/src/test/kotlin/arrow/meta/plugins/liquid/LiquidTests.kt#L108-L118
It’s possible there is a bug in where it’s not validating that. if you want to help fix it feel free to add it as a test case in that same file
t
Cool, I'm looking at the code right now. I'll try to see that if I can help on any thing.
r
Thank you @thanh!
t
Hey @raulraja, may be I did something wrong with my setup. It is compiled even with code like this:
Copy code
fun f(n: Int) =
    PositiveInt(n)

// this should not be compiled
fun useDynamicValue() {
    val z = f(-1)
    println(z)
    // Output
    // Exception in thread "main" java.lang.IllegalArgumentException: -1 should be > 0
    //	at se.thanh.refined.MainKt.f(Main.kt:52)
    //	at se.thanh.refined.MainKt.useDynamicValue(Main.kt:28)
    //	at se.thanh.refined.MainKt.main(Main.kt:14)
    //	at se.thanh.refined.MainKt.main(Main.kt)
}
I pushed a sample code in github, can you please take a look: https://github.com/lenguyenthanh/arrrow-refined-types-example.
r
I just checked and the plugin is not getting applied. I think it’s because we haven’t yet published the gradle plugin so the compiler plugin needs to be added manually
I’m gonna dig a bit further and I’ll let you know if there is an interim solution until we publish the gradle plugin.
t
Thanks Raul, really appreciated!
r
Hi @thanh ! Thanks for your interest 🙌 I reviewed the deployments and your workspace and it seems the use of the
kapt
plugin interferes with the proper behavior of the Refined Types Plugin. If I just add
kapt
plugin into demos , Refined Types Plugin doesn't work as expected in
refined-types-compiler-plugin-demo
. We'll review the interaction with other plugins, thank you so much for letting us know!!
r
thanks @Rachel!
t
Thanks @Rachel for your update! But it doesn't seem to work for me even if I removed all other plugins (kapt and Kotlin Serialization). For example if I modified the demos like this:
Copy code
data class PositiveIntEven private constructor(val value: Int) {
    companion object : Refined<Int, PositiveIntEven>(::PositiveIntEven, PositiveInt, Even)
}

fun main() {
    val n = -1
    val result = PositiveInt(n).value
//    val result = PositiveInt(-1).value
}
It is still compiled and throw an exception in runtime.
r
Sorry for the delay @thanh, you're right, it doesn't work if I validate that use case in a test for Arrow Refined Types Plugin directly:
Copy code
arrow-meta/refined-types-plugin/src/test/kotlin/arrow/meta/plugins/liquid/LiquidTests.kt
An issue about it would be very welcome 🙏 Thank you so much!! 🙌
t
No worries Rachel and thanks for the update, I'll create an issue later today.
r
Awesome, thank you @thanh!
❤️ 1