Hullaballoonatic
09/05/2020, 7:20 PMvar foo = 6
set {
println("setting foo to $it")
field = it
}
var foo = 6
set { num ->
println("this is maybe more tedious")
field = num
}
raulraja
09/13/2020, 1:10 PMfun interface
. Is this just a current limitation or something that would not be supported because of the way SAM interop works in the compiler?
fun interface DelimitedContinuation<A, B> {
suspend operator fun invoke(a: A): B //fails to compile
}
Animesh Sahu
11/01/2020, 10:43 AMval delegatedProperty: Int by anotherObj::intProperty
using reflection? When val delegatedProperty: Int get() = anotherObj.intProperty
works out of the box (getter property without field).
Doesn't the former will have performance penalty due to reflections are involved (KProperty0
as seen by decompiler).Animesh Sahu
11/09/2020, 5:52 PMObject declaration's initialization is thread-safe and done at first access.But the following code
object TestObject {
val a = "hello"
fun b(): Nothing = TODO()
}
decompiles as follows:
public final class TestObject {
@NotNull
private static final String a;
public static final TestObject INSTANCE;
@NotNull
public final String getA() {
return a;
}
@NotNull
public final Void b() {
boolean var1 = false;
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
}
private TestObject() {
}
static {
TestObject var0 = new TestObject();
INSTANCE = var0;
a = "hello";
}
}
Tested with/without property and with/without function declaration. Same behavior static initialization, is the docs outdated?Animesh Sahu
01/15/2021, 4:10 AMsuspend operator fun invoke
in the companion), or probably just wrap the result into a Deferred<>
and call await() every time when needed?natario1
02/12/2021, 11:23 AMNumber
is a class as opposed to an interface, any hints?Quy D X Nguyen
02/19/2021, 8:40 AMAlex Vanyo
03/10/2021, 4:00 AMsealed interface Alpha {
interface A : Alpha
interface B : Alpha
}
sealed class Beta /* : Alpha */ {
object Y : Beta(), Alpha.A
object Z : Beta(), Alpha.B
}
fun Beta.fix(): Alpha = when (this) {
is Beta.Y -> this
is Beta.Z -> this
}
fun first(beta: Beta) = second(beta.fix()) // Could fix() be implicit?
fun second(alpha: Alpha) = when (alpha) {
is Alpha.A -> "A"
is Alpha.B -> "B"
}
All subclasses of Beta
implement one of the subinterfaces of Alpha
, so Beta is Alpha
is always true.
However, Beta
can't directly implement Alpha
without adding more subclasses to Alpha
.
It's possible to make this explicit with something like the Beta.fix()
above, which shows that Beta is Alpha
is always true, but it'd be really cool if that could be done automatically in some way.christophsturm
03/11/2021, 11:18 AMYoussef Shoaib [MOD]
03/18/2021, 7:52 PMcoeffects
? Because I heard that there's some internal discussions about it and the name itself sounds intriguing especially because it'll allegedly solve DI. Obviously it's in a very very very early stage, but if there's a keep or something for it so that the community can discuss it then that'd be quite hlepfulchristophsturm
03/19/2021, 10:20 AMimport kotlin.reflect.KCallable
interface IImpl {
fun method()
fun method(number: Int, name: String)
fun nonOverloadedMethod()
}
fun function(callref: KCallable<*>) {println(callref)}
function(IImpl::nonOverloadedMethod)
function(IImpl::method) // resolution ambiguity
I wonder how a syntax that makes that work could look likemelatonina
03/24/2021, 2:38 PM?.let { }
expressions, in case it has not been discussed before.
Whenever, inside of an expression, a ?
(question mark) is appended to the name of a variable (mutable var
or non mutable val
) of nullable type, the whole expression containing it would evaluate to null
if that variable is null
, otherwise the variable is cast to the respective non-nullable type.
fun f(a: A): B = TODO()
fun g(a: A?): B? = f(a?)
fun h(a: Int?, b: Int): Int? = (a? + b)*2
fun i(a: Int?, b: Int?, c: Int?): Int? = (a? + b?)*c?
I don't know how to call this feature. Maybe "short-circuiting null operator"? As suggested by @uli, this feature is symmetric to the ?.
operator, and would work on all arguments of a function like ?.
does on the this
argument.
Would this syntax cause conflict with other language features? Would you consider its implementations?Derek Peirce
03/27/2021, 6:04 AMval listA = listOf("foo", "bar")
val listB = listOf("alpha", "beta", "gamma")
val firstLetters = listA.map { it.first() } + listB.map { it.first() }
+
works, and it's as concise as can be, so everything is fine, right? Well, it works, but not very efficiently. If we inline the methods, we see that each map
creates its own ArrayList
, and then a third ArrayList
is created with +
to store all of them, which is two more list objects than we actually needed, because +
respected that the individual lists shouldn't be mutated, even though they're never used again beyond this block of code.
Consider a rewritten, more efficient form:
val firstLetters = ArrayList<Char>(listA.size + listB.size).apply {
listA.mapTo(this) { it.first() }
listB.mapTo(this) { it.first() }
}
This creates exactly the single list that we need, no wasted objects. It's also terribly ugly. But what if Kotlin allowed us to use the concise expressiveness of the first functional line with the efficiency of the four lines above? It would have to recognize that map
has a corresponding mapTo
, and then that the results of map
are used only by +
. The second requirement is easy, but the first requires a bit more. What if mapTo
didn't exist as a separate method from map
?
Suppose map
were written as:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
@CollectionBuilder val destination = ArrayList<R>(collectionSizeOrDefault(10))
for (item in this)
destination.add(transform(item))
return destination
}
@CollectionBuilder
would indicate to the compiler that it should build two versions of map
, the one above and a transformed one, `map_collectionBuilder`:
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.map_collectionBuilder(destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
This is, of course, the original mapTo
. As long as the result of add
is never checked, it should be possible for any method that builds its own collection to instead append to an existing one, with the type List
being effectively the default output.
The compiler can then create its own empty list and fill it with each map_collectionBuilder
call automatically. The only remaining problem is how to right-size that list. Maybe it would add the two existing collectionSizeOrDefault
calls together, but that could get tricky to find. Or maybe the map
method would need an annotation to give a hint to this collection-optimization step that this method creates a collection that is exactly as large as the input, @SameSizeOutput
, while others like filter
may indicate that as a ceiling, @SameSizeOrSmallerOutput
. In such a case, perhaps the methods would be inverted, and we write this one method instead:
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(@CollectionBuilder(directName = "map", size = SAME_SIZE) destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
and the corresponding map
would be generated from there.
If it seems strange for the language to specifically optimize +
on collections like this, we've already done it for String
, converting repeated `+`s into `StringBuilder`s, we may as well offer something similar to collections.Rob Elliot
03/30/2021, 3:42 PMzokipirlo
04/08/2021, 1:32 PMprivate fun isGroupChatInfoExpired(groupChatInfo: GroupChatInfo): Boolean {
return Epoch.currentEpoch > (groupChatInfo.epoch ?: 0 + GROUP_CHAT_INFO_VALID_TIME) //epoch is nullable
}
This is always true
. To fix the problem`groupChatInfo.epoch ?: 0` must be in parenthesis like:
private fun isGroupChatInfoExpired(groupChatInfo: GroupChatInfo): Boolean {
return Epoch.currentEpoch > ((groupChatInfo.epoch ?: 0) + GROUP_CHAT_INFO_VALID_TIME) //epoch is nullable
}
Is it possible to show some warning in IntelliJ (Android Studio) that this will possibly have a wrong evaluation?edrd
04/12/2021, 4:15 PMTomasz Krakowiak
04/22/2021, 9:03 AMTomasz Krakowiak
04/22/2021, 9:51 AMPeter
05/12/2021, 8:01 PM// Java approach works of course
fun <T> test1(a: T) = a
fun test1() = test1("World")
// Not allowed, gives an error
fun <T> test2(a: T = "World") = a
nisrulz
05/26/2021, 12:31 PM@OptIn
annotation.
My usecase is that when we start using non-propagating opt-in, lets say internal to library in a multi-module library project, then there is no ability to provide reasoning for why we are @OptIn
. In this particular case the reasoning would be “Internal to main library” for example. The reason can vary though. The actual annotation of say ShinyNewAPI
does have the message via the @RequireOptIn
annotation, like so
@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.")
However this message is for clients who try to use the class marked with the annotation. When writing code for internal modules, the reason to opt in could vary. Maybe it could be like “Donot want to propagate to client code, but want to use code from another sub module”. This can be fixed by adding a code comment, but I was thinking if it could be part of the Annotation somehow, so one can wrap a new annotation with it and then use that, thus not requiring to add the message everytime.
// Function doSomething uses API marked with ShinyNewAPI and is also
// required to opt in. Here, we choose a non-propagating opt-in, because the
// API is used in the function body and it should not concern our clients
@OptIn(ShinyNewAPI::class)
fun doSomething() {
val foo = Foo()
foo.bar()
}
Please follow more in thread.Youssef Shoaib [MOD]
06/08/2021, 3:06 PMTomasz Krakowiak
06/15/2021, 8:09 PMedrd
06/17/2021, 3:46 PMgiso
07/01/2021, 10:10 AMpublic
.
I was wondering how this use case can be fulfilled with Kotlin. Maybe with a compiler plugin, which is only applied to the main module, granting higher visibility? Or more tightly scoped with a method annotation granting only this method a higher visibility?
What are you thoughts, or have I overlooked an already present solution?Tomasz Krakowiak
07/14/2021, 6:59 AMinterface A1
interface A2 : A1
interface B<out T>
interface C1 : B<A1>
interface C2 : C1, B<A2> // Type parameter T of 'B' has inconsistent values: A1, A2
https://pl.kotl.in/xgbmimjy6nwh
07/28/2021, 12:17 AMfirstNotNullOfOrNull
? I have seen an increasing number of these functions appearing and they always strike me as excessive and obtuse - this one in particular still does not make sense to me, even after some soul seeking. It's a combination of firstOrNull
and mapNotNull
, the latter of which is itself a "convenience" for filterNotNull().map { ... }
. Where does it end? Should we have firstNotNullOfOrNullIsInstanceOf
? I feel like I'm missing something here.
To me, seeing this in code explains a lot more conceptually, and I don't see what's "wrong" with it:
data.map { String.toDoubleOrNull(it) }.filterNotNull().firstOrNull()
Oliver.O
08/18/2021, 9:07 PMget()
• get<DateModel>()
Currently, it requires two function definitions to achieve the goal, which might be resolved by introducing a default type for a type parameter as shown in the following example:
package genericMethodDefaultType
open class Model
open class AttributeModel : Model()
data class DateModel(var value: String) : AttributeModel()
val modelCache = mutableMapOf<Int, Model>()
@Suppress("UNCHECKED_CAST")
class ModelReference<SpecificModel : Model>(private val modelId: Int) {
// Variant (1) returning the default type.
fun get() = modelCache[modelId]!! as SpecificModel
// Variant (2) returning a derivative.
@JvmName("getSpecific") // required to disambiguate JVM signatures
fun <MoreSpecificModel : SpecificModel> get() = modelCache[modelId]!! as MoreSpecificModel
// Variant (3) replacing (1) and (2) – currently not valid Kotlin:
// fun <MoreSpecificModel = SpecificModel : SpecificModel> get() = modelCache[modelId]!! as MoreSpecificModel
//
// Note the specification of a default type ` = SpecificModel` which shall be used if type inference cannot
// determine a result.
}
@Suppress("UNUSED_VARIABLE")
fun main() {
val modelId = 42
modelCache[modelId] = DateModel("2021-08-18")
val attributeModelReference = ModelReference<AttributeModel>(modelId)
// Some code would access attribute models like this:
val attributeModel1 = attributeModelReference.get()
// Does not compile if `get()` variant (1) is not defined: Not enough information to infer type variable MoreSpecificModel
// If `get()` variant (1) is not defined, we would have to specify the type redundantly like this:
val attributeModel2 = attributeModelReference.get<AttributeModel>()
// A date view might want to access its model like this:
val dateModel = attributeModelReference.get<DateModel>()
println(attributeModel1)
println(attributeModel2)
println(dateModel)
}
Is this too exotic or do there exist better solutions for the above case?Youssef Shoaib [MOD]
08/22/2021, 2:01 PMtransparent
modifier to it so that, whenever you have a value of that value class, it acts like if it is of the aforementioned type, but you may still access the functions that the value class itself provides. In a way, this is just a way to provide scoped extension functions, which I admit is practically solved by multiple receivers already, but there's some use-cases that don't quite fit that bill. Let me first just start with a simple example that is solved by context receivers:
transparent value class NumericalString private constructor(val underlying: String) {
constructor(value: Int): this(value.toString())
... etc for the rest of the number types
val doubled: String get() = "${underlying.toDouble() * 2}"
}
fun main() {
val fortyTwo = NumericalString(42)
val four = fortyTwo.doubled.lastLetter()
println(four + fortyTwo)
}
fun String.lastLetter() = last()
Re-writing this with context receivers is messy-ish, but it's kind of doable:
interface NumericalStringOperations {
val String.doubled: String
companion object Default: NumericalStringOperations {
override val String.doubled: String get() = this.toDoubleOrNull()?.let { "${it * 2}" } ?: this
}
}
fun main() {
with NumbericalStringOperations:
val fortyTwo = "42"
val four = fortyTwo.doubled.lastLetter()
println(four + fortyTwo)
}
fun String.lastLetter() = last()
The issue is, however, that first of all this is not typesafe, since it has no idea if a String is numerical or not. The second issue is a bit niche, which is that what if you want to shadow a function that already exists:
transparent value class NumericalString private constructor(val underlying: String) {
constructor(value: Int): this(value.toString())
... etc for the rest of the number types
val doubled: String get() = "${underlying.toDouble() * 2}"
// Kind of assuming that no boxing will really happen and that the compiler is smart enough to not force `other` to be boxed
// The point of this is that "42" and "42.0" should be considered equal
override inline fun equals(other: Any?) = if(other is NumericalString) other.underlying.toDouble() == underlying.toDouble() else underlying == other.underlying
fun last(): NumericalString = NumericalStirng(underlying.last())
}
fun main() {
val fortyTwo = NumericalString(42)
val four = fortyTwo.doubled.last()
val doubleFour = NumericalString(4.0)
println("$four == $doubleFour returned ${four == doubleFour}") // Should be true
}
In fact, this absolutely can't be done with context receivers (right now) because @HidesMembers
is limited in its usage to a handful of functions, and even if you do hack around the compiler to allow it to work everywhere (which I did try), it still doesn't resolve the shadowing correctly because call resolution with @HidesMembers
only takes into account normal static extensions and not member extensions. Even if @HidesMembers
was extended properly to handle this, I still believe that making it a first-class citizen in Kotlin gives it more validity, if you will.
So far, the case I've mentioned is basically just a refined type, but this feature can extend beyond that. For example, let's take a detour into functional programming with a simple Either<A,B>
perhaps like this:elect
09/22/2021, 5:23 AMelect
10/04/2021, 7:56 AMelect
10/04/2021, 7:56 AMephemient
10/04/2021, 8:28 AMelect
10/04/2021, 8:29 AM