Using applyOptional in Monocle - monocle-scala

I'm trying to figure out how to use applyOptional. I've got this:
import monocle.function.all.index
import monocle.macros.{GenLens, Lenses}
import monocle.std.map._
import monocle.syntax.ApplyOptionalOps._
import monocle.function.Index._
val map: Map[Int, String] = Map.empty
val lens = map applyOptional index(4)
But the compiler tells me "Cannot resolve symbol applyOptional." I imported ApplyOptionalOps._ just to confirm that I had the right imports.

ApplyOptionalOps is the case class with the source object as the parameter, so by importing its companion object one can't access its functions. One should import monocle.syntax.apply._ instead, which extends ApplySyntax trait containing implicit conversion from generic source object to ApplyOptionalOps as well as some other operation wrappers. In fact, for just this example it the following imports is sufficient:
import monocle.syntax.apply._
import monocle.function.Index._
val map: Map[Int, String] = Map.empty
val lens = map applyOptional index(4)

Related

Trying to implement the TornadoFX "Motivational example"

I'm trying to implement the motivational example from this page: https://docs.tornadofx.io/0_subsection/1_why_tornadofx
For this I need a data class Person as defined here:
class Person(id: Int, name: String, birthday: LocalDate) {
val idProperty = SimpleIntegerProperty(id)
var id by idProperty
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
val birthdayProperty = SimpleObjectProperty(birthday)
var birthday by birthdayProperty
val age: Int get() = Period.between(birthday, LocalDate.now()).years
}
To do this it was neccessary to make the following imports:
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import java.time.LocalDate
import java.time.Period
However, if I try to run the example I get the following error:
Kotlin: Property delegate must have a 'getValue(Person, KProperty<*>)' method. None of the following functions is suitable:
public open fun getValue(): Int! defined in javafx.beans.property.SimpleIntegerProperty
I can circumvent this by not using delegate types and setting the properties like this:
val idProperty = SimpleIntegerProperty(id)
var id: Int
get() = idProperty.value
set(value) { idProperty.value = value}
But that seems to defeat the point of using delegates in TornadoFX when this is their motivational example for using it.
Here's what I found on delegate types: https://edvin.gitbooks.io/tornadofx-guide/content/part2/Property_Delegates.html
That doesn't help with getting the shorthand of var id by idProperty to work though.
Can somebody point me in the right direction here?
You need to also import the following:
import tornadofx.getValue
import tornadofx.setValue
Those are extension operator functions defined for various types in JavaFX (e.g., properties, observable values, etc.) so that those types can be used as delegates. But those function aren't defined in those types, thus the need for the additional imports.

Convert string data representation back to Kotlin Data Class

In Kotlin how is possible to convert a data class from its string representation back to the actual data class without having to manually parse the string?
for example, I have the next data class where both Interest and EmploymentType are enums, being the second element a List.
data class DataFilter(val mainInterest: Interest, val employments: List<EmploymentType>)
with toString I can get the contents its string representation, but if I want to get it back to a data class, how is it done?
For this purpose, you need to use a serialization library. I recommend you use Kotlinx.serialization library. This will help you convert the data object into a JSON String and convert it back to the data object easily.
You can follow the guideline to setup Kotlinx.serialization library.
After the setup is done, look at the following example:
I will define both enum classes Interest and EmploymentType, and the DataFilter class:
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
enum class Interest {
SPORTS,
BOOKS,
TRAVEL,
FOOD,
TECHNOLOGY,
ART,
OTHER
}
enum class EmploymentType {
FULL_TIME,
PART_TIME,
CONTRACT,
FREELANCE,
OTHER
}
#Serializable //You have to annotate your class as Serializable to make it works.
data class DataFilter(
val mainInterest: Interest,
val employment: List<EmploymentType>
)
Notice that Interest and EmploymentType are not annotated as #Serializable because enums are Serializable by default, but if you defined any other normal classes to use with DataFilter, then you have to annotate them as well.
Next, I will show you how conversions work:
fun main() {
val filter = DataFilter(
mainInterest = Interest.BOOKS,
employment = listOf(EmploymentType.FULL_TIME, EmploymentType.FREELANCE)
)
// Converting filter object into JSON String:
val filterAsJsonString = Json.encodeToString(filter)
println(filterAsJsonString) //prints {"mainInterest":"BOOKS","employment":["FULL_TIME","FREELANCE"]}
//Convert JSON String back to data object:
val filterAsDataObject: DataFilter = Json.decodeFromString(filterAsJsonString)
println(filterAsDataObject) //prints DataFilter(mainInterest=BOOKS, employment=[FULL_TIME, FREELANCE])
}

Why can Kotlin's code directly call the top level function from “kotlin.collections”, without import the package

Why can Kotlin's code directly call the top level function from “kotlin.collections”, without import the package. such as below function listOf:
data class Person1(val name: String, val age: Int)
class DataClassExecutor {
... ...
fun test(arg: String?): String? {
val persons = listOf(
Person1("Lucy", age = 26),
Person1("Lily", age = 29))
... ...
}
}
Please refer this page: https://kotlinlang.org/spec/packages-and-imports.html. It says:
There are some packages which have all their entities implicitly
imported into any Kotlin file, meaning one can access such entity
without explicitly using import directives.
The List includes kotlin.collections.
This is similar to how in Java, java.lang is implicitly imported. In Java one does not need to say java.lang.System.out.println, just System.out.println is enough.

Add new custom properties to retrofit2.Response

In my android app:
Here my interface method:
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
#GET("traders/json")
suspend fun getTraidersList(): Response<List<Trader>>
Nice.
but I need to add 2 my custom properties to retrofit2.Response
e.g.
val isCorrect : boolean
val myCustom : MyCustomClass
I want to set/get this properties. Smt like this:
val response: Response<List<Trader>> = TransportService.getTraidersList()
if (response.isCorrect) {
// do some logic
}
val myCustom = response.getMyCustom()
Is is possible in Kotlin?
Only you can do in Kotlin is to add some extension members, which are in fact just a usual Java's static methods. All the stuff around extension getters and setters is also emulated using static methods.
Based on mentioned above we cannot add new state (fields) using static methods.
But what can we do (I'm not familiar with Retrofit, it should be possible), is to use extension getter isCorrect, which can read response status, and if it is 4xx or 5xx it returns false

Exception when using spring-data-mongodb with Kotlin

I'm new to Kotlin, and experimenting with spring-data-mongodb. Please see example below (also available here as fully runnable Maven project with in-memory MongoDb: https://github.com/danielsindahl/spring-boot-kotlin-example).
Application.kt
package dsitest
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
open class Application
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
User.kt
package dsitest
import org.springframework.data.annotation.Id
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.mongodb.core.mapping.Document
#Document(collection = "user")
data class User #PersistenceConstructor constructor(#Id val id: String? = null, val userName: String)
UserRepository.kt
package dsitest
import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository<User, String>
KotlinIntegrationTest.kt
package dsitest
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
#SpringBootTest
class KotlinIntegrationTest constructor () {
#Autowired
lateinit var userRepository : UserRepository;
#Test
fun persistenceTest() {
val user : User = User(userName = "Mary")
val savedUser = userRepository.save(user)
val loadedUser = userRepository.findOne(savedUser.id) // Failing code
println("loadedUser = ${loadedUser}")
}
}
When running the test KotlinIntegrationTest.persistenceTest, I get the following error message when trying to retrieve a User object from MongoDb:
org.springframework.data.mapping.model.MappingException: No property null found on entity class dsitest.User to bind constructor parameter to!
If I modify the User data class so that userName is nullable, everything works.
data class User #PersistenceConstructor constructor(#Id val id: String? = null,
val userName: String? = null)
I would like to understand why this is the case, since I don't want userName to be nullable. Is there some alternative, better way of defining my User class in Kotlin?
Many thanks,
Daniel
Yes, it is a known problem. You should check how the bytecode for your User class looks like. Java sees the constructor with all the parameters present and tries to call it with a null value for the 2nd one.
What you could do is to try adding #JvmOverloads to your constructor - this will force Kotlin compiler to generate all versions of the constructor and so the Spring Data Mongo could pick the correct one (get rid of the #PersistenceConstructor) then.
You could also define 1 constructor with no defaults - only for Java-based frameworks and 2nd one with some defaults your you. Or...
When I write things like you are now, I create simple 'persistence' data classes with no default values whatsoever that are mapped to/from my regular domain objects (a sort of abstraction over database). It may generate some overhead at the start - but keeping your domain model not coupled so tightly with the storage model is usually a good idea anyway.