What I would like to have is two different integer types which are semantically distinguishable.
E.g. in this code a 'Meter' type and a 'Pixel' int type
typealias Meter = Int
typealias Pixel = Int
fun Meter.toPixel() = this * 100
fun Pixel.toMeter() = this / 100
fun calcSquareMeters(width: Meter, height: Meter) = width * height
fun calcSquarePixels(width: Pixel, height: Pixel) = width * height
fun main(args: Array<String>) {
val pixelWidth: Pixel = 50
val pixelHeight: Pixel = 50
val meterWidth: Meter = 50
val meterHeight: Meter = 50
calcSquareMeters(pixelWidth, pixelHeight) // (a) this should not work
pixelWidth.toPixel() // (b) this should not work
}
The problem with this solution is
(a) that I can call calcSquareMeters with my 'Pixel' type which I don't want to be possible and
(b) that I can call the toPixel() extension function which I only want to have for my 'Meter' type on my 'Pixel' type which I don't want to be possible.
I guess this is the intended behaviour of typealias, so I guess to achieve my goal I have to use something different than typealias...
So how can I achieve this?
In addition to the existing answer: If you have a lot of common functionality between the two types and don't want to duplicate it, you can work with an interface:
interface MetricType<T> {
val value: Int
fun new(value: Int): T
}
data class Meter(override val value: Int) : MetricType<Meter> {
override fun new(value: Int) = Meter(value)
}
data class Pixel(override val value: Int) : MetricType<Pixel> {
override fun new(value: Int) = Pixel(value)
}
Like this, you can easily define operations on the base interface, such as addition, subtraction and scaling:
operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
The combination of interface and generics ensures type safety, so you do not accidentally mix types:
fun test() {
val m = Meter(3)
val p = Pixel(7)
val mm = m + m // OK
val pp = p + p // OK
val mp = m + p // does not compile
}
Keep in mind that this solution comes at a runtime cost due to the virtual functions (compared to rewriting the operators for each type separately). This in addition to the overhead of object creation.
Indeed, typealiases don't guarantee this sort of type safety. You'll have to create wrapper classes around an Int value instead to achieve this - it's a good idea to make these data classes so that equality comparisons work on them:
data class Meter(val value: Int)
data class Pixel(val value: Int)
Creation of instances of these classes can be solved with extension properties:
val Int.px
get() = Pixel(this)
val pixelWidth: Pixel = 50.px
The only problematic thing is that you can no longer directly perform arithmetic operations on Pixel and Meter instances, for example, the conversion functions would now look like this:
fun Meter.toPixel() = this.value * 100
Or the square calculations like this:
fun calcSquareMeters(width: Meter, height: Meter) = width.value * height.value
If you really need direct operator use, you can still define those, but it will be quite tedious:
class Meter(val value: Int) {
operator fun times(that: Meter) = this.value * that.value
}
fun calcSquareMeters(width: Meter, height: Meter) = width * height
There is a proposal (not yet guaranteed to be accepted) to add inline classes for this purpose. I.e.
#InlineOnly inline class Meter(val value: Int)
will really be an Int at runtime.
See https://github.com/zarechenskiy/KEEP/blob/28f7fdbe9ca22db5cfc0faeb8c2647949c9fd61b/proposals/inline-classes.md and https://github.com/Kotlin/KEEP/issues/104.
From kotlin doc:
Type aliases do not introduce new types. They are equivalent to the corresponding underlying types. When you add typealias Predicate and use Predicate 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
This means that there isn't possible check over your typealias, and you are rally declaring your extensions functions as:
fun Int.toPixel() = this * 100
fun Int.toMeter() = this / 100
fun calcSquareMeters(width: Int, height: Int) = width * height
fun calcSquarePixels(width: Int, height: Int) = width * height
I fear the only way to achieve that you want is implementing an extra class for each type.
I would also go with the solution from TheOperator. But I would like to add the inline keyword to the operator functions. By doing so you could avoid a virtual function call when ever you use this operators.
inline operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
Related
I have a map whose values are an arraylist of different object types (LocalDate, LocalTime, Float, List(Float)).
I would like to transform the map values to a custom data class i have created.
I have tried destructuring the maps value components however i am getting error "Destructuring declaration initializer of type Map.Entry<String, Any> must have a 'component3()' function".
How would i perform this transformation below?
Blockquote
val localDate1 = LocalDate.now()
val localTime1 = LocalTime.now()
val float1 = 1f
val floatList1 = listOf<Float>(1f, 2f, 3f)
val localDate2 = LocalDate.now()
val localTime2 = LocalTime.now()
val float2 = 2f
val floatList2 = listOf<Float>(4f, 5f, 6f)
val myMap = HashMap<String, Any>()
myMap["keyOne"] = arrayListOf<Any>(localDate1, localTime1, float1, floatList1)
myMap["keyTwo"] = arrayListOf<Any>(localDate2, localTime2, float2, floatList2)
val newMap = myMap.mapValues { (date, time, float, floatList) -> /*Here i am getting destructuring error*/
CustomObject(
date,
time,
float,
floatList
)
}
Blockquote
data class CustomObject (
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)
Map.mapValues() takes a lambda whose receiver is the map Entry, not its value. (The method is named for the lambda's results, not its parameter.) So you can't destructure it that way.
There are also typecasting issues: myMap is a map whose values are Any, but you seem to be assuming that each value is actually a List of Any (with the actual elements being of the appropriate types in the appropriate order). So you need to do the necessary casting to tell the compiler what types they are (or at least, what types you assume they are…)
So you could destructure it as a separate step, e.g.:
val newMap = myMap.mapValues { entry ->
val (date, time, float, floatList) = entry.value as List<Any>
CustomObject(
date as LocalDate,
time as LocalTime,
float as Float,
floatList as List<Float>
)
}
Or you could just use the array values directly:
val newMap = (myMap as Map<String, List<Any>>).mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
it.value[3] as List<Float>
)
}
However I think it would be a lot better to avoid having the lists in the first place. You're taking a lot of extra code to create the lists and then convert them — but much of that code is unsafe: each destructuring can fail with an exception (perhaps ArrayIndexOutOfBoundsException) if the list isn't long enough, and each cast can fail with a ClassCastException. Casts are always a code smell, especially unchecked ones like these; Kotlin's type system is powerful enough to express most things you're likely to want, so bypassing it like this tends to indicate that the design can be improved.
So if there's any way you can create your custom objects directly, that's likely to be shorter, safer, faster, and easier to read.
import java.time.LocalDate
import java.time.LocalTime
data class CustomObject(
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)
val myMap = mapOf(
"keyOne" to listOf(LocalDate.now(), LocalTime.now(), 1f, listOf(1f, 2f, 3f)),
"keyTwo" to listOf(LocalDate.now(), LocalTime.now(), 2f, listOf(4f, 5f, 6f))
)
val newMap = myMap.mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
(it.value[3] as List<*>).map { fl -> fl as Float }
)
}
println(newMap)
I am trying to do this
infix fun Int.divBy (_denom: Int): Rational {
var num = this.toBigInteger()
var denom = _denom.toBigInteger()
// reduce result
val result = Rational(num, denom)
result.reduce()
// return
println("Returning: Rational $result")
return result
}
but for Long, Int and BigInteger, and without having to rewrite the same for all of them. I tried this, but I get an error:
infix fun <T> T.divBy (_denom: T): Rational where
T: Int, T:BigInteger, T: Long
{
....
}
So that things like val third = 1 divBy 3 can be done.
Error 1
I believe this is not possible, because there is no interface for "something convertable to BigInteger". Still, you can keep most of your code in a single function receiving BigInteger objects and provide multiple functions for just converting to BigInteger:
infix fun Int.divBy(denom: Int): Rational = toBigInteger() divBy denom.toBigInteger()
infix fun Long.divBy(denom: Long): Rational = toBigInteger() divBy denom.toBigInteger()
inline infix fun BigInteger.divBy (denom: BigInteger): Rational {
// reduce result
val result = Rational(this, denom)
result.reduce()
// return
println("Returning: Rational $result")
return result
}
Edit (#ademord): the final solution looks like this, after cleaning it up:
infix fun Int.divBy(denom: Int): Rational = toBigInteger() divBy denom.toBigInteger()
infix fun Long.divBy(denom: Long): Rational = toBigInteger() divBy denom.toBigInteger()
infix fun BigInteger.divBy (denom: BigInteger): Rational = Rational(this, denom).reduce()
As a side note, it can't be done with generics and upper bounds, because their main purpose is that the code inside the function knows what is the type of T. where clause is used to specify multiple requirements that T needs to satisfy altogether. Upper bounds are ANDed, not ORed. Note that if they would be ORed then we would still have to add runtime type checks and casts, which is exactly what we try to avoid providing upper bounds (thanks #Tenfour04 for pointing this out).
Let's say I have a set of two pojo's like so:
data class Test (
var id : Long? = null
)
data class TestOther (
var id : Long = 0,
var isCool : Boolean = false
}
and then I have an infix function like so:
infix fun <T : Any?> KProperty<T>.equal(rhs : KProperty<T>) = BinaryExpression<Boolean>(this, rhs, EQUALS)
then this works fine as I'd expect:
Test::id equal TestOther::id
but so does this, since T is all types that extend Any?:
Test::id equal TestOther::isCool
Is there anyway to specify generic constraints such that nullable and non nullable types can be compared, but objects of different types cannot without having to specify an overload for every possible concrete type?
It is not possible to do right now. You may follow the issue for more details
https://youtrack.jetbrains.com/issue/KT-13198
I see a workaround here (similar to the one from the issue). The idea is to wrap the KProperty<R> into a wrapper class without variance. As you see, the KProperty type has out R variance, which works against us in the example. You may follow the link for the details on the declaration-side variance in Kotlin
https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance
The workaround works as strict as expected
class KWrapper<R>(val p : KProperty<R>)
infix fun <T : KWrapper<R>, R> T.equal(rhs : T) = false
val <T> KProperty<T>.wrap get() = KWrapper(this)
val a = Test::id.wrap equal TestOther::id.wrap //fails: Long vs Long?
val b = Test::id.wrap equal Test::id.wrap //works
val c = Test::id.wrap equal TestOther::isCool.wrap // fails Long vs Boolean
The downside is that you need to use .wrap extension property (or extension function) for the left and right parameters separately
I'm trying to reduce duplication across some vector types by defining the operators once, but I'm not sure it's possible. This seemed like the most promising approach:
open class VecN<Derived: VecN<Derived>>(val buffer: FloatArray) {
operator fun minus(other: Derived) = Derived(buffer.zip(other.buffer, { a, b -> a - b }).toFloatArray())
operator fun plus(other: Derived) = Derived(buffer.zip(other.buffer, { a, b -> a + b }).toFloatArray())
... many more operators...
}
class Vec2(x: Float, y: Float) : VecN<Vec2>(floatArrayOf(x, y))
class Vec3(x: Float, y: Float, z: Float) : VecN<Vec3>(floatArrayOf(x, y, z))
class Vec4(x: Float, y: Float, z: Float, w: Float) : VecN<Vec4>(floatArrayOf(x, y, z, w))
This gives me "Type parameter Derived cannot be called as function" where I try to construct my Derived return value.
Is it possible to achieve this in Kotlin?
You cannot do that in a straightforward way because, in Kotlin, you can only call a constructor of a concrete type, there's no way to call a constructor of a type parameter. Moreover, Kotlin does not allow passing an array into a function/constructor that expects fixed number of separate values.
However, you can try to achieve that without too much boilerplate using an abstract function, like this:
abstract class VecN<Derived: VecN<Derived>>(val buffer: FloatArray) {
protected abstract fun createNew(buffer: FloatArray): Derived
operator fun minus(other: Derived) =
createNew(buffer.zip(other.buffer, Float::minus).toFloatArray())
// ...
}
Then you have to override this function in each of the derived classes:
class Vec2(x: Float, y: Float) : VecN<Vec2>(floatArrayOf(x, y)) {
override protected fun createNew(buffer: FloatArray) = Vec2(buffer[0], buffer[1])
}
(demo of this code)
I'm curious about what is the suggested way to define member functions in Kotlin. Consider these two member functions:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
These appear to accomplish the same thing, but I found subtle differences.
The val based definition, for instance, seems to be more flexible in some scenarios. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. I found that this does not compile:
val z = g andThen A::f // f is a member function
But if f were defined as a val pointing to the same function, it would compile just fine. To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this:
val fref: KFunction1<Int, Int> = ::f
val gref: (Int) -> Int = g
So one is of type KFunction1<Int, Int>, the other is of type (Int) -> Int. It's easy to see that both represent functions of type Int -> Int.
What is the difference between these two types, and in which cases does it matter? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so:
val z = g andThen A::f.partially1(this)
i.e. I had to partially apply it to this first.
Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?
Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
is effectively equivalent to:
public class A {
private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() {
#Override
public Integer invoke(final Integer integer) {
return 42;
}
};
public int f(final int value) {
return 42;
}
public Function1<Integer, Integer> getG() {
return gref;
}
}
As you can see, the main differences are:
fun f is just a usual method, while val g in fact is a higher-order function that returns another function
val g involves creation of a new class which isn't good if you are targeting Android
val g requires unnecessary boxing and unboxing
val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java
UPDATE:
Regarding the A::f syntax. The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each:
val first = A::f
val second = A::f
Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831. In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3
Here's some code showing how f and g are different when it comes to usage:
fun main(args: Array<String>) {
val a = A()
exe(a.g) // OK
//exe(a.f) // does not compile
exe { a.f(it) } // OK
}
fun exe(p: (Int) -> Int) {
println(p(0))
}
Where f and g are:
fun f(x: Int) = 42
val g = fun(x: Int) = 42
You can see that g is an object that can be used like a lambda, but f cannot. To use f similarly, you have to wrap it in a lambda.