I have the following class:
class SymbolTable(){
var map = mutableMapOf<String, Entry>()
var kindCounter = mutableMapOf<String, Int>()
fun define(name:String, kind:String, type:String){
if(kindCounter[kind]==0){
kindCounter[kind]=0
}
var index = 1
map[name]= Entry(type, kind, index)
kindCounter[kind]=kindCounter[kind]!!.plus(1)
}
class Entry looks like this:
class Entry(var type:String, var kind:String, var index:Int)
Main:
fun main(args:Array<String>){
var example = SymbolTable()
example.define("ex1", "ex1", "ex1")
example.define("ex2", "ex2", "ex2")
}
When I run the program and try using the "define" function I get the following error:
Exception in thread "main" kotlin.KotlinNullPointerException
at SymbolTable.define(SymbolTable.kt:21)
at SymbolTableKt.main(SymbolTable.kt:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
I assume the problem has something to do with how I create a new symbolTable class, but seeing as Kotlin doesn't have "new" I don't know how to avoid the null pointer exception.
if(kindCounter[kind]==0){
kindCounter[kind]=0
}
This doesn't make much sense: you test if the value is 0, and if it is, you set it to 0. So it's basically a noop.
What you want is to test if the value is null:
if (kindCounter[kind] == null) {
kindCounter[kind] = 0
}
You could also avoid using the dangerous !! operator by saving the value into a variable.
And you should really use val rather than var: all of your fields shouldn't be mutable:
class SymbolTable() {
val map = mutableMapOf<String, Entry>()
val kindCounter = mutableMapOf<String, Int>()
fun define(name: String, kind: String, type: String) {
val count = kindCounter[kind] ?: 0
map[name] = Entry(type, kind, 1)
kindCounter[kind] = count + 1
}
}
class Entry(val type: String, val kind: String, val index: Int)
fun main(args:Array<String>) {
val example = SymbolTable()
example.define("ex1", "ex1", "ex1")
example.define("ex2", "ex2", "ex2")
}
Related
I have the following dataclasses:
data class JsonNpc(
val name: String,
val neighbours: JsonPreferences
)
data class JsonPreferences(
val loves: List<String>,
val hates: List<String>
)
I have a list of these, and they reference each other through strings like:
[
JsonNpc(
"first",
JsonPreferences(
listOf("second"),
listOf()
)
),
JsonNpc(
"second",
JsonPreferences(
listOf(),
listOf("first")
)
)
]
note that a likes b does not mean b likes a
I also have the Dataclasses
data class Npc(
val name: String,
val neighbours: NeighbourPreferences,
)
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
And I want to convert the String reference types to the normal reference types.
What I have tried:
recursively creating the npcs (and excluding any that are already in the chain, as that would lead to infinite recursion):
Does not work, as the Npc can not be fully created and the List is immutable (I dont want it to be mutable)
I have managed to find a way to do this. It did not work with Npc as a data class, as I needed a real constructor
fun parseNpcs(map: Map<String, JsonNpc>): Map<String, Npc> {
val resultMap: MutableMap<String, Npc> = mutableMapOf()
for (value in map.values) {
if(resultMap.containsKey(value.name))
continue
Npc(value, map, resultMap)
}
return resultMap
}
class Npc(jsonNpc: JsonNpc, infoList: Map<String, JsonNpc>, resultMap: MutableMap<String, Npc>) {
val name: String
val neighbourPreferences: NeighbourPreferences
init {
this.name = jsonNpc.name
resultMap[name] = this
val lovesNpc = jsonNpc.neighbours.loves.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
val hatesNpc = jsonNpc.neighbours.hates.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
this.neighbourPreferences = NeighbourPreferences(
lovesNpc, hatesNpc
)
}
}
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
checking in the debugger, the people carry the same references for each Neighbour, so the Guide is always one Npc instance.
I'm trying to pass data class to the service-proxy of Vert.x like this:
data class Entity(val field: String)
#ProxyGen
#VertxGen
public interface DatabaseService {
DatabaseService createEntity(Entity entity, Handler<AsyncResult<Void>> resultHandler);
}
However, the service-proxy requires a DataObject as the parameter type.
Below are what I've tried so far.
First, I rewrite the data class as:
#DataObject
data class Entity(val field: String) {
constructor(json: JsonObject) : this(
json.getString("field")
)
fun toJson(): JsonObject = JsonObject.mapFrom(this)
}
Although this works, the code is redundant, so I tried the kapt with the following generator:
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(ProxyDataObject::class.java).forEach { el ->
val className = el.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(el).toString()
val filename = "Proxy$className"
val classBuilder = TypeSpec.classBuilder(filename)
val primaryConstructorBuilder = FunSpec.constructorBuilder()
val secondaryConstructorBuilder = FunSpec.constructorBuilder().addParameter("json", JsonObject::class)
val secondaryConstructorCodeBlocks = mutableListOf<CodeBlock>()
el.enclosedElements.forEach {
if (it.kind == ElementKind.FIELD) {
val name = it.simpleName.toString()
val kClass = getClass(it) // get the corresponding Kotlin class
val jsonTypeName = getJsonTypeName(it) // get the corresponding type name in methods of JsonObject
classBuilder.addProperty(PropertySpec.builder(name, kClass).initializer(name).build())
primaryConstructorBuilder.addParameter(name, kClass)
secondaryConstructorCodeBlocks.add(CodeBlock.of("json.get$jsonTypeName(\"$name\")"))
}
}
secondaryConstructorBuilder.callThisConstructor(secondaryConstructorCodeBlocks)
classBuilder
.addAnnotation(DataObject::class)
.addModifiers(KModifier.DATA)
.primaryConstructor(primaryConstructorBuilder.build())
.addFunction(secondaryConstructorBuilder.build())
.addFunction(
FunSpec.builder("toJson").returns(JsonObject::class).addStatement("return JsonObject.mapFrom(this)").build()
)
val generatedFile = FileSpec.builder(pack, filename).addType(classBuilder.build()).build()
generatedFile.writeTo(processingEnv.filer)
}
return true
}
Then I can get the correct generated file by simply writing the original data class, but when I execute the building after cleaning, I still get the following error:
Could not generate model for DatabaseService#createEntity(ProxyEntity,io.vertx.core.Handler<io.vertx.core.AsyncResult<java.lang.Void>>): type ProxyEntity is not legal for use for a parameter in proxy
It seems that the generated annotation #DataObject is not processed.
So what should I do? Is there a better solution?
how can I set properties of a dataclass by its name. For example, I have a raw HTTP GET response
propA=valueA
propB=valueB
and a data class in Kotlin
data class Test(var propA: String = "", var propB: String = ""){}
in my code i have an function that splits the response to a key value array
val test: Test = Test()
rawResp?.split('\n')?.forEach { item: String ->
run {
val keyValue = item.split('=')
TODO
}
}
In JavaScript I can do the following
response.split('\n').forEach(item => {
let keyValue = item.split('=');
this.test[keyValue[0]] = keyValue[1];
});
Is there a similar way in Kotlin?
You cannot readily do this in Kotlin the same way you would in JavaScript (unless you are prepared to handle reflection yourself), but there is a possibility of using a Kotlin feature called Delegated Properties (particularly, a use case Storing Properties in a Map of that feature).
Here is an example specific to code in your original question:
class Test(private val map: Map<String, String>) {
val propA: String by map
val propB: String by map
override fun toString() = "${javaClass.simpleName}(propA=$propA,propB=$propB)"
}
fun main() {
val rawResp: String? = """
propA=valueA
propB=valueB
""".trimIndent()
val props = rawResp?.split('\n')?.map { item ->
val (key, value) = item.split('=')
key to value
}?.toMap() ?: emptyMap()
val test = Test(props)
println("Property 'propA' of test is: ${test.propA}")
println("Or using toString: $test")
}
This outputs:
Property 'propA' of test is: valueA
Or using toString: Test(propA=valueA,propB=valueB)
Unfortunately, you cannot use data classes with property delegation the way you would expect, so you have to 'pay the price' and define the overridden methods (toString, equals, hashCode) on your own if you need them.
By the question, it was not clear for me if each line represents a Test instance or not. So
If not.
fun parse(rawResp: String): Test = rawResp.split("\n").flatMap { it.split("=") }.let { Test(it[0], it[1]) }
If yes.
fun parse(rawResp: String): List<Test> = rawResp.split("\n").map { it.split("=") }.map { Test(it[0], it[1]) }
For null safe alternative you can use nullableString.orEmpty()...
There are many examples how to use init during class construction, but they are mostly without using actual class properties.
What I need to do is init a class property from database on object init. So far I have this:
class MyObj constructor(val id: Long){
var data: MutableMap? = null
init {
data = db.find(id) // more like pseudocode, db fetch is done and result assigned to data property
}
}
But it seems a little overcomplicated to me. Is there any better, more elegant way to do this ?
You can initialize the property directly in the class body:
class MyObj(val id: Long) {
val data: MutableMap = db.find(id)
}
Then you won't need to declare it as a nullable type, and may be able to use val instead of var. (I've also removed the constructor keyword which is redundant.).
You can just write:
class MyObj constructor(val id: Long){
val data: Map<String, String> = mapOf()
}
It's equivalent to:
class MyObj constructor(val id: Long){
val data: Map<String, String>
init {
data = mapOf()
}
}
Init block is useful if you need introduce some logic for your object initialization, like error checking etc. Ofc you can do it with or without init block. But remember what is more readable:
class MyObj constructor(val id: Long){
val data: Map<String, String> = if (id == 0L) {
mapOf(Pair("", ""))
} else {
throw IllegalStateException()
}
} // without init
class MyObj constructor(val id: Long){
val data: Map<String, String>
init {
data = if (id == 0L) {
mapOf(Pair("", ""))
} else {
throw IllegalStateException()
}
}
} // with init
You can also init this value using lazy (what means property will be initialized as function, but value will be assigned when you first ask for it, not when object is created). I guess taking value from db can be quite long, so it may be useful:
val data: Map<String, String> by lazy { mapOf() }
I have the following simplied definition of an addition operation over a field:
import inox._
import inox.trees.{forall => _, _}
import inox.trees.dsl._
object Field {
val element = FreshIdentifier("element")
val zero = FreshIdentifier("zero")
val one = FreshIdentifier("one")
val elementADT = mkSort(element)()(Seq(zero, one))
val zeroADT = mkConstructor(zero)()(Some(element)) {_ => Seq()}
val oneADT = mkConstructor(one)()(Some(element)) {_ => Seq()}
val addID = FreshIdentifier("add")
val addFunction = mkFunDef(addID)("element") { case Seq(eT) =>
val args: Seq[ValDef] = Seq("f1" :: eT, "f2" :: eT)
val retType: Type = eT
val body: Seq[Variable] => Expr = { case Seq(f1,f2) =>
//do the addition for this field
f1 //do something better...
}
(args, retType, body)
}
//-------Helper functions for arithmetic operations and zero element of field----------------
implicit class ExprOperands(private val lhs: Expr) extends AnyVal{
def +(rhs: Expr): Expr = E(addID)(T(element)())(lhs, rhs)
}
}
I'd like this operation to be used with infix notation and the current solution that I find to do so in Scala is given here. So that's why I'm including the implicit class at the bottom.
Say now I want to use this definition of addition:
import inox._
import inox.trees.{forall => _, _}
import inox.trees.dsl._
import welder._
object Curve{
val affinePoint = FreshIdentifier("affinePoint")
val infinitePoint = FreshIdentifier("infinitePoint")
val finitePoint = FreshIdentifier("finitePoint")
val first = FreshIdentifier("first")
val second = FreshIdentifier("second")
val affinePointADT = mkSort(affinePoint)("F")(Seq(infinitePoint,finitePoint))
val infiniteADT = mkConstructor(infinitePoint)("F")(Some(affinePoint))(_ => Seq())
val finiteADT = mkConstructor(finitePoint)("F")(Some(affinePoint)){ case Seq(fT) =>
Seq(ValDef(first, fT), ValDef(second, fT))
}
val F = T(Field.element)()
val affine = T(affinePoint)(F)
val infinite = T(infinitePoint)(F)
val finite = T(finitePoint)(F)
val onCurveID = FreshIdentifier("onCurve")
val onCurveFunction = mkFunDef(onCurveID)() { case Seq() =>
val args: Seq[ValDef] = Seq("p" :: affine, "a" :: F, "b" :: F)
val retType: Type = BooleanType
val body: Seq[Variable] => Expr = { case Seq(p,a,b) =>
if_(p.isInstOf(finite)){
val x: Expr = p.asInstOf(finite).getField(first)
val y: Expr = p.asInstOf(finite).getField(second)
x === y+y
} else_ {
BooleanLiteral(true)
}
}
(args, retType, body)
}
//---------------------------Registering elements-----------------------------------
val symbols = NoSymbols
.withADTs(Seq(affinePointADT,
infiniteADT,
finiteADT,
Field.zeroADT,
Field.oneADT,
Field.elementADT))
.withFunctions(Seq(Field.addFunction,
onCurveFunction))
val program = InoxProgram(Context.empty, symbols)
val theory = theoryOf(program)
import theory._
val expr = (E(BigInt(1)) + E(BigInt(1))) === E(BigInt(2))
val theorem: Theorem = prove(expr)
}
This won't compile giving the following error:
java.lang.ExceptionInInitializerError
at Main$.main(Main.scala:4)
at Main.main(Main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
Caused by: inox.ast.TypeOps$TypeErrorException: Type error: if (p.isInstanceOf[finitePoint[element]]) {
p.asInstanceOf[finitePoint[element]].first == p.asInstanceOf[finitePoint[element]].second + p.asInstanceOf[finitePoint[element]].second
} else {
true
}, expected Boolean, found <untyped>
at inox.ast.TypeOps$TypeErrorException$.apply(TypeOps.scala:24)
at inox.ast.TypeOps$class.typeCheck(TypeOps.scala:264)
at inox.ast.SimpleSymbols$SimpleSymbols.typeCheck(SimpleSymbols.scala:12)
at inox.ast.Definitions$AbstractSymbols$$anonfun$ensureWellFormed$2.apply(Definitions.scala:166)
at inox.ast.Definitions$AbstractSymbols$$anonfun$ensureWellFormed$2.apply(Definitions.scala:165)
at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:733)
at scala.collection.immutable.Map$Map2.foreach(Map.scala:137)
at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:732)
at inox.ast.Definitions$AbstractSymbols$class.ensureWellFormed(Definitions.scala:165)
at inox.ast.SimpleSymbols$SimpleSymbols.ensureWellFormed$lzycompute(SimpleSymbols.scala:12)
at inox.ast.SimpleSymbols$SimpleSymbols.ensureWellFormed(SimpleSymbols.scala:12)
at inox.solvers.unrolling.AbstractUnrollingSolver$class.assertCnstr(UnrollingSolver.scala:129)
at inox.solvers.SolverFactory$$anonfun$getFromName$1$$anon$1.inox$tip$TipDebugger$$super$assertCnstr(SolverFactory.scala:115)
at inox.tip.TipDebugger$class.assertCnstr(TipDebugger.scala:52)
at inox.solvers.SolverFactory$$anonfun$getFromName$1$$anon$1.assertCnstr(SolverFactory.scala:115)
at inox.solvers.SolverFactory$$anonfun$getFromName$1$$anon$1.assertCnstr(SolverFactory.scala:115)
at welder.Solvers$class.prove(Solvers.scala:51)
at welder.package$$anon$1.prove(package.scala:10)
at welder.Solvers$class.prove(Solvers.scala:23)
at welder.package$$anon$1.prove(package.scala:10)
at Curve$.<init>(curve.scala:61)
at Curve$.<clinit>(curve.scala)
at Main$.main(Main.scala:4)
at Main.main(Main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
In fact, what is happening is that in the expression x === y+y the + is not being applied correctly so that it is untyped. I recall that inside the objects of Welder proofs one cannot define nested objects or classes I don't know if this has to do with it.
Anyways, do I have to forget about using infix notation in my code for Welder or there is a solution to this?
The issue here is that the implicit class you defined is not visible when you create y+y (you would need to import Field._ for it to be visible).
I don't remember exactly how implicit resolution takes place in Scala, so maybe adding import Field._ inside the Curve object will override the + that comes from the inox DSL (that's the one being applied when you write y+y, giving you an arithmetic plus expression that expects integer arguments, hence the type error). Otherwise, you'll unfortunately have ambiguity in the implicit resolution, and I'm not sure it's possible to use the infix + operator in that case without giving up the whole dsl.