Niko
12/29/2021, 12:27 PM@sample
annotations) I began to wonder:
Almost nothing is as bad as public API being documented wrong: old information, deprecated behaviour, bugs in the code having invalid/unexpected behaviour.
This is mostly about compiler+dokka/kdoc, but would it be feasible to offer an additional @proof path.to.Test.withProofOfBehaviour
attached to kdoc paragraph, that at compile time/in IDE after changes to source would be checked (run using the test runner, like git bisect
) and show the doc with red squiggly if doc proof didn't pass?xxfast
01/03/2022, 11:16 PMfun <T> Foo(bar: T) = Unit
class <T> Bar(foo: T) // wont compile
or
fun Foo<T>(bar: T) = Unit // wont compile
class Bar<T>(foo: T)
Arun
01/12/2022, 11:27 PMRescribet
01/15/2022, 5:02 PMto
method was for.
A method (or pair) to let the compiler automatically wire type conversions:
// I'm not well versed in Kotlin interfaces
interface Convertible<reified A : Any, reified B : Any> {
inline fun from<A>(val it: A): B
inline fun <http://A.to|A.to><B>(): B
}
// or more explicit
convert fun A.B(val it: B): A
data class Point(val x: Int, val y: Int)
/** Creates a Point from an IntArray */
fun Point.from<IntArray>(val it: IntArray) = Point(it[0], it[1])
fun <http://Point.to|Point.to><IntArray>() = intArrayOf(this.x, this.y)
fun draw(point: Point)
draw(Point(0, 1))
draw(listOf(0, 1) -> draw(Point(it[0], it[1]))
Where the compiler will check the existence of a matching conversion and apply it automatically.
Note that it should only be added by developers on types with clear semantics, e.g. String should be avoided in most use cases.
Adding inline classes for more specific behaviour might be helpful (i.e. HumanReadableString
and SerializableString
)
Other use-cases:
• Java interop, <http://java.time.Instant.to|java.time.Instant.to><kotlinx.datetime.Instant>()
• Serialization, ie MyClass.from<String>()
• Automatic API upgrades, <http://V1Object.to|V1Object.to><V2Object>()
hfhbd
01/26/2022, 3:05 PMUnit
or Nothing
.
Usecase: A generic Converter
interface:
interface Converter<Input: Any, Output: Any> {
fun convert(input: Input?): Output?
}
// normal (old) usage
class StringToIntConverter: Converter<String, Int> {
override fun convert(input: String?) = input?.toIntOrNull()
}
But sometime, you need to adopt the usage, because you always return a value unrelated to its inputs or simple log and fail:
class FixedIntConverter(val unrelatedReturnValue: Int): Converter<Nothing, Int> {
override fun convert(input: Nothing?) = unrelatedReturnValue
}
class IgnoreConverter: Converter<Int, Nothing> {
override fun convert(input: Int?) = error("$input")
}
To understand, which Nothing
matches to which generic type, you have to read the interface declaration. With named generics, you don't have to look up the declaration, because it is understandable by its name. This is also very useful, when you can't simple go to the declaration without any tools, eg in the GitHub PR diff view..
class FixedIntConverter(val unrelatedReturnValue: Int): Converter<Input=Nothing, Int> {
override fun convert(input: Nothing?) = unrelatedReturnValue
}
class IgnoreConverter: Converter<Int, Output=Nothing> {
override fun convert(input: Int?) = error("$input")
}
Like named arguments, the same rules and behavior should apply, so naming is optional.smallufo
02/14/2022, 1:53 PMinline class
pool that returns same reference ?
Related link : https://kotlinlang.slack.com/archives/C0B8Q383C/p1644805562640269
Play ground : https://pl.kotl.in/uBVYwgQgO?theme=darcula
I am building a carousel-like Int , naming Three
, which restrict any Int value to 1..3
. That is Three.of(1) == Three.of(4)
, Three.of(0) == Three.of(3)
. I use a prebuild array to simulate caching , hoping they get the same reference
private val array by lazy {
arrayOf(Three(1), Three(2), Three(3))
}
But it seems it doesn't work. When comparing reference ===
, it complains Identity equality for arguments of types Three and Three is forbidden
, which is counter-intuitive .Wyatt Kennedy
02/15/2022, 5:51 AMclass TestInnerClass {
var x : Float = 0f
var y : Float = 0f
}
class TestClass {
val bar : TestInnerClass! // the exclamation point makes it a value property declaration. Initialization rules are the same as type TestInnerClass
var bar2 : TestInnerClass! // ERROR : value types cannot be mutable, since they aren't references
}
fun semanticExamples() {
val foo = TestClass()
foo.bar.x = 6f
val test : TestInnerClass! = foo.bar // FINE: When used on a function local variable, TestInnerClass! is essentially a const ref in c++
// when compiled, all usages of test can be replaced by foo.bar.
val test2 : TestInnerClass = foo.bar // ERROR references of this type can be assigned elsewhere, would allow undefined behavior when foo is destroyed.
val test3 : TestInnerClass? = foo.bar // ERROR same as above as well as not being able to be a nullable reference
val foo2 = TestClass()
foo.bar = foo2.bar // ERROR: value properties cannot be assigned because they are not references and implicit copy constructors do not exist
takesTestInnerClassRef(foo.bar)
functionTypeExample {
foo.bar.x = 6f // the value type can still be referenced because the reference to foo is enclosed.
test.x = 6f // ERROR: cannot be sure that the owner of test hasn't been garbage collected
}
}
// Types TestInnerClass and TestInnerClass? are demotable to TestInnerClass! which is least permissive
// The value type "!" annotates that the reference cannot be assigned to anything.
fun takesTestInnerClassRef(testInnerClass: TestInnerClass!) {
val test : TestInnerClass! = testInnerClass // still valid because they are basically just const refs
val foo2 = TestClass()
foo2.bar = testInnerClass // Error: value properties cannot be assigned to as mentioned above
}
fun functionTypeExample(func: () -> Unit) {
}
Most of these are simple semantic rules in the type system, the only compile issue being able to use indirection in JVM without a reference being managed by the garbage collector, (not sure if it doesn't already support this). This syntax being purely ad-hoc discourages it's use unless someone explicitly needs better control of memory locality for high performance sections.
If you also introduced a value generic, you could also use this for compile time fixed length arrays of value objects should people want them.
// a way to declare value generics, I'm sure this has been discussed elsewhere
// |
// v
class FixedArray<T : Any!, val Length : Int> {
// ...
}
class Test {
var x : Float = 0f
var y : Float = 0f
}
fun valueArrayTest() {
val foo = FixedArray<Test!, 6>()
}
While the semantic rules could apply to all compilation targets, (JS, JVM, Native, etc), you could just provide a warning that value annotations will be ignored in environments where it's simply not possible to enforce, (probably JS). Is any/all of this something that has already been made permanently off the table?janvladimirmostert
02/25/2022, 7:47 AMval foo = """
Hello World from ${firstName} ${lastName}
""".toTemplate()
foo
now contains a StringTemplate which contains a list of variables, firstName and lastName as well as parts of the String: ["Hello World from ", firstName, " ", lastName, "\n"]
Use case 1 on the JVM, SQL queries:
val simpleQuery = """
SELECT a, b, c FROM table WHERE foo = $value1 AND bar = ${value2 + 1}
""".toTemplate()
by doing a toTemplate() and replacing all variables directly with ?, you avoid SQL injection attacks while also simplifying having to track how many ?
you've added to the String and how many values you need to provide (especially when building more complex queries that requires concatenating clauses depending on what filters are switched on).
If a SQL driver now only accepts a StringTemplate instead of a String for a query, there's very little chance of a dodgy value slipping into the SQL query via ${dodogyValue} whereas there's no way currently to stop someone from putting variables directly into a String and potentially opening a SQL Injection attack vector.
Use case 2 that spans JVM + JS:
val html = """
<html>
<head>
...
</head>
<body>
Hello World from $firstName $lastName
</body>
</html>
""".toTemplate()
This now allows me to serve that HTML server-side with firstName and lastName already filled in (great for building sites that requires SEO)
<html>
<head>
...
</head>
<body>
Hello World from Piet Venter
</body>
</html>
but I can also generate a JavaScript ES6 String Template that can be re-used on the web to re-render the DOM if one of those values change client-side:
let template = `<html>
<head>
...
</head>
<body>
Hello World from ${firstName} ${lastName}
</body>
</html>`;
This ES6 template way of doing things is being used by frameworks such as uhtml, lit-html, lit-element and many more and if you're using those with a JVM backend, there's no easy way to get server-side rendering without writing the templates twice, once for backend rendering and once for client rendering.
Isomorphic applications with such ES6 frameworks are only supported with a JavaScript backend; with a String.toTemplate() compile-time helper, it becomes possible to build those templates in a Multiplatform way that can be used for the initial server-side rendering and then re-using those templates client-side.
XSS attacks can also be mitigated by stripping the ${} from the String and allowing the framework to escape anything that could be considered dangerous and using the positions array to insert those strings before rendering it.
val securedString = stringTemplate.mapIndexed { index, part -> if (index.isEven()) part else escape(part.toString()) }.joinToString()
Use case 3: Building re-usable templates that can be used with other services
Something like MailChimp might want you to use %value for variables while SendGrid might want a different format for sending emails.
I can now re-use my StringTemplate and create extension functions to convert these StringTemplates to something that other services might want to use, StringTemplate.toMailChimpFormat() or StringTemplate.toSendGridFormat()
Or if you are sending an email locally where you have all the data, you can just fill in those variables yourself to convert that StringTemplate to a String again.
Suggested data format for StringTemplate:
StringTemplate can be as simple as a value class that contains the String parts and Value parts all mixed together:
@JvmInline
value class StringTemplate(private val value: List<Any?>)
Considerations:
1: Doing """ ... """.trimIndent().toTemplate()
should trim the indent without replacing the template variables after which toTemplate() gets to strip the template variables
2: StringTemplate + StringTemplate should concatenate their individual lists of String parts and Values which would return a new StringTemplate with Strings and Values combined
3: It might be beneficial to adjust Dukat to convert tag-functions from JavaScript into regular Kotlin functions that takes a StringTemplate as param instead of the vararg thing it currently generates that's not really usable in KotlinJS
4: toString() can concatenate all the String parts and values to return a value as if it was never broken up.
5: Other platforms: since StringTemplate is just a List<Any?>, it shouldn't be an issue when interopting with other languages
6: String + StringTemplate should not be allowed and instead it should be encouraged to either explicitly convert String.toTemplate() or StringTemplate.toString() unless somebody explicitly overrides the plus operator to allow this.
7: Shorthand: instead of writing toTemplate() everywhere, maybe there's a nicer way to accomplish this, maybe we can use the Spread Operator here, *" My String Template ${...} "
// converts this to a StringTemplate at compile time
8: Considering that we'll need to distinguish between what is a static String and what is a Value, there are two approaches here
8.1: wrap Strings and Values with some value class (personally I think this is unnecessary)
8.2: When splitting the String, make sure that it's always following the format String, Value, String, Value, String, ...
""" Hello $foo$bar """
would then split to [" Hello ", foo, "", bar, " "]
which means two variables that are next to each other will be separated with an empty string and the beginning and end of the list of values will always be a String even if it's an empty String
9: Value classes would solve the "proper escaping of expressions" ticket, value class HTML(val template: StringTemplate)
, but this can be implemented by each framework separately and should not be part of the standard library.
Inside the value class, the toString can be overwritten which can then call the correct escape function
fun toString() = template.mapIndexed { index, part -> if (index.isEven()) part else htmlEscape(part.toString()) }.joinToString()
val safeHTML: String = HTML("""
<strong>$foo</strong>$bar
""").toString()
EDIT: submitted this in YouTrack
https://youtrack.jetbrains.com/issue/KT-51481Derek Peirce
02/26/2022, 4:27 AMapplyMutation({ x }, { x = it })
I would much rather be able to pass in the property to the inline method, and have the getter and setter be derived from it automatically when inlining:
applyMutation(::x)
but we currently instead end up with terribly inefficient (compared to direct accesses) `KMutableProperty0`s.Mina Racic
02/27/2022, 10:27 AMlhwdev
03/05/2022, 3:34 AMinterface Super {
fun a()
}
And
class Child : Super {
override fun a() { TODO() }
}
If we add new member to Super
, it will cause a compiler error to Child
. What I want is, to have a same behavior(compiler error) even if Child
is abstract class. So I need to explicitly implement all abstract members like:
interface Super {
fun a()
fun b() // new
}
@ExplicitAbstract
abstract class Child : Super {
override fun a() { TODO() }
// if this is absent, would lead to compilation error
override abstract fun b() // if cannot implement here, mark as abstract
}
Note that I didn't think about how to mark it(here, @ExplicitAbstract
) so don't care.
Honestly, this is just an idea and I don't know if it would be useful. What do you think?therealbluepandabear
03/13/2022, 11:25 PMMikhail
03/30/2022, 2:14 PMtoTypedArray
every time i want to translate the list to variable length argument.
For example:
operator fun <T> whatever(list: List<T>): Array<out T> = list.toTypedArray()
stantronic
03/30/2022, 5:20 PMLastExceed
04/01/2022, 6:48 AMinterface I {
fun foo(x: Int)
}
class C : I {
override fun foo(x: Int, y: Int = 0) {}
}
basically sugar for
override fun foo(x: Int) = foo(x, 0)
fun foo(x: Int, y: Int) {}
Mikhail
04/02/2022, 8:54 PMExerosis
04/09/2022, 3:28 PMinterface Foo
class MyClass : private Foo
Unless I'm missing something it's more memory efficient than dedicating a private/protected field to an instance of Foo to be used internally.Michael de Kaste
04/11/2022, 9:31 AM[a..b]
we get with the ..
constructor (and even the until
keyword, which is still a [a..b]
range. They don't need to be progressions.Sam Stone
04/12/2022, 5:39 PMFilterableListAdapter
that keeps track of what the previous search was, whether the user initiated a search or it was from the system, etc. I am going to initialize those values to false/empty string on every single implementor, and it just becomes boilerplate to do so.
If the interface could define them, classes would be much cleaner and focused on what makes them unique.Sam Stone
04/12/2022, 11:05 PMIterable<A>.zip(Iterable<B>)
version similar to Iterable.withIndex()
that returns an Iterable<Pair<A, B>>
instead of what withIndex()
returns (viz. Iterable<Pair<T, Int>
). I called it Iterable.with(Iterable): ZippingIterable
(instead of IndexingIterable
for withIndex()
). It seems like a natural corollary to withIndex()
and a common enough use case (viz. two Iterables, want to zip like zip
but want it lazy like withIndex
). Thoughts?Saiedmomen
04/15/2022, 7:28 AMwhen
has, to more(or all) language control flows?
when (val response = executeRequest()) {
...
}
https://kotlinlang.org/docs/control-flow.html#when-expressiontwisterrob
04/22/2022, 5:11 PM<major>.<minor>.<patch>
form so updates could be automated by scripts (Renovatebot/dependeabot) with ease. The current 1.<major>.<minor><patch>
format is hard to parse and also not even correct since theoretically there was no major breaking change yet (that is if you ignore the deprecated/removed methods, renamed command line flags, incubation/optin features, compiler backend change, Gradle DSL changes, and tons of warnings etc.). Is there a proposal for doing the "Java maneuver" and going from 1.6 to 7.0 next release?ephemient
04/27/2022, 6:32 AMifExpression
be changed from
'if' '(' expression ')' (controlStructureBody | (controlStructureBody? ';'? 'else' (controlStructureBody | ';')) | ';')
to
'if' '(' expression ')' (controlStructureBody | (controlStructureBody? ';'? 'else' (ifExpression | controlStructureBody | ';')) | ';')
or some other equivalent change, to make
if (…) { … } else if (…) { … } else { … } ?: …
parse like
(if (…) { … } else if (…) { … } else { … }) ?: …
rather than the current unintuitive
if (…) { … } else (if (…) { … } else { … } ?: …)
?Rob Elliot
04/28/2022, 10:14 PMcopy
method delegate to an operator fun invoke
on the class's companion object
that took the same parameters but was able to return a more flexible return type.
It's a shame that you have to put all the invariant checking in the data class's init
, and so have to fail it with exceptions.Sebastian Brunner
05/05/2022, 12:22 PM@NonExhaustive
which forces you to define an else
branch even if you cover all enums in your when clause.
See Rust docs: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
Here's their original RFC from 2008: https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.mdBVG
05/06/2022, 2:14 PMsealed interface SealedOf<Type>
object SealedOfString1 : SealedOf<String>
object SealedOfString2 : SealedOf<String>
object SealedOfInt1 : SealedOf<Int>
object SealedOfInt2 : SealedOf<Int>
fun theIssue() {
// Note that variable restricts the values only to SealedOf<String>
var subject : SealedOf<String> = SealedOfString1
// This is the only way to have exhaustive when statement - and it produces a compilation error:
// Incompatible types: SealedOfInt1 and SealedOf<String>
when (subject) {
SealedOfString1 -> TODO()
SealedOfString2 -> TODO()
SealedOfInt1 -> TODO()
SealedOfInt2 -> TODO()
}
// This produces warning or error:
// Non exhaustive 'when' statements on sealed class/interface will be prohibited in 1.7,
// add 'SealedOfInt1', 'SealedOfInt2' branches or 'else' branch instead
when (subject) {
SealedOfString1 -> TODO()
SealedOfString2 -> TODO()
}
}
It'd be nice for compiler to understand that SealedOf<Int>
types cannot be assigned to the variable, and therefore no SealedOfInt1, SealedOfInt2
branches are needed. Writing else
branch resolves the compilation error, but that is not why sealed types are used 🙂xxfast
05/07/2022, 1:23 PMval funtionType: (index: Int) -> Unit
functionType(index = 1) // Named arguments are not allowed for function types
is there a reason why it is not allowed?Mikhail
05/11/2022, 10:19 AMMikhail
05/11/2022, 4:05 PMval (first, second!!) = Pair<String, String?>("not null", "same")
// so I dont have to:
val (first, second) = Pair<String, String?>("not null", "same")
// and then
val secondNotNull = second!!
Mikhail
05/16/2022, 4:03 PM