Best Kotlin builder implementation ever? - kotlin

I work with a lot's of generated kotlin data classes (openapi-generated) with val's and two common fields available and processed. So i can assign values only on construction, like:
data class StringRepresentation {
val value: String,
val available: Boolean,
val processed: Boolean,
}
data class DoubleRepresentation {
val value: Double,
val available: Boolean,
val processed: Boolean,
}
And i have to init lot's of them with common robust code, like:
val currentRepresentation = StringRepresentation("Something", true, false)
Can any pattern or Kotlin language support be used to remove robost object initialization?
It could be wonderful to use some kind of generic template method, something like this:
private inline fun <reified T: StringRepresentation> buildRepresentation(
value: Any,
available: Boolean,
processed: Boolean
): T {
return when(T) {
is StringRepresentation -> StringRepresentation(value.toString(), available, processed)
else -> ...
}
}
, but my types and properties are final and also syntax doesn't allow to set multiple generic boundaries. I can't figure it out the right approach for this. I guess in that case I need to write a builder, but this seems to be java way.
Is there any kotlin way to do this?

I do not think that it is possible with your setup to write a builder that would actually be useful. Your *Representation data types stand in no explicit type relation to their respective type parameter (e.g. a StringRepresentation is not Something<String>), so what should be the return type of a generic builder function? It could only be Any and you would need to cast every result to its expected type in order to use it.
What you can do is to define a generic data class:
data class Representation<T>(
val value: T,
val available: Boolean,
val processed: Boolean,
)
I know, you cannot use that class as super class of your specific data classes, but you can write extension functions that convert the generic representation for one value type into its corresponding specific representation:
fun Representation<String>.typed() = StringRepresentation(value, available, processed)
fun Representation<Double>.typed() = DoubleRepresentation(value, available, processed)
Then you can use the same code to create a data object of any type:
val stringRepresentation: StringRepresentation = Representation("x", false, true).typed()
val doubleRepresentation: DoubleRepresentation = Representation(1.0, false, true).typed()
But please note that this is still not a generic solution since whatever you put into the constructor Representation has to be typed explicitly as a String or Double, respectively.
Let's say you define a generic function for all undefined value types:
fun <T: Any> Representation<T>.typed(): Any = error("no typed representation for value type ${value::class}")
The specific cases above will still work, and you could additionally write something like this:
val x : Any = 2.0
val someRep: Any = Representation(x, true, false).typed()
This is syntactically correct and will compile, but it will not work as desired, because what you get is an IllegalArgumentException ("no typed representation for value type class kotlin.Double"), because x is not typed as Double, but as Any.

Related

Interfering type parameter based on another type parameter

Is it possible to Kotlin Compiler to infer type parameter based on another type parameter?
That is: infere type A based on type B in IntExample class definition.
interface Something<A>
interface Example<A, B: Something<A>>
class IntSomething: Something<Int>
// Can Int type parameter be infered by the fact that IntSomething is Something<Int>?
// If yes, how could I specify B type parameter without specifing the infered A (Int) parameter?
class IntExample: Example<Int, IntSomething> // specifing Int here is painful!!!
Imagine that we have more type parameters like that - it will be a lot of boilerplate to specify each of them if some might (theoretically) be infered.
EDIT
After an exhausive respose from #KarstenGabriel I will extend the previous example, to make it clear what for are the type parameters used here:
interface Something<A> {
val sth: A
}
interface Example<A, B: Something<A>> {
val eg: A
val something: B
}
data class IntSomething(override val sth: Int): Something<Int>
data class DescribedIntSomething(
override val sth: Int,
val description: String
): Something<Int>
data class DescribedIntExample(
override val eg: Int,
override val something: DescribedIntSomething,
): Example<Int, DescribedIntSomething> // specifing Int here is painful
fun main() {
val describedIntExample = DescribedIntExample(
eg = 1,
something = DescribedIntSomething(1, "Just one")
)
// We have to know that something is DescribedIntSomething to read description.
// Neither `*` nor `Something<Int>` is sufficient, we need `B: Something<Int>` to keep B
val description = describedIntExample.something.description
println(description)
}
So we use the type parameters - A and B as return values from eg and something
Wildcard * cannot be used as it is just means Any?. We need to keep concrete type B. (eg. to enable reading descrption in the example)
Wildcard Something<Int> cannot be used instead of B: Something<Int> for thesame reason as in the point 1
Maybe it's just an language design level. The type parameters do exists in compile time (they are ereased later), so theoretically Example<DescribedIntSomething> might be sufficient instead of Example<Int, DescribedIntSomething> as DescribedIntSomething is Something<Int>
The problem is solvable under some assumptions:
You need type parameter A only for property eg (or other similar cases).
It is okay that we convert eg to be a function instead of a property.
The desired value for eg (your A value) can be derived from the value of something (your B value). If the value is not inferred, then it is not possible to infer the type.
We then define Example without parameter A:
interface Example<B: Something<*>> {
val something: B
}
Now inside Example we do not have a possibility to obtain the type parameter of B's Something-type, because the type parameter A on Something<A> does not exist at runtime. So you cannot just ask an instance of B what its Something-type parameter is. And Kotlin cannot do that either, because the information is just not there, if it is not explicitly specified by you.
But we know the parameter at compile-time which can be used in an inline extension function with reified parameter:
inline fun <reified A, reified B : Something<A>> Example<B>.eg(): A = something.sth
(documentation: Inline functions with reified parameter and Extension functions)
In this example I assume that eg should have the value of sth.
If you want eg to be private, you can put the function inside Example and make it private, otherwise it must be specified outside of the class.
Now you can just define your subclass without the need to specify the former A parameter, but you still have the right type:
data class DescribedIntExample(
override val something: DescribedIntSomething,
) : Example<DescribedIntSomething>
fun main() {
val describedIntExample = DescribedIntExample(
something = DescribedIntSomething(1, "Just one")
)
val x = describedIntExample.eg() // x has inferred type Int and value 1
}

Providing only one type parameter to an extension function with multiple type parameters in Kotlin

Introduction
In Kotlin I have a generic conversion extension function that simplifies conversion of this object of type C to an object of another type T (declared as the receiver) with additional conversion action that treats receiver as this and also provides access to original object:
inline fun <C, T, R> C.convertTo(receiver: T, action: T.(C) -> R) = receiver.apply {
action(this#convertTo)
}
It is used like this:
val source: Source = Source()
val result = source.convertTo(Result()) {
resultValue = it.sourceValue
// and so on...
}
I noticed I often use this function on receivers that are created by parameterless constructors and thought it would be nice to simplify it even more by creating additional version of convertTo() that automates construction of the receiver based on its type, like this:
inline fun <reified T, C, R> C.convertTo(action: T.(C) -> R) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
Unfortunately, I cannot call it like this:
source.convertTo<Result>() {}
because Kotlin expects three type parameters provided.
Question
Given above context, is it possible in Kotlin to create a generic function with multiple type parameters that accepts providing just one type parameter while other types are determined from the call-site?
Additional examples (by #broot)
Imagine there is no filterIsInstance() in stdlib and we would like to implement it (or we are the developer of stdlib). Assume we have access to #Exact as this is important for our example. It would be probably the best to declare it as:
inline fun <T, reified V : T> Iterable<#Exact T>.filterTyped(): List<V>
Now, it would be most convenient to use it like this:
val dogs = animals.filterTyped<Dog>() // compile error
Unfortunately, we have to use one of workarounds:
val dogs = animals.filterTyped<Animal, Dog>()
val dogs: List<Dog> = animals.filterTyped()
The last one isn't that bad.
Now, we would like to create a function that looks for items of a specific type and maps them:
inline fun <T, reified V : T, R> Iterable<T>.filterTypedAndMap(transform: (V) -> R): List<R>
Again, it would be nice to use it just like this:
animals.filterTypedAndMap<Dog> { it.barkingVolume } // compile error
Instead, we have this:
animals.filterTypedAndMap<Animal, Dog, Int> { it.barkingVolume }
animals.filterTypedAndMap { dog: Dog -> dog.barkingVolume }
This is still not that bad, but the example is intentionally relatively simple to make it easy to understand. In reality the function would be more complicated, would have more typed params, lambda would receive more arguments, etc. and then it would become hard to use. After receiving the error about type inference, the user would have to read the definition of the function thoroughly to understand, what is missing and where to provide explicit types.
As a side note: isn't it strange that Kotlin disallows code like this: cat is Dog, but allows this: cats.filterIsInstance<Dog>()? Our own filterTyped() would not allow this. So maybe (but just maybe), filterIsInstance() was designed like this exactly because of the problem described in this question (it uses * instead of additional T).
Another example, utilizing already existing reduce() function. We have function like this:
operator fun Animal.plus(other: Animal): Animal
(Don't ask, it doesn't make sense)
Now, reducing a list of dogs seems pretty straightforward:
dogs.reduce { acc, item -> acc + item } // compile error
Unfortunately, this is not possible, because compiler does not know how to properly infer S to Animal. We can't easily provide S only and even providing the return type does not help here:
val animal: Animal = dogs.reduce { acc, item -> acc + item } // compile error
We need to use some awkward workarounds:
dogs.reduce<Animal, Dog> { acc, item -> acc + item }
(dogs as List<Animal>).reduce { acc, item -> acc + item }
dogs.reduce { acc: Animal, item: Animal -> acc + item }
The type parameter R is not necessary:
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
inline fun <reified T, C> C.convertTo(action: T.(C) -> Unit) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
If you use Unit, even if the function passed in has a non-Unit return type, the compiler still allows you to pass that function.
And there are other ways to help the compiler infer the type parameters, not only by directly specifying them in <>. You can also annotate the variable's result type:
val result: Result = source.convertTo { ... }
You can also change the name of convertTo to something like convert to make it more readable.
Another option is:
inline fun <T: Any, C> C.convertTo(resultType: KClass<T>, action: T.(C) -> Unit) = with(resultType.constructors.first().call()) {
convertTo(this, action)
}
val result = source.convertTo(Result::class) { ... }
However, this will conflict with the first overload. So you have to resolve it somehow. You can rename the first overload, but I can't think of any good names off the top of my head. I would suggest that you specify the parameter name like this
source.convertTo(resultType = Result::class) { ... }
Side note: I'm not sure if the parameterless constructor is always the first in the constructors list. I suggest that you actually find the parameterless constructor.
This answer does not solve the stated problem but incorporates input from #Sweeper to provide a workaround at least simplifying result object instantiation.
First of all, the main stated problem can be somewhat mitigated if we explicitly state variable's result type (i.e. val result: Result = source.convertTo {}) but it's not enough to solve the problem in cases described by #broot.
Secondly, using KClass<T> as result parameter type provides ability to use KClass<T>.createInstance() making sure we find a parameterless constructor (if there's any – if there is none, then result-instantiating convertTo() is not eligible for use). We can also benefit from Kotlin's default parameter values to make result parameter type omittable from calls, we just need to take into account that action might be provided as lambda (last parameter of call) or function reference – this will require two versions of result-instantiating convertTo().
So, taking all the above into account, I've come up with this implementation(s) of convertTo():
// version A: basic, expects explicitly provided instance of `receiver`
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
// version B: can instantiate result of type `T`, supports calls where `action` is a last lambda
inline fun <C, reified T : Any> C.convertTo(resultType: KClass<T> = T::class, action: T.(C) -> Unit) = with(resultType.createInstance()) {
(this#convertTo).convertTo(this#with, action)
}
// version C: can instantiate result of type `T`, supports calls where `action` is passed by reference
inline fun <C, reified T : Any> C.convertTo(action: T.(C) -> Unit, resultType: KClass<T> = T::class) = with(resultType.createInstance()) {
(this#convertTo).convertTo(T::class, action)
}
All three versions work together depending on a specific use case. Below is a set of examples explaining what version is used in what case.
class Source { var sourceId = "" }
class Result { var resultId = "" }
val source = Source()
fun convertX(result: Result, source: Source) {
result.resultId = source.sourceId
}
fun convertY(result: Result, source: Source) = true
fun Source.toResultX(): Result = convertTo { resultId = it.sourceId }
fun Source.toResultY(): Result = convertTo(::convertX)
val result0 = source.convertTo(Result()) { resultId = it.sourceId } // uses version A of convertTo()
val result1: Result = source.convertTo { resultId = it.sourceId } // uses version B of convertTo()
val result2: Result = source.convertTo(::convertX) // uses version C of convertTo()
val result3: Result = source.convertTo(::convertY) // uses version C of convertTo()
val result4: Result = source.toResultX() // uses version B of convertTo()
val result5: Result = source.toResultY() // uses version C of convertTo()
P.S.: As #Sweeper notices, convertTo might not be a good name for the result-instantiating versions (as it's not as readable as with basic version) but that's a secondary problem.

Kotlin type is not inferred into generic function

I have a generic function to fetch/get any list out of the SharedPreferences. However, when I wanted to test, that it does not work, when I saved a list of say, Messages and ask for a list of say, Ints, it still worked. It just ignored the type I precised and returned a List of JsonObjects. When I debugged the whole code, I found, that apparently the function does not care about the inferred class type. I´ll first put here the code, so I can explain the problem:
fun <T> getListFromPreferences(preferences : SharedPreferences, key : String)
: MutableList<T> {
val listAsString = preferences.getString(key, "")
val type: Type = object : TypeToken<List<T>>() {}.type
val gson = SMSApi.gson
return gson.fromJson<ArrayList<T>>(listAsString, type)
?: ArrayList()
}
So, what I would expect, was, that when I call the function like this:
PreferenceHelper.getListFromPreferences<Message>(preferences, TEST_KEY)
the "type" variable in the above code should return List. However the result the debugger shows me is: java.util.List<? extends T>
I have absolute no idea, why the inferring does not work, but I´d really like it to work to ensure, what I am requesting is actually what I get, for obvious reasions.
Does anybody know a reason and a solution for this weird behaviour?
Due to type erasure, actual type information about T is lost, so basically this method returns List<Any?> (even if you pass Int as T).
To preserve the actual type, you need to declare this method with reified parameter:
inline fun <reified T> getListFromPreferences(preferences : SharedPreferences, key : String)
: MutableList<T> {
//...
}

Type inference for higher order functions with generic return types

The following example is perfectly legal in Kotlin 1.3.21:
fun <T> foo(bar: T): T = bar
val t: Int = foo(1) // No need to declare foo<Int>(1) explicitly
But why doesn't type inference work for higher order functions?
fun <T> foo() = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
When using higher order functions, Kotlin forces the call site to be:
val t = foo<Int>()(1)
Even if the return type of foo is specified explicitly, type inference still fails:
fun <T> foo(): (T) -> T = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
However, when the generic type parameter is shared with the outer function, it works!
fun <T> foo(baz: T) = fun (bar: T): T = bar
val t: Int = foo(1)(1) // Horray! But I want to write foo()(1) instead...
How do I write the function foo so that foo()(1) will compile, where bar is a generic type?
I am not an expert on how type inference works, but the basic rule is: At the point of use the compiler must know all types in the expression being used.
So from my understanding is that:
foo() <- using type information here
foo()(1) <- providing the information here
Looks like type inference doesn't work 'backward'
val foo = foo<Int>()//create function
val bar = foo(1)//call function
To put it in simple (possibly over-simplified) terms, when you call a dynamically generated function, such as the return value of a higher-order function, it's not actually a function call, it's just syntactic sugar for the invoke function.
At the syntax level, Kotlin treats objects with return types like () -> A and (A, B) -> C like they are normal functions - it allows you to call them by just attaching arguments in parenthesis. This is why you can do foo<Int>()(1) - foo<Int>() returns an object of type (Int) -> (Int), which is then called with 1 as an argument.
However, under the hood, these "function objects" aren't really functions, they are just plain objects with an invoke operator method. So for example, function objects that take 1 argument and return a value are really just instances of the special interface Function1 which looks something like this
interface Function1<A, R> {
operator fun invoke(a: A): R
}
Any class with operator fun invoke can be called like a function i.e. instead of foo.invoke(bar, baz) you can just call foo(bar, baz). Kotlin has several built-in classes like this named Function, Function1, Function2, Function<number of args> etc. used to represent function objects. So when you call foo<Int>()(1), what you are actually calling is foo<Int>().invoke(1). You can confirm this by decompiling the bytecode.
So what does this have to do with type inference? Well when you call foo()(1), you are actually calling foo().invoke(1) with a little syntactic sugar, which makes it a bit easier to see why inference fails. The right hand side of the dot operator cannot be used to infer types for the left hand side, because the left hand side has to be evaluated first. So the type for foo has to be explicitly stated as foo<Int>.
Just played around with it a bit and sharing some thoughts, basically answering the last question "How do I write the function foo so that foo()(1) will compile, where bar is a generic type?":
A simple workaround but then you give up your higher order function (or you need to wrap it) is to have an intermediary object in place, e.g.:
object FooOp {
operator fun <T> invoke(t : T) = t
}
with a foo-method similar as to follows:
fun foo() = FooOp
Of course that's not really the same, as you basically work around the first generic function. It's basically nearly the same as just having 1 function that returns the type we want and therefore it's also able to infer the type again.
An alternative to your problem could be the following. Just add another function that actually specifies the type:
fun <T> foo() = fun(bar: T): T = bar
#JvmName("fooInt")
fun foo() = fun(bar : Int) = bar
The following two will then succeed:
val t: Int = foo()(1)
val t2: String = foo<String>()("...")
but... (besides potentially needing lots of overloads) it isn't possible to define another function similar to the following:
#JvmName("fooString")
fun foo() = fun(bar : String) = bar
If you define that function it will give you an error similar as to follows:
Conflicting overloads: #JvmName public final fun foo(): (Int) -> Int defined in XXX, #JvmName public final fun foo(): (String) -> String defined in XXX
But maybe you are able to construct something with that?
Otherwise I do not have an answer to why it is infered and why it is not.

Kotlin type-safe typealiases

I use typealiases in my Kotlin code a lot, but I wonder if I can enforce type-safety on them.
typealias Latitude = Double
typealias Longitude = Double
fun someFun(lat: Latitude, lon: Longitude) {...}
val lat: Latitude = 12.34
val lon: Longitude = 56.78
someFun(lon, lat) // parameters are in a wrong order, but the code compiles fine
It would be nice if I could somehow prevent implicit casting between typealiases, helping to avoid such issues.
Of course, there is a problem, that the operations on basic types would be not available for typealiases, but it can be solved with extension functions (or casts).
I don't want to use data classes, holding a single field, because it seems a bit of overkill, especially for primitive types (or maybe I am wrong and they'll be optimized out?)
So the question: can I somehow enforce type-safety for typealiases?
Update for Kotlin 1.3
Inline classes are already available as of Kotlin 1.3 and currently are marked as experimental. See the docs
Original answer
Unfortunately you can't avoid this currently. There is a feature in progress - inline classes (#9 in this document), which will solve the problem with the runtime overhead, while enforcing compile time type-safety. It looks quite similar to Scala's value classes, which are handy if you have a lot of data, and normal case classes will be an overhead.
Unfortunately this is not possible with typealiases. The kotlin reference says:
Type aliases do not introduce new types. They are equivalent to the
corresponding underlying types. When you add typealias Predicate<T>
and use Predicate<Int> in your code, the Kotlin compiler always expand
it to (Int) -> Boolean. Thus you can pass a variable of your type
whenever a general function type is required and vice versa:
typealias Predicate<T> = (T) -> Boolean
fun foo(p: Predicate<Int>) = p(42)
fun main(args: Array<String>) {
val f: (Int) -> Boolean = { it > 0 }
println(foo(f)) // prints "true"
val p: Predicate<Int> = { it > 0 }
println(listOf(1, -2).filter(p)) // prints "[1]"
}
See kotlin type aliases.
tl;dr You have to use (data) classes
As the name typealias implies, a typealias is only a alias and not a new type. In your example Latitude and Longitude are Ints, independet from their names. To make them typesafe you have to declare a type. In theory you could inherit new types from Int. Since Int is a final class this is not possible. So its reqiuered to create a new class.
You can use inline classes for this (since Kotlin 1.5). Inline classes are erased during complication, so at runtime lat and lon are just doubles, but you get the benefit of the compile-time checks.
#JvmInline
value class Latitude(private val value: Double)
#JvmInline
value class Longitude(private val value: Double)
fun someFun(lat: Latitude, lon: Longitude) {
println("($lat, $lon)")
}
fun main() {
val lat = Latitude(12.34)
val lon = Longitude(56.78)
someFun(lon, lat) // Type mismatch: inferred type is Longitude but Latitude was expected
someFun(lat, lon) // OK
}
Here is the difference between typealias and inline classes for the case of avoiding params wrong order:
typeAlias:
typealias LatitudeType = String
typealias LongitudeType = String
fun testTypeAlias() {
val lat: LatitudeType = "lat"
val long: LongitudeType = "long"
testTypeAliasOrder(lat, long) // ok
testTypeAliasOrder(long, lat) // ok :-(
}
fun testTypeAliasOrder(lat: LatitudeType, long: LongitudeType) {}
inline classes:
#JvmInline
value class Latitude(val lat: String)
#JvmInline
value class Longitude(val long: String)
fun testInlineClasses() {
val lat = Latitude("lat")
val long = Longitude("long")
testInlineClassesOrder(lat, long) // ok
testInlineClassesOrder(long, lat) // Compilation error :-)
}
fun testInlineClassesOrder(lat: Latitude, long: Longitude) {}
I've been interested in this topic for quite a while. This is what I came up with.
I would define an interface for an Id type and I would implement it:
interface UniqueId<T> {
fun getId(): T
fun toExternal(): String
}
data class UniqueIdImpl<T>(private val id: T) : UniqueId<T> {
override fun getId(): T = id
override fun toExternal(): String = "External-$id"
}
(For the sake of the example, I could have made it simpler by omitting the type parameter and just go for Int...)
Then you define your types like so, using delegation:
data class ClientId(private val id: UniqueId<Int>): UniqueId<Int> by id
data class OrderId(private val id: UniqueId<Int>): UniqueId<Int> by id
data class SomeId(private val id: UniqueId<UUID>): UniqueId<UUID> by id
And this is how to use them:
val clientId = ClientId(UniqueIdImpl(1))
val someId = SomeId(UniqueIdImpl(UUID.randomUUID()))
EDIT:
Well, you can get similar effect with abstract classes...
abstract class I<T>(private val i: T) {
fun getId() = i
fun toExternal() = "External-$i"
}
data class OtherId(private val i: Int) : I<Int>(i)
data class YetAnotherId(private val i: UUID) : I<UUID>(i)
By defining Latitude and also Longitude as aliases for Double it can be seen as transitive aliases, i.e. you defined Latitude as an alias for Longitude and vice versa. Now, all three type names can be used interchangeably:
val d: Double = 5.0
val x: Latitude = d
val y: Longitude = x
You could, as an alternative, simply use parameter names to make clear what is being passed:
fun someFun(latitude: Double, longitude: Double) {
}
fun main(args: Array<String>) {
val lat = 12.34
val lon = 56.78
someFun(latitude = lon, longitude = lat)
}
I was recently struggling with similar case. Inline classes are not the solution cause it forces me to use property wrapper.
Hopefully for me I've managed to solve my problem by inheritance delegation.
class ListWrapper(list: List<Double>): List<Double> by list
This approach allows us to operate directly on ListWrapper as on regular List. Type is strictly identified so it might be passed via the Koin dependency injection mechanism for example.
We can go even deeper:
class ListListWrapper(list: ListWrapper): ListWrapper by list
but this require us to "open" the parent class with reflection cause `#Suppress("FINAL_SUPERTYPE") does not work.
Unfortunately with primitives there is other issue, cause they somehow providing only empty private constructor and are initialized with some undocumented magic.