Kotlin extension properties do not work in Kotlin script - kotlin

var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
val sb = StringBuilder("Kotlin?")
sb.lastChar = '!'
println(sb)
When I run this code with kotlinc -script, the compiler throws:
extensions.kts:3:14: error: unexpected type specification
set(value: Char) {
However, this code works without problems when compiled.
So, does it mean extension properties cannot work in kotlin script mode?

Kotlin scripts just wrap code in main function. And extension properties doesn't work when defined inside functions. I think this is bug, and you can submit it here: https://youtrack.jetbrains.com/issues/KT.

Related

Passing StringBuilder::append as argument in Kotlin

I'm using BIP39 plugin for Java to create a mnemonic.
So I've converted (well mostly IDEA did) this Java code to a function in Kotlin syntax which looks like this:
fun mnemonicBuilder(): String {
val sb = StringBuilder()
val entropy = ByteArray(Words.TWELVE.byteLength())
SecureRandom().nextBytes(entropy)
MnemonicGenerator(English.INSTANCE)
.createMnemonic(entropy, sb::append)
return sb.toString()
}
IntelliJ IDEA keeps telling me that "None of the following functions can be called with the arguments supplied." for sb::append.
My quess is that .createMnemonic requires the second argument to have no return value (given by Target interface) but all appenders return the StringBuilder as a value.
Please, can somebody help me?
Indeed, argument signatures do not match. You can solve it by using lambda instead of the method reference:
MnemonicGenerator(English.INSTANCE)
.createMnemonic(entropy) { sb.append(it) }

Gradle Kotlin Script, trying to assign classpatch via builtin variable

Why this:
val runVersionSplicer by tasks.registering(type = JavaExec::class) {
classpath = sourceSets.main.runtimeClasspath // error
main = "com.concurnas.build.VersionSplicer"
}
returns:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val NamedDomainObjectContainer.runtimeClasspath: NamedDomainObjectProvider defined in org.gradle.kotlin.dsl
While this works fine instead?
val runVersionSplicer by tasks.registering(type = JavaExec::class) {
classpath = sourceSets["main"].runtimeClasspath
main = "com.concurnas.build.VersionSplicer"
}
I know that sourceSets.main is so defined:
val org.gradle.api.tasks.SourceSetContainer.`main`: NamedDomainObjectProvider<org.gradle.api.tasks.SourceSet>
get() = named<org.gradle.api.tasks.SourceSet>("main")
However isn't supposed to replace the sourceSets["main"] construct?
Because one of them is a provider and the other is the object.
sourceSets.main uses NamedDomainObjectCollection.named under the hood as you found out. If you look at the javadoc for named, you see that it returns the following:
A Provider that will return the object when queried. The object may be created and configured at this point, if not already
sourceSets["main"] returns the source set directly, because it uses NamedDomainObjectCollection.getByName instead, whi:
The object with the given name. Never returns null.
So these two are equivalent:
sourceSets.main.get()
sourceSets["main"]
In Groovy the following are equivalent:
sourceSets.main
sourceSets["main"]
sourceSets.getAt("main")
Which are all different syntax of the getAt method implemented by the NamedDomainObjectCollection class. This is why you don't see a lot of get() in Groovy scripts but you see a lot more in Kotlin DSL scripts.

How to get Class<java.lang.Long> in Kotlin?

Writing some querydsl code. In Java I would do like this:
#Test
void countTest() {
NumberPath<Long> cnt = Expressions.numberPath(Long.class, "count");
NumberPath<Long> typeId = Expressions.numberPath(Long.class, "type_id");
List<Long> fetched = sql.select(typeId)
.from(SQLExpressions.select(tGroup.typeId.as(typeId), tGroup.count().as(cnt))
.from(tGroup)
.groupBy(tGroup.typeId))
.where(cnt.gt(100L)).fetch();
System.out.println(fetched);
}
Notice this Long.class in Expressions.numberPath(Long.class, ...)
If I create a .kt file and copy-paste the above Java code, it will be converted by Intellij to:
Expressions.numberPath(Long::class.java, ...).
So the resulting Kotlin code I have is:
val cnt = Expressions.numberPath(Long::class.java, "count")
val typeId = Expressions.numberPath(Long::class.java, "type_id")
val fetched = sql.select(typeId)
.from(SQLExpressions.select(QTGroup.tGroup.typeId.`as`(typeId), QTGroup.tGroup.count().`as`(cnt))
.from(QTGroup.tGroup)
.groupBy(QTGroup.tGroup.typeId))
.where(cnt.gt(100L)).fetch()
println(fetched)
Now when I run the code, I get:
java.lang.IllegalArgumentException: Unsupported target type : long
at com.querydsl.core.util.MathUtils.cast(MathUtils.java:86)
at com.querydsl.core.types.dsl.NumberExpression.cast(NumberExpression.java:178)
at com.querydsl.core.types.dsl.NumberExpression.gt(NumberExpression.java:337)
at project.dao.QuerydslKotlinCountTest.countTest(QuerydslKotlinCountTest.kt:30)
So it's not a Class<java.lang.Long> which I would expect, but some class Class<long> (never seen this before and can not get it programmatically with Class.forName("long")).
So, how do I make this simple piece of code work in Kotlin?
If I replace Long::class.java with java.lang.Long::class.java, the code does not compile:
Error:(27, 104) Kotlin: None of the following functions can be called with the arguments supplied:
public open fun `as`(p0: Path<Long!>!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
public open fun `as`(p0: String!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
The only way I made it work is using a boxed java primitive: java.lang.Long.valueOf(1).javaClass, but it looks ugly.
Try using KClass's javaObjectType property instead of java, e.g.:
1L::class.java // returns long
1L::class.javaObjectType // returns java.lang.Long
From its documentation:
[...] In case of primitive types it returns corresponding wrapper classes.

Run single Kotlin file

I have two Kotlin files in the same folder:
Both files have a trivial main method.
I can right click on helloworld.kt file and select "Run..." from the menu.
There's no such option for the circle.kt file though.
This is my run/debug configuration for helloworld.kt:
I tried to create an equivalent configuration for circle.kt, but it complains that the class com.example.kotlin.learning.CircleKt has no main method:
There's actually no class in that file. But there isn't one in HelloWorld.kt as well, and that works.
Here's the code for helloworld.kt:
package com.example.kotlin.learing
fun main (argomenti: Array<String>) {
println ("SUCA!")
println (saluta ( "mario"))
val vettore : Array<String> = arrayOf("pippo", "pluto", "paperino")
println(vettore [0])
}
fun saluta (chi : String) = ( chi + " antani" )
here's circle.kt:
package com.example.kotlin.learing
fun main() = println("pippuz!")
I realize I am missing something deep here. What is that?
Thanks
If I'm not mistaken, main method without parameters is supported from Kotlin version 1.3-RC. Which version of Kotlin are you using?
If you are using an older version of Kotlin, you should pass an array of Strings as the argument of the main method.

FileHelpers BadUsageException only on compiled F# but not in script

I wrote an fsi script that worked great and wanted to compile it so that I could move it easier. However, when I compiled it, all of a sudden FileHelpers began giving errors.
The following code uses FileHelpers 2.9.9. It is a minimal working example to illustrate the issue, test.fsx:
#r "FileHelpers.dll"
open FileHelpers
[<DelimitedRecord(",")>]
type Type =
val field1 : string
val field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
let readFile<'a> file = seq {
use engine1 = new FileHelperAsyncEngine(typeof<'a>)
use tmp1 = engine1.BeginReadFile(file)
engine1.ReadNext() |> ignore
while engine1.LastRecord <> null do
yield engine1.LastRecord :?> 'a
engine1.ReadNext() |> ignore
}
readFile<Type> "test.csv" |> Seq.iter (printfn "%A")
with the file test.csv as
test1,1
test2,2
test3,3
If I run the code as fsi .\test.fsx it will work fine. However, if I try to compile it with fsc .\test.fsx and run .\test.exe I get the error Unhandled Exception: FileHelpers.BadUsageException: The record class Type needs a constructor with no args (public or private). A work around that works in both scripting and compiled mode is
[<DelimitedRecord(",")>]
type Type () =
[<DefaultValue>]
val mutable field1 : string
[<DefaultValue>]
val mutable field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
Why would it work as a script but not compiled? I would like to keep it immutable if possible. Thanks for any insight!
FSI uses System.Reflection.Emit to compile your F# code on-the-fly. It appears that types generated with System.Reflection.Emit always have at least one constructor (either the default public constructor or an explicitly defined constructor). Thus it isn't easily possible for the code emitted by FSI to exactly imitate the result of the compiled code, which has no constructors at all (neither public nor private).