Hi all, I was wondering about value classes, is th...
# language-evolution
d
Hi all, I was wondering about value classes, is there a reason why they don't automatically inherit the extension functions of its wrapped type? E.g. if I have a
value class Id(val value: String)
, extension functions for String such as
toInt()
are not exposed, so I can't do
Id("1").toInt()
Would it make sense to expose them? Why, why not?
a
you can use interface delegation, which helps some https://kotlinlang.org/docs/inline-classes.html#inline-classes-and-delegation You can’t delegate to
String
because it’s not an interface, but you can delegate to
CharSequence
w
What if I had
Copy code
fun String.isValidEmail() = ...

value class Username(val value: String)
with your suggestion it would be valid to call
Username("foo").isValidEmail()
. I think the point of value classes is to represent separate types.
String
is not
Username
, even though its underlying type is
String
d
Sadly this only seems to work for actual declared functions of that interface, not extension functions of that interface. So in this case I could use subSequence, but not toInt
a
toInt()
is an extension function on
String
d
@Adam S ok bad example, but if we take min which is an extension function on CharSequence, it still doesn't work
@wasyl each username is a String, but not every String is a username, this could actually be solved by using a value class to wrap an email and defining a function or extension function on that specific type. Defining extension functions on very broad types always have this problem
a
hmm, do you have an example of it not working? https://pl.kotl.in/OgFZzflwN
d
@Adam S I messed something up in my code, nvm 🫢
w
Defining extension functions on very broad types always have this problem
That's why value classes exist. You'll never wrap all types in value classes and so leaking such invalid extensions will always be the case. Take stdlib
fun String.toInt()
, you're saying you'd still want to be able to call
Username("foo").toInt()
? You can't define
toInt()
on a more specific type
d
So your reasoning is that if you want to restrict a type and NOT make all the functions of its wrapped type available, then that is a use case for value types, which makes sense I guess
w
Yes, IMO
Username
!=
String
, and more generally a value classes is not its wrapped type. While some extension functions on wrapped type may be useful, a lot of things can go wrong: • what if an extension returns a type? What if it's a different type (like
toInt()
• even if the function returns the same type, it might not be valid for the given inline class (e.g.
String?.orEmpty()
wouldn't make sense if your value class had a contract that the value is never empty) • the function may semantically not make sense, like
username.isValidEmail()
I'd ask the opposite question — why do you want to use e.g.
String
extensions on a non-String type?
b
It's not really the goal of value classes. They will also support multiple properties in the future, so from that perspective it wouldn't make sense.
r
is there a reason why they don't automatically inherit the extension functions of its wrapped type?
Yes. The whole point of a value class is to create a new type. If you want a different name for the same type, use
typealias
.
b
Perhaps refined types is what you're after? @Davio