Kotlin flatMapTo does not compile - kotlin

I want to flat map a Collection of Sets to a single Set. I have the following code which does not compile in IntelliJ IDEA. I can't tell why:
listOf(HashSet<String>()).flatMapTo(HashSet<String>()) { it.iterator() as Iterator<String> }
There is a very confusing error message on the lambda in the end which says:
Type mismatch. Required: (kotlin.collections.HashSet<String> /* =
java.util.HashSet<String> /) → Iterable<String> Found:
(kotlin.collections.HashSet<String> / = java.util.HashSet<String> */)
→ Iterator<String>
But they are both completely the same? I am confused as to why that doesn't want to work.
There is also an error message shown on the flatMapTo function and says:
Type inference failed:
inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo (
destination: C, transform: (T) → Iterable<R> ) : C cannot be applied
to
receiver: List<kotlin.collections.HashSet<String> /* =
java.util.HashSet<String> &ast;/> arguments: (
kotlin.collections.HashSet<String> /* = java.util.HashSet<String> &ast;/,
(kotlin.collections.HashSet<String> /* = java.util.HashSet<String> &ast;/)
→ Iterator<String> )
*Hope I didn't forget any special characters in the quotes. had to replace < and some * with html entities.*

Your lambda returns an Iterator<String>. It's supposed to return an Iterable<String>. A Set<String> is already an Iterable<String>.
All you need is
listOf(HashSet<String>()).flatMapTo(HashSet()) { it }

Related

How to use `when` with 2 sealed classes and getting the inner value?

Consider this extreme simplified code (available on https://pl.kotl.in/bb2Irv8dD):
sealed class Person {
data class A(val i: Int) :
Person()
}
fun main() {
val a = Person.A(i = 0)
val b = Person.A(i = 1)
// Compiles
when (a) {
is Person.A -> print("I have access to {$a.i}")
}
// Does not compile :(
when (a to b) {
is Person.A to is Person.A -> print("I have access to {$a.i} and b {$b.i}")
}
}
Why does the (a to b) code not work? It works for 1 variable, I was hoping I can match on both classes and get both inner values.
The error is:
Incompatible types: Person.A and Pair<Person.A, Person.A> Expecting
'->' Expecting an element Incompatible types: Person.A and
Pair<Person.A, Person.A>
Aside from that syntax not being supported (you can only use is on one thing in a when branch), by using to you're literally creating an instance of the Pair class.
Pair uses generics for the types of its two variables, so this type information is lost at runtime due to type erasure.
So although, you can do this:
when (a to b) {
is Pair<Person.A, Person.A> -> print("I have access to {$a.i} and b {$b.i}")
}
it is only allowed when both a and b are local variables whose types are declared locally, so that the generic types of the Pair are known at compile time. But this makes it mostly useless, because if a and b are local variables with known type at compile time, then you could just replace the above with true or false.
To be able to do something like this in a general way, you must either create local variables to use:
val aIsTypeA = a is Person.A
val bIsTypeA = b is Person.A
when (aIsTypeA to bIsTypeA) {
true to true -> //...
//...
}
or use when without a subject and put the full condition on each branch:
when {
a is Person.A && b is Person.A -> //...
//...
}
The (a to b) returns a Pair<Person.A,Person.A> but what you are checking is Type Person.A to Type Person.A instead of the Type Pair<Person.A,Person.A>.
What you can do instead is:
when (a to b) {
is Pair<Person.A,Person.A> -> print("I have access to {$a.i} and b {$b.i}")
}

Error when trying to convert a list of objects in a string using reduce function

I am playing with kotlin language and I tried the following code:
data class D( val n: Int, val s: String )
val lst = listOf( D(1,"one"), D(2, "two" ) )
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
The last statement causes the following errors:
Expected parameter of type String
Expected parameter of type String
Type mismatch: inferred type is D but String was expected
while this version of the last statement works:
val res = lst.map { e -> e.toString() }.reduce { acc, el -> acc + ", " + el }
I do not understand why the first version does not work. The formal definition of the reduce function, found here, is the following:
inline fun <S, T : S> Iterable<T>.reduce(
operation: (acc: S, T) -> S
): S
But this seems in contrast with the following sentence, on the same page:
Accumulates value starting with the first element and applying
operation from left to right to current accumulator value and each
element.
That is, as explained here:
The difference between the two functions is that fold() takes an
initial value and uses it as the accumulated value on the first step,
whereas the first step of reduce() uses the first and the second
elements as operation arguments on the first step.
But, to be able to apply the operation on first and second element, and so on, it seems to me tha the operation shall have both arguments of the base type of the Iterable.
So, what am I missing ?
Reduce is not the right tool here. The best function in this case is joinToString:
listOf(D(1, "one"), D(2, "two"))
.joinToString(", ")
.let { println(it) }
This prints:
D(n=1, s=one), D(n=2, s=two)
reduce is not designed for converting types, it's designed for reducing a collection of elements to a single element of the same type. You don't want to reduce to a single D, you want a string. You could try implementing it with fold, which is like reduce but takes an initial element you want to fold into:
listOf(D(1, "one"), D(2, "two"))
.fold("") { acc, d -> "$acc, $d" }
.let { println(it) }
However, this will add an extra comma:
, D(n=1, s=one), D(n=2, s=two)
Which is exactly why joinToString exists.
You can see the definition to understand why its not working
To make it work, you can simply create an extension function:
fun List<D>.reduce(operation: (acc: String, D) -> String): String {
if (isEmpty())
throw UnsupportedOperationException("Empty list can't be reduced.")
var accumulator = this[0].toString()
for (index in 1..lastIndex) {
accumulator = operation(accumulator, this[index])
}
return accumulator
}
you can use it as:
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
or simply:
val res = lst.reduce { acc, d -> "$acc, $d" }
You can modify the function to be more generic if you want to.
TL;DR
Your code acc:String is already a false statement inside this line:
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
Because acc can only be D, never a String! Reduce returns the same type as the Iterable it is performed on and lst is Iterable<D>.
Explanation
You already looked up the definition of reduce
inline fun <S, T : S> Iterable<T>.reduce(
operation: (acc: S, T) -> S
): S
so lets try to put your code inside:
lst is of type List<D>
since List extends Iterable, we can write lst : Iterable<D>
reduce will look like this now:
inline fun <D, T : D> Iterable<T>.reduce(
operation: (acc: D, T) -> D //String is literally impossible here, because D is not a String
): S
and written out:
lst<D>.reduce { acc:D, d:D -> }

Understanding Validated.applicative in kotlin arrow library

I come across below generic function which takes two Either type and a function as an argument. If both arguments are Either.Right then apply the function over it and returns the result, if any of the argument is Either.Left it returns NonEmptyList(Either.Left). Basically it performs the independent operation and accumulates the errors.
fun <T, E, A, B> constructFromParts(a: Either<E, A>, b: Either<E, B>, fn: (Tuple2<A, B>) -> T): Either<Nel<E>, T> {
val va = Validated.fromEither(a).toValidatedNel()
val vb = Validated.fromEither(b).toValidatedNel()
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
}
val error1:Either<String, Int> = "error 1".left()
val error2:Either<String, Int> = "error 2".left()
val valid:Either<Nel<String>, Int> = constructFromParts(
error1,
error2
){(a, b) -> a+b}
fun main() {
when(valid){
is Either.Right -> println(valid.b)
is Either.Left -> println(valid.a.all)
}
}
Above code prints
[error 1, error 2]
Inside the function, it converts Either to ValidatedNel type and accumulates both errors
( Invalid(e=NonEmptyList(all=[error 1])) Invalid(e=NonEmptyList(all=[error 2])) )
My question is how it performs this operation or could anyone explain the below line from the code.
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
Let's say I have a similar data type to Validated called ValRes
sealed class ValRes<out E, out A> {
data class Valid<A>(val a: A) : ValRes<Nothing, A>()
data class Invalid<E>(val e: E) : ValRes<E, Nothing>()
}
If I have two values of type ValRes and I want to combine them accumulating the errors I could write a function like this:
fun <E, A, B> tupled(
a: ValRes<E, A>,
b: ValRes<E, B>,
combine: (E, E) -> E
): ValRes<E, Pair<A, B>> =
if (a is Valid && b is Valid) valid(Pair(a.a, b.a))
else if (a is Invalid && b is Invalid) invalid(combine(a.e, b.e))
else if (a is Invalid) invalid(a.e)
else if (b is Invalid) invalid(b.e)
else throw IllegalStateException("This is impossible")
if both values are Valid I build a pair of the two values
if one of them is invalid, I get a new Invalid instance with the single value
if both are invalid, I use the combine function to build Invalid instance containing both values.
Usage:
tupled(
validateEmail("stojan"), //invalid
validateName(null) //invalid
) { e1, e2 -> "$e1, $e2" }
This works in a generic way, independent of the types E, A and B. But it only works for two values. We could build such a function for N values of type ValRes.
Now back to arrow:
Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
tupled is similar to map (with hardcoded success function). va and vb here are similar to a and b in my example. Instead of returning a pair of values, here we have a custom function (fn) that combines the two values in case of success.
Combining the errors:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
Semigroup in arrow is a way for combining two values from the same type in a single value of that same type. Similar to my combine function. NonEmptyList.semigroup() is the implementation of Semigroup for NonEmptyList that given two lists adds the elements together into a single NonEmptyList.
To sum up:
If both values are Valid -> it will combine them using the supplied function
If one value is Valid and one Invalid -> gives back the error
If both values are Invalid -> Uses the Semigroup instance for Nel to combine the errors
Under the hood this scales for 2 up to X values (22 I believe).

kotlin object conversion in lambdas convert

I'm trying to have this compiling:
val criteriaList = aList.stream().map { dateRange -> {
Criteria.where("KEY").`is`(dateRange) } }.toList().toTypedArray()
Criteria().orOperator(*criteriaList)
But:
Criteria().orOperator(*criteriaList)
Currently does not compile:
Type mismatch.
Required:
Array<(out) Criteria!>!
Found:
Array<(() → Criteria!)!>
Why?
You are mapping your dateRange to a () -> Criteria.
You do not need to wrap what is following after -> with curly braces. Check also the Kotlin reference regarding Lambda expression syntax:
val sum = { x: Int, y: Int -> x + y }
A lambda expression is always surrounded by curly braces [...], the body goes after an -> sign. If the inferred return type of the lambda is not
Unit, the last (or possibly single) expression inside the lambda body is treated as the return value.
So you could just write the following instead:
.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
Note also that you do not really need to call stream(), but you can directly call map on it (except it wouldn't be a real List in the first place).
So your code could probably be simplified to something like:
val criteriaList = aList.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
.toTypedArray()
or
val criteriaList = aList.map { Criteria.where("KEY").`is`(it) }
.toTypedArray()

SML converting a string to an int with error catching

So what I want to do is to convert a string into an int and do some error catching on it. I would also like to know where I would put what I want it to do after it fails if it does.
I know how to convert, but I am not sure how to catch it and where the code will jump to after the error
I believe the method for converting it Int.fromString(x)
Thank you.
SML has two approaches to error handling. One, based on raise to raise errors and handle to catch the error, is somewhat similar to how error handling works in languages like Python or Java. It is effective, but the resulting code tends to lose some of its functional flavor. The other method is based on the notion of options. Since the return type of Int.fromString is
string -> int option
it makes the most sense to use the option-based approach.
An int option is either SOME n, where n is and integer, or it is NONE. The function Int.fromString returns the latter if it fails in its attempt to convert the string to an integer. The function which calls Int.fromString can explicitly test for NONE and use the valOf to extract the value in the case that the return value is of the form SOME n. Alternatively, and somewhat more idiomatically, you can use pattern matching in a case expression. Here is a toy example:
fun squareString s =
case Int.fromString(s) of
SOME n => Int.toString (n * n) |
NONE => s ^ " isn't an integer";
This function has type string -> string. Typical output:
- squareString "4";
val it = "16" : string
- squareString "Bob";
val it = "Bob isn't an integer" : string
Note that the clause which starts NONE => is basically an error handler. If the function that you are defining isn't able to handle such errors, it could pass the buck. For example:
fun squareString s =
case Int.fromString(s) of
SOME n => SOME (Int.toString (n * n))|
NONE => NONE;
This has type string -> string option with output now looking like:
- squareString "4";
val it = SOME "16" : string option
- squareString "Bob";
val it = NONE : string option
This would make it the responsibility of the caller to figure out what to do with the option.
The approach to error handling that John explains is elaborated in the StackOverflow question 'Unpacking' the data in an SML DataType without a case statement. The use-case there is a bit different, since it also involves syntax trees, but the same convenience applies for smaller cases:
fun squareString s = Int.fromString s >>= (fn i => SOME (i*i))
Assuming you defined the >>= operator as:
infix 3 >>=
fun NONE >>= _ = NONE
| (SOME a) >>= f = f a
The drawback of using 'a option for error handling is that you have to take into account, every single time you use a function that has this return type, whether it errored. This is not unreasonable. It's like mandatory null-checking. But it comes at the cost of not being able to easily compose your functions (using e.g. the o operator) and a lot of nested case-ofs:
fun inputSqrt s =
case TextIO.inputLine TextIO.stdIn of
NONE => NONE
| SOME s => case Real.fromString s of
NONE => NONE
| SOME x => SOME (Math.sqrt x) handle Domain => NONE
A workaround is that you can build this constant error handling into your function composition operator, as long as all your functions share the same way of expressing errors, e.g. using 'a option:
fun safeSqrt x = SOME (Math.sqrt x) handle Domain => NONE
fun inputSqrt () =
TextIO.inputLine TextIO.stdIn >>=
(fn s => Real.fromString s >>=
(fn x => safeSqrt x))
Or even shorter by applying Eta conversion:
fun inputSqrt () = TextIO.inputLine TextIO.stdIn >>= Real.fromString >>= safeSqrt
This function could fail either because of a lack of input, or because the input didn't convert to a real, or because it was negative. Naturally, this error handling isn't smart enough to say what the error was, so you might want to extend your functions from using an 'a option to using an ('a, 'b) either:
datatype ('a, 'b) either = Left of 'a | Right of 'b
infix 3 >>=
fun (Left msg) >>= _ = Left msg
| (Right a) >>= f = f a
fun try (SOME x) _ = Right x
| try NONE msg = Left msg
fun inputLine () =
try (TextIO.inputLine TextIO.stdIn) "Could not read from stdIn."
fun realFromString s =
try (Real.fromString s) "Could not derive real from string."
fun safeSqrt x =
try (SOME (Math.sqrt x) handle Domain => NONE) "Square root of negative number"
fun inputSqrt () =
inputLine () >>= realFromString >>= safeSqrt
And trying this out:
- ​inputSqrt ();
​9
> val it = Right 3.0 : (string, real) either
- ​inputSqrt ();
​~42
> val it = Left "Square root of negative number" : (string, real) either
- ​inputSqrt ();
Hello
> val it = Left "Could not derive real from string." : (string, real) either
- (TextIO.closeIn TextIO.stdIn; inputSqrt ());
> val it = Left "Could not read from stdIn." : (string, real) either