https://kotlinlang.org logo
Title
g

Gabriel Luchtenberg

02/14/2023, 1:20 AM
is there a Kotlin similar to the C# implicit operator? I'd like to be able to have an e-mail class that has its validation on the constructor, but I'd like to be able to do something similar to
val email : Email = "<mailto:gabrie@test.co|gabrie@test.co>"
b

Bryan Buschmann

02/14/2023, 1:31 AM
Does that imply that there is a class with a constructor`Email(String)` ?
g

Gabriel Luchtenberg

02/14/2023, 1:31 AM
yep
b

Bryan Buschmann

02/14/2023, 1:38 AM
Wouldn't
val primaryIdentity = Email("<mailto:gabrie@test.co|gabrie@test.co>")
be more concise and more explicit at the same time?
Using the constructor informs other developers about the How as well as the What.
a

Andrew O'Hara

02/14/2023, 3:05 AM
Alternatively;
"<mailto:gabriel@test.co|gabriel@test.co>".toEmail()
where
fun String.toEmail(): Email = TODO("validate and init email")
b

Bryan Buschmann

02/14/2023, 3:13 AM
I was thinking about that as well, but I tilted back and forth on it and decided to fall on the constructor side of things.
j

Josh Eldridge

02/14/2023, 3:28 AM
If I saw
val email : Email = "<mailto:gabrie@test.co|gabrie@test.co>"
I'd think someone type aliased a string as
Email
for some reason, not super readable up front
s

Stephan Schröder

02/14/2023, 8:09 AM
let's talk more about the validation on the constructor aspect. First your use case sounds like the typical inline class example where you don't want to pay for the overhead of a wrapper type. So this would be the approach to do that:
@JvmInline
value class Email(val value: String) {
    init {
        require(value.contains('@')) {"email value $value doesn't contain '@'"}
    }
}
At runtime the compiler will have gotten rid of the Email class in most cases, so you have the performance of only handling a String but the type safety of a wrapper type approach. But as mentioned before the wrapping of your type around a string isn't invisible, so it's either
Email("<mailto:gabrie@test.co|gabrie@test.co>")
or you write an extension function (this time without the validation, since that's taken care of in the init-block).
fun String.toEmail(): Email = Email(this)
For more info on inline classes see https://kotlinlang.org/docs/inline-classes.html
p

phldavies

02/14/2023, 10:07 AM
Given the general usage of inline value classes to represent refined types, I can see the value in an implicit constructor - possibly useful as
value class Email implicit constructor(val value: String)
🤔
s

Stephan Schröder

02/15/2023, 7:49 AM
Kotlin very much prefers being explicit over being implicit. If you want to overwrite a function, you have to add the
overwrite
keyword, if you want tail recursion to be applied to a function that would allow it, you still have to add
tailrec
, if you want to convert number types, e.g. convert an Int to Long, you still have to invoke
toLong()
. Notice that this is even the case for the safe direction Int -> Long. I googled a bit a found an old discussion from 2015, coming to the same conclusion https://discuss.kotlinlang.org/t/union-types/77/10 Of course this is not a primary source (from JetBrains itself), but just another dev coming to the same conclusion.