iex
11/30/2019, 12:14 PMHullaballoonatic
12/03/2019, 9:17 PMjimn
12/05/2019, 12:26 PMLeon K
12/06/2019, 4:51 PMtypealias Person =
{ name :: String
, age :: Int
, mobileNumber :: String
, landlineNumber :: String
, gender :: Gender
}
then this would not contain any information about the mutability of any of the fields.
this is where the rust-inspiration comes in:
Record-mutability would be defined on a per-usage basis.
if you create a person (simplified here):
val person: Person = { name: "Tom", age: 12, gender: Gender.Male } //other fields left out
then this object is deeply immutable. this could be enforced by saying that a record can only store other Record-types or "primitive" types (and maybe a basic set of collections which are guaranteed to be immutable here)
if you wanted to have a mutable version of that person, you would do something like this:
val person: mut#Person = mut#{ name: "Tom", age: 12, gender: Gender.Male } //other fields left out
(this demo-syntax is ugly, i know)
this would mean that all fields of that record (and all fields of potential records stored within it) are mutable, and thus provide a setter (callable via normal =
functions)
turning a mutable record into an immutable one would need to be an explicit operation, because mutable records would be stored like normal classes (by reference), while immutable records would be stored and passed by value, thus be cloned wherever they are passed to.
this approach would have a lot of benefits as well:
- usually, you, the caller, know, if you need something to be mutable. thats why there are loads of classes that have mutable and immutable variants (List
/ MutableList
, etc). this would make it possible to let YOU decide about the mutability your object needs
- functions could guarantee purity in regards to the record by not requiring your arguments to be mutable, thus not being able to mutate them
- you wouldn't need to create builder-classes, because you could just create a mutable version of the record, build your stuff, and then store it as immutable in the end
- as a result of guaranteed deep immutability, the compiler could do heavy optimizations which are currently not possible.
- you could still have mutating methods on the records, by just defining the extension-functions only for mutable versionseduardog3000
12/22/2019, 10:22 PMUnit?
• Is this even useful?Ben Woodworth
12/23/2019, 9:36 AMcomponentN
operator that takes parameters?
For `List`s and `Array`s, the standard library has to define component1
-5
functions, but a component operator with a positional argument could be used if it existed:Slackbot
12/24/2019, 9:01 AMMarc Knaup
12/25/2019, 1:50 PM#file
and #line
in Swift / __FILE__
and __LINE__
in C languages (for the latter see: https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID390). Values should be resolved at compile-time, not at run-time like for exceptions.
That would be great for improving diagnostic logging in Kotlin DSLs, so that the DSL user has a rough idea where an error that has been reported by the DSL library has originated from.
Exceptions won’t help here since in many cases you cannot throw them at the moment that a function has been invoked. Validation may take place at the very end. Also, using stack traces to get that info adds a lot of overhead at runtime and isn’t available in multiplatform/common code.eduardog3000
12/29/2019, 12:53 AMpablisco
01/09/2020, 11:28 PMjdemeulenaere
01/15/2020, 11:48 AM@DslMarker
annotation class MyDsl
interface TopLevelBuilder : MethodsThatShouldBeAvailableAnywhere, TopLevelMethodsOnly
interface MethodsThatShouldBeAvailableAnywhere {
fun doSomethingAnywhere()
}
@MyDsl
interface TopLevelMethodsOnly {
fun doSomethingAtTopLevel()
fun lowerLevel(builder: LowerLevelBuilder.() -> Unit)
}
@MyDsl
interface LowerLevelBuilder {
fun doSomethingAtLowerLevel()
}
fun startDsl(builder: TopLevelBuilder.() -> Unit) {
}
fun main() {
startDsl {
doSomethingAnywhere() // compiles
doSomethingAtTopLevel() // compiles
lowerLevel {
doSomethingAtLowerLevel() // compiles
doSomethingAnywhere() // doesn't compile
this@startDsl.doSomethingAnywhere() // compiles but is not very nice
}
}
}
gregorbg
01/19/2020, 9:31 AMfun <T> List<T>.headWithTail() = firstOrNull() to drop(1)
Obviously the function still needs a better name, but I find myself using this concept rather often when recursively processing list entries or working with data that is presented as a "heading + contents" table.
val (heading, contents) = parsedCsv.headWithTail()
elect
01/29/2020, 11:26 AMwhile (true) { }
might be simplified to while { }
Marc Knaup
02/07/2020, 8:45 AMinline fun <R> Any.toIntOrElse(elseBlock: () -> R): R where Int: R = …
should allow Int
, Int?
, Any
and Any?
as R
.stantronic
02/07/2020, 11:02 AMwhen(someObject){
.hasSomeProperty() -> doSomething
.isBetterThan(someValue) -> doSomethingElse()
else -> doAThirdThing()
}
Where hasSomeProperty()
and .isBetterThan
are either member or extensions functions on someObject
Marc Knaup
02/08/2020, 11:59 AMgetValue()
was called. It would be great if we could access the property name at the operator fun delegateProperty(thisRef: Any?, property: KProperty*) { … }
Currently I have to pass the property name manually because it need to “register” the argument name and type way before the delegate’s getValue()
is called.janvladimirmostert
02/08/2020, 3:29 PMinline class HotelCode(val value: String)
suspend fun listHotels(vararg hotelCode: HotelCode) {
^^^ forbidden vararg type HotelCodeelect
02/12/2020, 5:10 PMjdemeulenaere
02/13/2020, 10:08 AMLeon K
02/14/2020, 1:36 PMlet foo: String = "4".to_string();
let foo: Option<i32> = foo.parse().ok(); // parse foo into the equivalent of Int?
let foo: i32 = foo.unwrap_or(1234); // equivalent of foo ?: 1234
i'd really love this for many reasons:
- it keeps you from having to come up with either unreadable or needlessly complex names (i.e. here: fooString
, fooMaybeInt
, foo
) or
- keeps you from alternatively having to combine everything into a giv method chain (which many times is preferable, in my example it would absolutely be preferable. but there are cases like more complicated in-between-steps where forcing everything into a single method-chain hurts readability badly (-> deep nesting, functions that take the value as an argument instead of as a receiver)
- it would even be great to make this good-practice for nullable-let-stuff (i.e.: foo?.let { foo -> doSomething(foo) }
is currently a warning, and using it
is not a good option when this turns into more complex nested chains or doSomething
is a longer call
some arguments against it i've considered:
# "just use var and mutate"
- No. I do not want to use var where i dont need to dynamically mutate a variable. mutation should only be used when it actually represents mutation, not just to make code more readable in cases like this. also this does not allow for type-changes like in my example
# "This hurts safety by allowing you to accidentally try to use a variable that has been shadowed "
- Maybe, but it would not be a big problem. Most of the time, you'd use this to actually change the types like in my example. this would mean that you will get compiler errors immediately nearly every time. This is still by far the biggest argument against it, as i could see it making things less obvious in some rare edge-cases. but it does not hurt your actual safety as this is NOT mutation, but name-shadowing.
# "This hurts readability, i dont like it"
- this is subjective, but imo, No. First of all, rust get's away with it very well, and it does help rust a lot. (but rust does also work with wrapped types like Option<T> and Result even more than kotlin, so it IS more necessary there)
- While shadowing variables can make for ambiguity on first sight, consider the ways this could help:
- reduce the amount of nested methods and indents by encouraging putting things in seperate lines (one of the biggest reasons people like using method chains is that they don't need to think of names for their intermediate variables)
- reduce the need for mutable variables. this is a big one. Using mutable vars where they are not strictly necessary does hurt your compile-time guarantees a lot more.
- another thing about mutables: sometimes you NEED mutable vars if you work with stuff in loops (most of the time it's better to replace these with map, filter, etc, but there are cases where a simple loop is better). now, after your loop, your variable is still mutable. with this feature, you could then do var foo: Int = 0; for(...){/*change foo*/}; val foo = foo;
and have it be immutable after the loop, again helping your compile-time guarantees.
# "What would happen with nested scopes? wouldn't this be mutation?"
- No. shadowing stays shadowing. if you shadow your variable inside a nested scope, this does not change the original variable.
are there any points i've missed or any big arguments against it?Derek Peirce
02/15/2020, 9:13 PMx
is an `Int`:
val y = x.takeIf { it > 5 } ?: 3
The compiled bytecode becomes:
Integer var2 = x;
int it = ((Number)var2).intValue();
int y = (it > 5 ? var2 : null) != null ? it > 5 ? var2 : null : 3;
This has some undesirable boxing and unboxing, it would be ideal if the compiler could optimize this to the equivalent of:
val y = if (x > 5) x else 3
which compiles in Java to:
int y = x > 5 ? x : 3;
What makes this particularly desirable? Consider the protobuf pattern: https://developers.google.com/protocol-buffers/docs/javatutorial#the-protocol-buffer-api
public boolean hasId();
public int getId();
To avoid boxing, primitives are stored with a default value, and a bitfield to indicate which primitives are truly set. Otherwise, we would ideally have a single method:
inline fun getId(): Int? = if (hasId()) getPrimitiveId() else null
It is inlined so that any boxing elimination could be carried out across method calls. If the compiler could eliminate unnecessary boxing, then this call:
if (proto.hasId()) proto.getId() else calculateDefault()
could be replaced with the more straightforward:
proto.getId()?: calculateDefault()
elect
02/16/2020, 11:29 AMPasha Tsier
02/18/2020, 6:43 AM@comptime
annotation, or even as a Comptime<A> trait. They would act as normal variables but would be evaluated at compile time instead of at runtime. This would allow for very powerful patterns - doing things like compile time bounds or units of measure checking. It could even allow us to make the nullibility checking and elvis operators part of the standard library written in Kotlin, instead of being part of the compiler itselfwcaokaze
02/25/2020, 8:53 AMsuppress keyword
for @Deprecated
?
e.g.
@Depreated("message", suppressKeyword = "FOO")
fun foo() {
}
fun bar() {
@Suppress("FOO")
foo()
}
Marc Knaup
02/26/2020, 9:40 AMinterface Foo {
fun foo()
}
interface FooDecorator : Foo by decorated {
val decorated: Foo
}
jimn
03/09/2020, 7:44 AMMarc Knaup
03/11/2020, 2:02 PMFoo.invoke()
rather than treating it as a trailing lambda since none is expected.
fun main() {
makeFoo() { // Too many arguments for public fun makeFoo(): Foo defined in root package in file main.kt
42
}
}
fun makeFoo() = Foo()
class Foo {
operator fun invoke(arg: () -> Int) {
println(arg())
}
}
elect
04/02/2020, 9:16 AMpablisco
04/02/2020, 4:59 PMLastExceed
04/04/2020, 1:16 PMLastExceed
04/04/2020, 1:16 PMilya.gorbunov
04/04/2020, 6:30 PM