What does toSortedMap in Kotlin [duplicate] - kotlin

This question already has answers here:
what is natural ordering when we talk about sorting?
(5 answers)
Closed 12 months ago.
What does the method "toSortedMap"?
According to the documentation we can read:
Converts this Map to a SortedMap. The resulting SortedMap determines
the equality and order of keys according to their natural sorting
order.
But what does natural sorting order mean ?
Their example doesn't look sorted.

The function signature of toSortedMap is defined as:
fun <K : Comparable<K>, V> Map<out K, V>.toSortedMap(): SortedMap<K, V>
The sort order thus is defined by the comparison logic defined while implementing Comparable by the type K resembling the keys.
For numeric and string values, this is most likely the intuitive ascending sort order. For custom types, e.g. User, a custom implementation to fulfill the Comparable interface must be provided.
Alternatively there is a second signature, which allows to provide a custom Comparator<in K> to make use of a custom sort order, if the keys of type K don't implement Comparable<K> or this implementation does not fullfil the expected ordering.
fun <K, V> Map<out K, V>.toSortedMap(
comparator: Comparator<in K>
): SortedMap<K, V>
Additional information can be found in the documentation of SortedMap and Comparable<T>.

Related

Why is the key type parameter of a Kotlin Map invariant?

The Map interface in Kotlin (using V1.6.21) has a signature of
interface Map<K, out V>
Why is K invariant instead of covariant (out K)?
The documentation of type parameter K says:
The map is invariant in its key type, as it can accept key as a parameter (of containsKey for example) and return it in keys set.
However, interface Set is covariant in the element type, so the the last part ("return it in keys set") is not applicable, at least not immediately.
Further, the type parameter K is used only at occurrences where the map state is not modified, for lookup purposes (methods containsKey, get, getOrDefault). At these places, isn't it safe to use #UnsafeVariance? After all, that same technique was employed to Map's value type parameter V, for example in containsValue, to allow making V covariant.
My guess would be that using a Map<KSubtype, V> as a Map<KSupertype, V> (where KSubtype : KSupertype) does not really make a lot of sense because the former, by construction, cannot contain entries with keys other than KSubtype.
So a proper implementation should return null from all calls to get(kSupertype) as well as return false from those to containsKey(kSupertype).
In the case of Set<out E> it's only the contains function that needs unsafe variance, and Map would also require unsafe variance on get. This might have been too much of a peculiarity to support, compared to the value of supporting the use case.

Relation between Arrow suspend functions and monad comprehension

I am new to Arrow and try to establish my mental model of how its effects system works; in particular, how it leverages Kotlin's suspend system. My very vague understanding is as follows; if would be great if someone could confirm, clarify, or correct it:
Because Kotlin does not support higher-kinded types, implementing applicatives and monads as type classes is cumbersome. Instead, arrow derives its monad functionality (bind and return) for all of Arrow's monadic types from the continuation primitive offered by Kotlin's suspend mechanism. Ist this correct? In particular, short-circuiting behavior (e.g., for nullable or either) is somehow implemented as a delimited continuation. I did not quite get which particular feature of Kotlin's suspend machinery comes into play here.
If the above is broadly correct, I have two follow-up questions: How should I contain the scope of non-IO monadic operations? Take a simple object construction and validation example:
suspend fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
In Haskell's do-notation, this would be
mkMessage :: String -> String -> String -> Maybe Message
mkMessage msgType appRef pId = do
type <- mkMessageType msgType
ref <- mkAppRef appRef
id <- mkId pId
return (Message type ref id)
In both cases, the function returns the monad type (a nullable value, resp. Maybe). However, while I can use the pure function in Haskell anywhere I see fit, the suspend function in Kotlin can only be called from within a suspend function. In this way, a simple, non-IO monad comprehension in Arrow behaves like an IO monad that must be threaded throughout my code base; I suppose this results because the suspend mechanism was designed for actual IO operations. What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
The two questions above probably mean that I am still lacking a mental model that bridges between the continuation-based implementation atop the Kotlin's suspend mechanism with the generic monad-as-typeclass implementation in Haskell.
schuster,
You're correct that Arrow uses the suspension feature from Kotlin to encode something like monad comphrensions.
To answer your first question:
Kotlin has suspend in the language (and Kotlin Std), by default suspend can only be called from other suspend code. However, the compiler also has a feature called RestrictsSuspension, this disallows for mixing suspend scopes and thus disallows the ablity to combine IO and Either for example. We expose a secondary DSL, either.eager which is encoded using RestrictsSuspension and it disallows calling foreign suspend functions.
This allows you to encode mkMessage :: String -> String -> String -> Maybe Message.
fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable.eager {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
To answer your second question:
IO as a data type is not needed in Kotlin, since suspend can implement all IO operations in a referential transparent way like it works in Haskell.
The compiler also makes a lot optimisations in the runtime, just like Haskell does for IO.
So the signature suspend fun example(): Either<Error, Value> is the equivalent of EitherT IO Error Value in Haskell.
The IO operations are however not implemented in the Kotlin Std, but in a library KotlinX Coroutines, and Arrow Fx Coroutines also offers some data types and higher-level operations such as parTraverse defined on top of KotlinX Coroutines.
It's slightly different than in Haskell, since we can mix effects instead of stacking them with monad transformers. This means that we can call IO operations from within Either operations. This is due to special functionality, and optimisations the compiler can make in the suspension system. This blog explains how that optimisation works, and why it's so powerful. https://nomisrev.github.io/inline-and-suspend/
Here is also some more background on Continuations, and tagless encodings in Kotlin. https://nomisrev.github.io/continuation-monad-in-kotlin/
I hope that fully answers your question.
I don't think I can answer everything you asked, but I'll do my best for the parts that I do know how to answer.
What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
you can use nullable.eager and either.eager respectively for pure code. Using nullable/either (without .eager) allows you to call suspend functions inside. Using eager means you can only call non-suspend functions. (not all effectual functions in kotlin are marked suspend)
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
You can use extension functions to emulate Reader. For example:
suspend fun <R> R.doSomething(i: Int): Either<Error, String> = TODO()
combines Reader + IO + Either. You can find a bigger example here from Simon, an Arrow maintainer.

mapOf() vs emptyMap() in Kotlin

Just started using Kotlin in our projects. To initialise an immutable map or list (possibly any collections in Kotlin) I could see two options mapOf() and emptyMap() (listOf() and emptyList() for a list).
Basically, the mapOf is nothing but an inline function that returns emptyMap().
#kotlin.internal.InlineOnly
public inline fun <K, V> mapOf(): Map<K, V> = emptyMap()
What is preferred over another and why does Kotlin expose both?
It's a specialized overload of mapOf(vararg Pair<K, V>) - there is no need to perform the size check if you're calling that function without any arguments.
As for "what's preferred over another" - whatever makes the code it's used in more readable. Performance-wise, there's no difference (as mapOf() is inline), though for the sake of consistency you might want to choose one and stick with it.

What is the difference between min and minOf in Kotlin?

In my legacy code I have this:
java.lang.Math.min(a, b)
I'd like to replace it with Kotlin equivalent, but I'm a little confused which one should I use. I found these two:
kotlin.math.min(a,b)
kotlin.comparisons.minOf(a,b)
As I noticed, both of them internally call Math.min. Is there any difference between them that I miss? Is one of them preferred for future use?
Kotlin kotlin.math.min(a,b) takes concrete types like Int, Double, Float, Long and compares them mathematically.
Kotlin kotlin.comparisons fun <T : Comparable<T>> minOf(a: T, b: T): T takes a generic type T, that extends the interface Comparable<T>. This can be used in collections to sort them, based on the implementation of Comparable on that class.
Which to use now?
As you see, kotlin.comparisons.minOf(a,b) is a more generic implementation than kotlin.math.min(a,b). If you are using number types it does not really matter which to use, as both are implemented with Math.min(a, b) as you already pointed out.

Kotlin non nullable map allows remove null

Why this code can be compiled and executed without erros?
val map = HashMap<Int, Long>()
val key :Int? = null
map.remove(key)
In MutableMap remove declared as accepting only non nullable key, so it shouldn't even compile. Is it a Kotlin type inference bug or am I missing something?
public fun remove(key: K): V?
Your code is perfectly fine as remove() allows nullable arguments - your map contents definition got nothing to it. When remove() is invoked, it would try to find matching requested key in the map and as it's not there (it's completely irrelevant why it's not there - it's valid case for key to be not present) nothing will happen. Where compiler will complain is on any attempt to put such key into your map. Then map definition kicks in and since it's known that nullable keys not allowed, such code won't even compile as this is clearly buggy code.
In this case, map.remove(key) doesn't not calls
public fun remove(key: K): V?
It calls an extension remove function:
public inline fun <#OnlyInputTypes K, V> MutableMap<out K, V>.remove(key: K): V? =
#Suppress("UNCHECKED_CAST") (this as MutableMap<K, V>).remove(key)
This function documentation says that it allows to overcome type-safety restriction of remove that requires to pass a key of type K.
It allows overcoming type-safety restriction because the key of the entry you are removing does not have to be the same type as the object that you pass into remove(key); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Any as a parameter, not just the same type as the object.
Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So, for example, according to the specification of the method, it is possible to have a MutableMap<ArrayList<Something>, Something> and call remove(key) with a LinkedList as an argument, and it should retrieve the key which is a list with the same contents. This would not be possible if this extension remove(key) didn't exist.[1]
Kotlin could warn or refuse to compile (would be good), but it doesn't (for now).
The reason for it being not as bad as it looks from a first glance is that you cannot put an Int? into a MutableMap<Int, Long> because
val map = HashMap<Int, Long>()
val key :Int? = null
map.put(key, 1) // <--- WON'T COMPILE [Type mismatch: inferred type was Int? but Int was expected]
map.remove(key)
Nevertheless, I think you are right by wondering about that method being compiled.
Eventually asking this question helped to find another question with explanation. In short, what actually happens is call of the extension function which have it's own type inference.