Structural search interface suspend function call - kotlin

I have a custom function for wrapping Service api call
I want to add some warning while some api call forget adding safeApiCall{}
suspend fun <T:Any> safeApiCall(
apiCall: suspend () -> T
) {
//do something
}
interface Service {
#Get
suspend fun getUser() : User
#Put
suspend fun updateUser(name:String) : User
}
val service:Service
service.getUser() //warning "should use safeApiCall"
SafeApiCall { service.getUser() } //ok
This is my attempt , but not working
interface $Interface$ {
suspend fun $Method$ ($Parameter$ : $ParameterType$): $ReturnType$
}
$Interface$.$MethodCall$

That was tricky, but here it is:
You need to use script constraints. The idea is to filter the parent of your whole match. There are many ways to do that, e.g. to distinguish them by their type.
Here is my search:
Template:
$Service$.$method$($args$)
$Service$ variable filter:
Type = you.package.Service
$args$ (optional, though):
Count = [0: +Inf]
And the most important part, the whole template script filter:
__context__.parent.parent.class.name == "org.jetbrains.kotlin.psi.KtNamedFunction"
It's dirty, I know. For some reason I was not able to make it work with instanceof, but you may tune it.
If you do, you may ask: How do I debug that script?
Here is my advice. Open the IDEA's logs (Help -> Show Log in Explorer). Then use println() in the script, it will print it's argument to the STDOUT:
You see, here there were two matches, but the parent of the second one (which os ok as per your request) was of a different type. So, a quick guess and dirty fix here was to compare them. A more cunning way would be to get the name of the wrapping construct and check if it's safeApiCall or not. I'll leave that to you as an exercise.
Good luck!

Related

When in Kotlin Either Hell

I am trying to use
Arrow Either results instead of try-catch, but have gone too deep down the rabbit hole. 🙄
I have been trying to use Either<Problem,Value> as my functional return types, where Problem is like
sealed interface Problem
data class Caught(val cause: Throwable): Problem
data class DataKeyDisabled(val uuid: UUID, val cause: String): Problem
data class SubscriberNotFound(val uuid: UUID, val cause: String): Problem
data class NotEncrypted(val field: String): Problem
where the use case looks like
when (val result = transform(...)) {
is Right -> {}
is Left -> when (val problem = result.value) {
is Caught -> {}
is DataKeyDisabled -> {}
is SubscriberNotFound -> {}
is NotEncrypted -> {}
// else -> {} not needed...
}
}
But, there are really three types of problems, and I don't want to have to exhaust all the choices all the time.
Problem -> Caught
KeyProblem -> Caught, DataKeyDisabled, SubscriberNotFound
DataProblem -> Caught, DataKeyDisabled, SubscriberNotFound, NotEncrypted
For example, I want to have something like
sealed interface Problem
sealed interface KeyProblem : Problem
sealed interface DataProblem : KeyProblem
data class NotHandled(val cause: Throwable): Problem
data class DataKeyDisabled(val uuid: UUID, val cause: String): KeyProblem
data class SubscriberNotFound(val uuid: UUID, val cause: String): KeyProblem
data class NotEncrypted(val cause: String) : DataProblem
And I want to be able to have some code like
fun bar(input: Either<Problem,String>) : Either<KeyProblem,String> {
val something = when (input) {
is Right -> {}
is Left -> {
when (val problem = input.value) {
is NotHandled -> {}
is DataKeyDisabled -> {}
is SubscriberNotFound -> {}
is NotEncrypted -> {}
}
}
}
}
But Kotlin complains about NotHandled, DataKeyDiabled, and SubscriberNotFound are not a DataProblem
In some cases, I want to return a KeyProblem so I can drop the NotEncrypted case from the when, and in some cases I want to return only a Problem such that the only case is NotHandled.
I do not know how to express this in Kotlin. I suspect it is not possible to express this in Kotlin, so if someone tells me it is impossible, that is a solution.
I am thinking it was a bad decision to replace try-catch with Arrow Either. If so, someone please tell me so.
I wanted to stick to Functional Reactive Programming paradigms, where try-catch does not work, but with Kotlin coroutines it sort of does work. 🤔
It seems to me, the problem with sealed things is that when using when you can only have one level of inheritance, and no more?
Maybe I am just looking at the whole problem the wrong way... help... please...
So my solution is to give up on trying to use Arrow Either and Kotlin sealed classes instead of using standard
try {
// return result
}
catch {
// handle or rethrow
}
finally {
// clean up
}
While I have been trying to practice Reactive and non-blocking programming for years, this was easy in Scala, but it's not easy in Kotlin.
After watching enough Java Project Loom videos, I am by far convinced this is the best way to go because exception handling just works... I could use Kotlin Coroutines because they also preserve correct exception handling, and may do that temporarily, but in the long run, Virtual Threads and Structured Concurrency are the way to go.
I hate using these words, but I am making a 'paradigm shift' back to cleaner code, retreating from this rabbit hole I have gone down.
It seems like you are going too far to re-use your error-types, when in fact your functions have different return-types and things that can go wrong. The simplest and cleanest solution in my opinion is to declare both the happy-case and error-case types per function. Then it should be very easy to only handle the cases than can actually go wrong per function.
For example if you have a function getPerson, you would declare the data class Person as the right value, and a GetPersonError as the left value, where the GetPersonError is an interface with only the relevant errors, like so:
private fun getPerson(identifier: String): Either<GetPersonError, Person> {...}
data class Person(name: String, ....)
sealed interface GetPersonError
sealed class PersonNotFoundError(): GetPersonError
sealed class InvalidIdentifierError(): GetPersonError
This does require you to write more code than reusing the same Problem-class for multiple functions, but the code becomes very readable and easy to change, which is much more difficult to achieve when reusing a lot of code.

Is there an easy way to call parent super function from inner class?

I have the following code
class DrawingActivity : AppCompatActivity() {
private inner class ImageInfoObserver : Observer<ImageInfo> {
override fun onChanged(imageInfo: ImageInfo?) {
// Is there a way to perform DrawingActivity.super.finish() ?
superFinish()
}
}
fun superFinish() {
super.finish()
}
override fun finish() {
...
super.finish()
}
Currently, I need to specially create superFinish() function, in order for inner class ImageInfoObserver to call DrawingActivity.super.finish()
I was wondering, is there an easier way to accomplish so, without having to create superFinish()?
You do it in the same way you access DrawingActivity - but instead of this#DrawingActivity you use super#DrawingActivity instead. So
super#DrawingActivity.finish()
would be like calling super.finish() from inside DrawingActivity. The docs have an example too.
Is there any reason you don't want to call finish() on the actual class though? (i.e. this#DrawingActivity.finish()) Why do you want to skip the DrawingActivity's own finish code? If there's a good reason for it, it might be worth making the extra code in the overridden finish() function conditional on some state variable.
That way it's clear from reading the function when certain stuff happens and when it doesn't, and all the finish teardown logic is handled in one place.

Use extension function from different context in Kotlin

Here is an example of what I'd like to achieve:
open class A {
open fun Int.foo() {
print("foo")
}
}
object B: A() {
val number = 5;
override fun Int.foo() {
print("overriden foo");
// I want to call the A.(Int.foo())
}
}
B.number.foo(); //outputs: "foooverriden foo"
First of all, does anything like this exist? Can I somehow assume number to be in the context of class A in its override method? How would I write this?
The more I think about it the more it twists my mind. Of course, you cannot call number.super.foo() because super for number is kotlin.Number. You cannot cast it to A because Int has nothing to do with A. The only way I can think about solving this to somehow import the extension function itself and rename it with as, but I cannot do that here since it is inside a class, so I cannot just import it. Any suggestions?
My use case for this is that I have a class where I manipulate some data, then in special cases, I want to manipulate it differently, but fall back to the original code as the last option. I could use normal functions instead of extension functions of course, but in my case, it comes natural to use extension functions, so I wanted to see if this could be achieved somehow.
It looks like this is impossible so far, I'm afraid.
There's an open issue for this on JetBrains' issue-tracking system: KT-11488.  There's a Kotlin work-around there, though that needs tweaks to the class designs.
(Also discussed on the JetBrains discussion board.  That mentions another workaround requiring a Java class.)
override fun Int.foo() {
print("overriden foo")
with (A()) {
foo()
}
}
Of course this is a bit of a hack and will get worse if A has some state which foo() depends on, which you'll then need to set manually.

Using Kotlin's extension function to suppress a returned value

I am using Kotlin in a spring boot application. Especially in the services, I have found that some of the function need to suppress the returned value from repository. For example, here is a save() that saves an entity without returning the persisted entity id:
fun save(person: Person) {
personRepository.save(person)
}
As you can see that this function simply delegates the call to the JpaRepository#save(...) and does not return anything. What I wanted to do was something like this:
fun save(person: Person) = personRepository.save(person)
In order to do this, I have created an extension function:
fun Any.ignoreReturn() = Unit
and then make the call to the `personRepository#save(...) as:
fun save(person: Person) = personRepository.save(person).ignoreReturn()
What I wanted to know was:
Is this the right way to do it?
Are there side effects to such extension function as I am extending Any?
One way could be to do it like this:
fun save(person: Person): Unit = let { personRepository.save(person) }
Important part there is to declare the function to return Unit so the generated code from let wont need to return what personRepository.save(person) is returning. You can test it, just remove : Unit part and you get different signature for your save function.

How does Kotlin recognize a lambda receiver in a "use" function

When I look at sample code for the "use" function in Kotlin, I usually see something like this:
private fun readFirstLine(): String {
BufferedReader(FileReader("test.file")).use { return it.readLine() }
}
However, in the following example, I don't understand where "input" comes from, since input -> appears to be a lambda. From my understanding, everything inside of use { } must be an expression:
val streamIn = resources.openRawResource(rawResId)
val streamOut = FileOutputStream(destFilename)
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
"input" clearly refers to the same object that "streamIn" refers to, but I don't understand how Kotlin knows that.
everything inside of use { } must be an expression
If you looked at the signature, you'll see that use takes a (T) -> R function, so really, any function/lambda that accepts the closable thing as a parameter can be passed to it.
With that misconception cleared up, let's see what the code in question is doing.
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
First we see streamIn.use {, which means we are going to do something with streamIn and then close it. And from now on streamIn will be called input. Then there is streamOut.use {, which indicates that we are also going to use streamOut to do stuff, and then close it, and we are going to call it output from now on.
I don't understand where "input" comes from
It's basically giving another name to the it as in your first code snippet. Since we have nested lambdas here, we can't use it to refer to the parameters of both lambdas.
"input" clearly refers to the same object that "streamIn" refers to, but I don't understand how Kotlin knows that.
This is because in the implementation of use, there's probably a line like this:
return block(this)
block is the lambda parameter you pass to use, and this is the object on which use is called. Since input is the parameter of the lambda, it refers to this.
Now we have declared that we are going to use two resources, what are going to do with them? input.copyTo(output)! Whatever copyTo returns is going to be returned by streamOut.use, which in turn is going to be returned by streamIn.use. streamOut and streamIn will also be closed one after another.
So overall what have we done? We have basically used 2 resources at the same time and closed them afterwards. This is how you'd compose use to use multiple resources at the same time.
in the lambda, you can define a name for your object so in the following code the input is equivalent to it which is streamIn and output is equivalent to streamOut:
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
The reason that they define input and output is you cannot use it when you use a lambda block inside another lambda block.
use is an extension function which takes whatever calls it as a parameter.
Assume this example:
file.bufferedReader().use{
println(it.readText()) // it is actually that object that calls `use`
}
According to the API docs of Kotlin, this is the schema of use:
inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R
The bufferedReader in my example is a closable class.
When you write somethingClosable.use { }, you are in fact passing a lambda function to it, like:
fun <T, R> function(t: T): R {
// use T and return an R
}
somethingClosable.use(function)
And inside use your function will be called.
More info on extension functions in Kotlin.