kotlin when expression autocast - kotlin

I would like the following kotlin code to work:
val result: Try<Option<String>> = Success(Some("test"))
val test = when {
result is Success && result.value is Some -> result.value.t // not working
result is Success && result.value is None -> "Empty result"
result is Failure -> "Call failed!"
else -> "no match!"
}
I use the arrow library for the Try and Option monad.
Unfortunately, I can only access the value of the first condition "is Success" and not the second condition "is Some". So, I can only do "result.value", I then get an Option of String.
Am I missing something? This will save me alot of inner ".map" and ".fold" calls.
Update:
I need to cast it first, which is ugly:
result is Success && result.value is Some -> (result.value as Some<String>).t

I tried your example in IntelliJ with Kotlin 1.3.21.
It shows the reason of the problem:
You need to extract the result.value as a variable to make it work. I found the following snippet to solve it
val result: Try<Option<String>> = Success(Some("test"))
val test = when (result) {
is Success -> when(val value = result.value) {
is Some -> value.t
is None -> "None"
}
is Failure -> "Call failed!"
else -> "no match!"
}
I use Kotlin 1.3.x when with declaration syntax.
You may also use Arrow API to get similar result:
val test = result.fold(
ifSuccess = { it.getOrElse { "None" }},
ifFailure = { "Call failed!" }
)
Here you do not need to have the else clause in when.

You can simplify the pattern matching like this:
val test = result
.map { it.getOrElse { "Empty result"} }
.getOrElse { "Call failed!" }
Which is a bit more exhaustive and doesn't require an else alternative
Alternatively, if you don't care about the exception that is thrown you can use toOption on the Try:
val test = result
.toOption()
.getOrElse { "No value!!" }
However, that has some obvious loss of information.
I personally would bubble up the Try instance to the consumer of the result collapsing the inner Option with a .map so that the final result is of type Try<String> and let the consumer handle the error.
However, it depends a lot of the actual context of the problem.

Related

Why to use "return#" in Ktor Route.get function

In the Ktor official tutorial I saw the following code:
get("{id?}") {
val id = call.parameters["id"] ?: return#get call.respondText(
"Missing id",
status = HttpStatusCode.BadRequest
)
val customer =
customerStorage.find { it.id == id } ?: return#get call.respondText(
"No customer with id $id",
status = HttpStatusCode.NotFound
)
call.respond(customer)
}
I can't understand, what is the purpose of the labeled return "return#". In Kotlin documentation it is said that: "return by default returns from the nearest enclosing function or anonymous function". In the provided code, as I understand, the get function is the nearest enclosing function, so why add labeled return to "get"?
it's because you return not from enclosing or anonymous function, but from lambda. Here you can see not declaration of get function but its invocation with lambda

How to only map some items from a sequence and ignore others

I'm working on a tool to help me look through log files and decided to learn Kotlin at the same time. (I'm pretty new to the language so I apologize if this question is obvious.) For now I want to read a log file and compare each line to several regular expressions. Depending on which one it matches, I want to give a different color. I return these in a list for the UI to handle displaying them
Here are the problems I see with my current code:
each line is being checked against the regular expression twice. This is an unnecessary performance hit which I want to avoid since this will eventually be handling VERY large log files
I need to return something in the else statement to keep my type List<LineAndStyle> instead of becoming List<Any>.
fun process(file: File): List<LineAndStyle> {
val result = file.useLines {
it.filter { line ->
exceptionsPattern.containsMatchIn(line) ||
networkPattern.containsMatchIn(line) ||
dataPattern.containsMatchIn(line)
}.map { line ->
when {
exceptionsPattern.containsMatchIn(line) -> LineAndStyle(line, Color.PINK)
networkPattern.containsMatchIn(line) -> LineAndStyle(line, Color.LIGHTBLUE)
dataPattern.containsMatchIn(line) -> LineAndStyle(line, Color.LIGHTGREEN)
else -> {
LineAndStyle(line, Color.BLACK)
}
}
}.toList()
}
return result
}
Please help me understand the idiomatic way to do this in Kotlin.
How about this?
fun process(file: File): List<LineAndStyle> =
file.useLines { lines ->
lines.mapNotNull { line ->
when {
exceptionsPattern.containsMatchIn(line) -> LineAndStyle(line, Color.PINK)
networkPattern.containsMatchIn(line) -> LineAndStyle(line, Color.LIGHTBLUE)
dataPattern.containsMatchIn(line) -> LineAndStyle(line, Color.LIGHTGREEN)
else -> null
}
}.toList()
}

How to typesafe reduce a Collection of Either to only Right

Maybe a stupid question but I just don't get it.
I have a Set<Either<Failure, Success>> and want to output a Set<Success> with Arrow-kt.
You can map the set like this for right:
val successes = originalSet.mapNotNull { it.orNull() }.toSet()
or if you want the lefts:
val failures = originalSet.mapNotNull { it.swap().orNull() }.toSet()
The final toSet() is optional if you want to keep it as a Set as mapNotNull is an extension function on Iterable and always returns a List
PS: No stupid questions :)
Update:
It can be done avoiding nullables:
val successes = originalSet
.map { it.toOption() }
.filter { it is Some }
.toSet()
We could potentially add Iterable<Option<A>>.filterSome and Iterable<Either<A, B>.mapAsOptions functions.
Update 2:
That last example returns a Set<Option<Success>>. If you want to unwrap the results without using null then one thing you can try is to fold the Set:
val successes = originalSet
.fold(emptySet<Success>()) { acc, item ->
item.fold({ acc }, { acc + it })
}
This last option (unintended pun) doesn't require the use of Option.

Kotlin: function to stop iteration when predicate satisfied

I'm looking for a function in Kotlin which stops an iteration once a predicate is fulfilled:
val services = listOf(service1, service2)
...
var res: Result = null
services.stopIfPredicateFulFilled { service ->
res = service.doSomething()
res != null
}
While this example is not really nice since res is overwritten in each iteration I hope the intention is clear.
forEach doesn't do the job the way I expect it to be done. So, I was wondering if there isn't anything else.
You can use the functions find { ... } and firstOrNull { ... } (they are equivalent, just named differently). They find the first element satisfying the predicate and return that element, ignoring all the remaining elements.
services.find { service ->
res = service.doSomething()
res != null
}

Is it possible to parameterize queries or parameters for an Acolyte ScalaCompositeHandler?

Background:
I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).
If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.
One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.
Question:
Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:
def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
parameters.foreach(p =>
// Tell the handler to handle this specific parameter
case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
someResultFunction(p)
)
}
Is this possible in Acolyte? If so, please provide an example.
It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.
See the code below for an example:
import java.sql.DriverManager
import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec
class AcolyteTest extends FunSpec {
describe("Using pattern matching to extract a query parameter") {
it("should extract the parameter and make it usable for dynamic result returning") {
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val handlerName = "testOneHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
rows.append(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "hello world")
val resultSet = preparedStatement.executeQuery()
resultSet.next()
assertResult(resultSet.getString(1))("hello world")
}
it("should support a slightly more complex example") {
val firstResult = "The first result"
val secondResult = "The second result"
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val results: Map[String, RowList1.Impl[String]] = Map(
"one" -> rows.append(firstResult),
"two" -> rows.append(secondResult)
)
def getResult(parameter: String): QueryResult = {
results.get(parameter) match {
case Some(row) => row.asResult()
case _ => acolyte.jdbc.QueryResult.Nil
}
}
val handlerName = "testTwoHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
getResult(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "one")
val resultSetOne = preparedStatement.executeQuery()
resultSetOne.next()
assertResult(resultSetOne.getString(1))(firstResult)
preparedStatement.setString(1, "two")
val resultSetTwo = preparedStatement.executeQuery()
resultSetTwo.next()
assertResult(resultSetTwo.getString(1))(secondResult)
}
}
}