Johannes Zick
04/06/2020, 4:30 PMval o = Option.just(1)
val i: Int = when (o) {
is Some -> {
val (value) = o
value
}
else -> 0
}
val i: Int = when (o) {
is Some(value) -> value
else -> 0
}
This should be exactly equivalent, it could even result in exactly the same bytecode. It's easy to provide conversions in the IDE, one way or another. And it could give a hint if people really use this or if it's just a reflex to want everything others have (not even sure about myself, if we are being honest here).
It could be extended in the future, to also do type checks on destructured values (great for Pair), or other checks. Or not. By tying it to the is
keyword for now, it's still open if this syntax will be extended, or dropped again, or will be replaced by something more powerful with a different keyword.hnOsmium0x0
04/21/2020, 9:52 PMfunction() { args ->
// Blah
}
and (optional) Java style lambdas for currying as presented below
function(curried -> args -> {
// Blah
})
Because in Kotlin's syntax, curried lambdas are really ugly
function() { curried ->
{ args ->
// Blah
}
}
Alternatively I think this is acceptable too, if we don't want to sacrifice too much Kotlin's syntax uniformity
function() { curried -> args ->
// Blah
}
LastExceed
04/28/2020, 11:22 AMval x = ::main
instead of just
val x = main
to store a function in a variable?LastExceed
04/29/2020, 9:31 AMelse
is not needed here? right now i HAVE to put an else
, otherwise it wont compileLastExceed
04/29/2020, 4:15 PMLastExceed
04/30/2020, 12:50 PMLastExceed
05/01/2020, 12:35 PMDerek Peirce
05/02/2020, 2:22 AMfilter
followed by a map
on a list, they have two FP-style options that both have drawbacks:
items.filter { ... }.map { ... }
requires two separate loops and creates an intermediate list after the filter
step.
items.asSequence().filter { ... }.map { ... }
creates two Sequence
objects and cannot inline its lambdas.
Both of these fall well short of the optimal mutableListOf<T>().apply { items.forEach { item -> if (...) add(...) } }
performance-wise, but that code is also not nearly as easy to write, read, or debug.
One of the goals of a language should be to minimize scenarios where programmers must choose between the code that is most efficient and the code that looks the best. To that end, I propose that we create an inlinable version of Sequence
, or modify the existing class, which will require the ability to create an inline class that contain multiple properties, including lambdas that can then be inlined. For example:
inline interface Stream<T> { // inline here means that all implementations must be inline, and it should be possible to determine the implementation of any given Stream at compile-time
fun forEach(operation: (T) -> Unit)
}
inline class IterableStream<T>(private val iterable: Iterable<T>): Stream<T> {
override fun forEach(operation: (T) -> Boolean) {
iterable.forEach { if (!operation(it)) return }
}
}
inline class FilteringStream<T>(private val sourceStream: Stream<T>, private inline val predicate: (T) -> Boolean): Stream<T> {
override fun forEach(operation: (T) -> Boolean) {
sourceStream.forEach { item: T ->
if (predicate(item)) {
operation(item)
} else {
true
}
}
}
}
inline class TransformingStream<T, R>(private val sourceStream: Stream<T>, private inline val transformer: (T) -> R): Stream<R> {
override fun forEach(operation: (R) -> Boolean) {
sourceStream.forEach { item: T ->
operation(transformer(item))
}
}
}
inline fun <T> Iterable<T>.asStream() = IterableStream(this)
inline fun <T> Stream<T>.filter(predicate: (T) -> Boolean) = FilteringStream(this, predicate)
inline fun <T, R> Stream<T>.map(transformer: (T) -> R) = TransformingStream(this, transformer)
inline fun <T> Stream<T>.toList() {
val list = ArrayList<T>()
forEach { item -> list.add(item) }
}
After everything is inlined, it would turn this:
val processedItemNames = items
.asStream()
.filter { it.isProcessed }
.map { it.name }
.toList()
into this:
val processedItemNames = ArrayList<String>()
items.forEach { item -> if (item.isProcessed) processedItemNames.add(item.name) }
This would come with the limitation that you can't pass Stream
as arguments to a function or return them from a function, unless those functions are inlined, but I don't think that would pose much of a limitation on their use in practice. Some operators like zip
on two `Stream`s would also be tricky, though zipping a Stream
with an Iterable
or Sequence
would be very doable.
When I brought this up on the discussion board, the primary concern was that it's not enough of a performance issue, so I ran some benchmarks. Not only is the optimal code significantly faster than the other two options, using sequences in my example code was actually significantly slower than using lists!
https://discuss.kotlinlang.org/t/sequences-and-inlined-lambdas/17228/11
There's also the issue of lambdas creating more bytecode, especially on Java 7, which is problematic for Android, a large motivation of this proposal is to eliminate that bytecode by making these lambdas able to be inlined.elect
05/04/2020, 9:04 AM_
for function parameters which are unused in the given fun
(but mandatory to match a specific signature type)Kirill Shepelev
05/04/2020, 7:12 PMinterface Animal
class Dog : Animal
class Cat : Animal
fun randomAnimal(callback: (Animal) -> Unit) {
callback.invoke(Dog())
}
fun main() {
// Directly casting parameter to specific type when
// declaring lambda parameters
randomAnimal { animal as Dog -> // pay your attention. Here we cast Animal to Dog
// Do something with dog...
}
// Safe cast sample
randomAnimal { animal as? Dog -> // safe cast Animal to Dog
animal?.let {
// Do something with dog...
}
}
}
Marc Knaup
05/06/2020, 4:55 AMinvoke
or put it into parenthesis. Neither make for a good DSL.
At the moment to work around that limitation I always have to add two functions:
• one that returns a lambda
• one that accepts a trailing lambda for immediate invocation and returns Unit
pablisco
05/07/2020, 3:17 PMfun doSomething(() -> Unit)
and
fun doSomething(suspend () -> Unit)
I understand that, at least on JVM implementations, this is not possible since both () -> Unit
and suspend () -> Unit
are the “same” because of type erasure. Both are instances of Function0
However, it could be useful when, as an API provider, we want to provide the same api for both without having to use namespacing (like adding a prefix or a postfix) 🙂
Same goes with non coroutine examples like if we want to have overload of operations with generics. Like doSomething(intList: List<Int>)
and doSomething(stringList: List<String>)
If not currently, is there a plans to add this as a language feature? Maybe with namespacing them at compile time.Nico
05/08/2020, 12:21 PMZac Sweers
05/13/2020, 7:33 PMsealed typealias Pet = Dog | Cat | String
• Keywords already exist. RHS expression syntax would need a little work but it could work
• At runtime they’re all just objects getting casted, but there’s plenty of precedent for this (generics)
• Self-contained in the alias expression
• Doesn’t allow anonymous expressions like typescript or similar languages do, which I think is a net positive
• Can be compiler-checked just like sealed classes but without requiring inheritance. Instance check just results in a smart cast like sealed classes or other instance checks do today
when (somePet) {
is Dog ->
is Cat ->
is String ->
}
elect
05/22/2020, 3:04 PMemptyMutableMap()
?vngantk
05/27/2020, 2:05 PMlouiscad
05/27/2020, 3:47 PMMarc Knaup
05/28/2020, 10:07 AMopen class Generic<T: Any> private constructor(
private val type: KType
) {
inline <reified T> constructor():
this(type = typeOf<T>())
}
Does anyone else have good use cases for this?elect
05/29/2020, 8:35 AMAny
to overwrite a default toBoolean()
.
This would by default evaluate true
, for not nullable objects. (https://www.concurnas.com/docs/controlStatements.html#toboolean)
class MyCounter(var cnt: Int = 0) {
override toBoolean() = cnt > 0
}
var counter: MyCounter? = null
if(counter) // -> false
...
counter = MyCounter()
if(counter) -> false
..
counter.cnt++
if(counter) -> true
..
Ben Woodworth
05/29/2020, 7:44 PMequals()
, hashCode()
, and toString()
were removed from the Any
class? (And maybe moved into an interface like Data
, or just left to be added as operator functions)
For a majority of the classes I write, these methods aren't implemented, and it would never make sense for them to be used. The default implementation of equals()
only checks for identity, so === should be used instead in all cases, and toString()
returns a practically useless String like "MyClass@6ea12c19"
. I couldn't think of a good reason for them to be included in Any
, and Googling around I couldn't find anything on the thought process behind including them. (I am still curious to know if there was a reason)
For interop with the JVM, they could be treated the same as the java.lang.Object
class's ::finalize
, ::clone
, etc. methods, which weren't carried over to Kotlin's Any
. (but can still be implemented without being marked with override
)
I don't think bytecode compatibility would be a problem, since the methods still exist on the JVM. Existing usages of Any.equals(...)
on classes that don't implement equals
could still compile, but with a warning that ===
should be used. The same for Any.toString()
, with a warning saying that the value might not be useful.
For code compatibility of existing usages, extension functions could be added to the standard library, similar to this for `toString()`:
fun Data?.toString() = this?.toString() ?: "null"
@Deprecated("Does not have a useful String representation")
fun Any.toString() = (this as java.lang.Object).toString()
@Deprecated("Does not have a useful String representation")
fun Any?.toString() = this?.toString() ?: "null"
The only incompatible thing I can think of is the override modifier on these functions in existing classes. Allowing override
on them but giving a warning that it's not needed could suffice.efemoney
06/03/2020, 1:17 AMval Application.cronUsername by this.environment.config
this
will refer to the receiver ie the Application
. Is there any limitation I’m missing why its not possible right now?GarouDan
06/10/2020, 11:50 PM!
(in the end), /.
, @
, ->
and also create lists in this way {1, "2", a}
, instead of listOf(1, "2", a)
.
I’d like to mimic as much as possible the Mathematica language in Kotlin. A valid Mathematica code is:
N[
D[
Normal[
Series[
Integrate[
Zeta[x],
x
],
{x, 0, 6}
]
],
{x, 2}
] /.x -> 15,
10
]
and in Kotlin, I’d like to run something like this:
val x = "x"
n(
d(
normal(
series(
integrate(
zeta(x),
x
),
{x, 0, 6}
)
),
{x, 2}
) /. x -> 15,
10
)!
Where the n
, d
, normal
, etc., objects have a special toString
method, and /.
binary operator would do something with the previous and next argument and the ->
would be another binary operator. In some way, these binary operators are similar to +
and -
binary operators.
At the moment the following works in Kotlin:
val x = "x"
!n(
d(
normal(
series(
integrate(
zeta(x),
x
),
listOf(x, 0, 6)
)
),
listOf(x, 2)
) + "/. $x -> 15",
10
)
Also, a lot of different operators would be hopefully needed since it is used in this grammar, like the @
, @@
, etc.
So the proposal is have the possibility to create the /.
, ->
, @
, @@
and other operators (to mimic the grammar) and also create lists in this way {1, "2", a}
, instead of using listOf(1, "2", a)
.jaqxues
06/29/2020, 5:03 PMconstexpr
So I want to make my App a bit harder to reverse engineer. There are only a few critical points that are actually important, in this case `String`s. So what I would love to do, just to make it a bit harder (I do know very well that it is still absolutely possible to get the strings), I want to encrypt the `String`s into encrypted `ByteArray`s
Kotlin has everything in place to allow for an extremely nice implementation
val encryptedStringExample by encryptedString { "Some_Secret" }
The Delegate uses a constexpr
to go from the String to an encrypted ByteArray and stores it as field. Hence the String itself cannot be found in the actual compiled code. The decrypted value is just the getValue operator of the delegate.
println(encryptedStringExample) // Some_Secret
However, Kotlin does not yet support constexpr
and the "Some_Secret String would always be found in the (Jvm) Bytecode, even with all fancy inlining etc.
So my question / request is, is there any news on this? (https://youtrack.jetbrains.com/issue/KT-14652). Or do you have any other ways to offer to achieve this or something similar?
Of course I do understand that the key would have to be final to allow to be used for a constexpr (so it really just is to make it harder to find the string in the first place), but this could be useful for some other use cases toojanvladimirmostert
07/05/2020, 12:45 PMAny?
, treat a non-valid expression on its last line as Unit instead of complaining that non-valid-expression is not Any?
See the example below:
inline fun foo(action: String = "Unknown", f: (bar: String) -> Any?): String {
f("TESTING")
return "DONE"
}
This currently works
fun doSomething() {
var k : Int = 0;
foo(action = "DoSomething") {
// Empty lambda's return type seen as Unit
}
}
This too works
fun doSomething() {
var k : Int = 0;
foo(action = "DoSomething") {
k++ // return type seen as Int
}
}
This doesn't work
fun doSomething() {
var k : Int = 0;
foo(action = "DoSomething") {
k = k + 1 // <<--- "Expected a value of type Any?"
}
}
Workaround
fun doSomething() {
var k : Int = 0;
foo(action = "DoSomething") {
k = k + 1
"Garbage value to please compiler" // now it compiles fine, return type seen as String
}
}
Also see the comment on YouTrack
Concerning Unit, 6.6.3 of spec says - "...This type may be used as a return type of any function that does not return any meaningful value." I think lambdas and blocks whose last statement is an assignment qualify as not returning any meaningful value.
https://youtrack.jetbrains.com/issue/KT-20215Zac Sweers
07/19/2020, 6:22 AMpablisco
07/28/2020, 9:57 AMmandeep
08/04/2020, 8:25 PMList
of some type. For example:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(val b: List<Base>) : Base by b
// or maybe use varargs
class Derived(vararg val b: Base) : Base by b
The Derived
class would just iterate over the list and invoke whatever is being overridden.
This is really useful in scenarios when you can only register a single instance as a callback to somethingMarc Knaup
08/07/2020, 6:57 PMpublic
.
For example I often use factory methods for classes and sometimes forget to make the constructors of these classes internal
, which is unexpected API surface.Ruckus
08/08/2020, 9:08 PMinline fun <reified T: Application> launch() = Application.launch(T::class.java)
which can be used like launch<MyApp>()
. It would be pretty slick if you could instead do something like MyApp.lauch()
that would do the exact same thing.
This is a very minor thing with very little benefit over looks, but I figured I'd suggest it.
Before any one suggests it, no, companion objects won't solve the problem as this needs to be generic.Marc Knaup
08/09/2020, 5:40 PM@LowPriorityInOverloadResolution
should be public. It’s so useful to resolve ambiguities 🙂Marc Knaup
08/09/2020, 5:40 PM@LowPriorityInOverloadResolution
should be public. It’s so useful to resolve ambiguities 🙂ilya.gorbunov
08/12/2020, 3:39 PMLowPriorityInOverloadResolution
may look as a last-resort hammer to solve an ambiguity, but it's inherently limited because it only helps if there are no more than two conflicting overloads.
It's better to find a way to resolve such conflict by means of existing language features than to manually hack the overload resolution, see for example this situation: https://youtrack.jetbrains.com/issue/KT-16255#focus=Comments-27-1904640.0-0
Please report your cases when you wanted to use this annotation, so we could consider it on case-by-case basis.
Meanwhile, we have some plans of making such annotations public, but requiring an explicit opt-in.Marc Knaup
08/12/2020, 4:06 PM