Arrow lens won't let me set a nullable property to null - kotlin

Given this (extremely simplified) code :
#optics
data class MigrationStatus(val token: String?)
val m = MigrationStatus(null)
I can call
val m1 = MigrationStatus.token.modify(m) { "some token" }
But as the argument type is a non nullable String, how can I modify token back to null ? Of course
val m2 = MigrationStatus.token.modify(m1) { null }
does not compile.
The same happens when changing token type to an Option<String> when trying to set it to None, but I avoided it as it is now deprecated (which I'm not sure I like, but that's another matter).
Did I miss something obvious ?

The #Optics compiler generates 2 optics for that property.
MigrationStatus.token & MigrationStatus.tokenNullable or MigrationStatus.tokenOption in the case of Option.
This is because there are two different Optics that are useful here.
Lens which has set & get and in this case `Lens<MigrationStatus, String?>
Optional which has set & getOption and in this case `Optional<MigrationStatus, String>
The first one is the one you'd want to use in this case to be able to set String? to null.
So MigrationStatus.tokenNullable.set(null).
The latter is more useful for the DSL, and composition of Optics since if instead of String? you had another data class or sealed class you'd want to operate on the value only in the case of not-null.
I hope that fully answers your question!

Related

Getters cannot be used to identify return type properly in Kotlin

I have a data class that has the following form:
data class ContentElementField(val type: String) {
val text: String? = null
get() = requireNotNull(field)
val style: String? = null
get() = requireNotNull(field)
val path: String? = null
get() = requireNotNull(field)
val caption: String? = null
get() = requireNotNull(field)
}
The problem arises when I want to perform the following operation:
when (it.type) {
"text" -> TextElement(Text(it.text), Style(it.style))
"image" -> ImageElement(Path(it.path), Caption(it.caption))
}
The compiler warns me about that You cannot send a nullable type to a function that does not accept nullable arguments.
Even if the field is signed to be nullable, its getter is signed to be not nullable, though.
The compiler should use getters to resolve whether to give this warning.
What would you offer to get around this problem?
It doesn't matter if your getter happens to crash if the current value is null - the type is still nullable, the getter's return type is still String?.
Why are you doing this anyway? Why not just make the fields non-null as normal and let a null assignment throw the exception instead? That way you won't have to fight the type system.
If what you have in mind is different and this was just meant to be a simple example, then you have a few options:
Use !! at the call site since you're guaranteeing it's not null
"text" -> TextElement(Text(it.text!!), Style(it.style))
Expose the private nullable property through a non-null one:
// I see people do this a lot in Activities and Fragments even though
// they should probably just be making the one property lateinit instead
private val _text: String? = whatever
val text: String get() = requireNotNull(_text)
Maybe look at Kotlin contracts which allow you to make guarantees to the compiler about values (no example because I've never used it)
It's not really clear what you actually want to do though, or why this is useful. Your example is even using vals and assigning null to them. Whatever your real use case is, there's probably a better way.
(Also in case you're not aware, properties that aren't constructor parameters aren't included in the basic data class behaviour, i.e. its equals/hashCode/toString implementations. Another reason just making the types non-null helps, you can stick them in the constructor instead of having to do this logic)

Different results on similar code with safe call operator in Kotlin

I'm new to Kotlin and these two below codes give different results.
fun main() {
var name: String? = "Rajat"
name = null
print(name?.toLowerCase())
}
Output: Compilation Error (illegal access operation)
fun main() {
var name: String? = null
print(name?.toLowerCase())
}
Output: null
When you do this assignment:
name = null
name is smart casted to Nothing?, which is problematic. Nothing is the subtype of every type, and so you become able to call any accessible extension functions of any type, according to the overload resolution rules here.
Compare:
fun main() {
var name: String? = "Denis"
name = null
print(name?.myExtension()) // works
val nothing: Nothing? = null
print(nothing?.myExtension()) // also works
}
fun Int.myExtension(): Nothing = TODO()
Note that allowing you to call any extension function on Nothing is perfectly safe - name is null anyway, so nothing is actually called.
Char.toLowerCase and String.toLowerCase happen to be two of the extension functions that are accessible, and you can call both on name, which is now a Nothing?. Therefore, the call is ambiguous.
Note that smart casts only happens in assignments, not in initialisers like var name: String? = null. Therefore, name is not smart casted to Nothing? in this case:
fun main() {
var name: String? = null
print(name?.toLowerCase()) // better to use lowercase(), toLowerCase is deprecated!
}
For the reason why, see my answer here.
The actual error on your first example is
Overload resolution ambiguity: public inline fun Char.toLowerCase(): Char defined in kotlin.text public inline fun String.toLowerCase(): String defined in kotlin.text
Looks like the Kotlin compiler is being too smart for its own good here. What's happening, is that on the second example, you are explicitly defining a variable of type String? and assigning it some value (null in this case, but that doesn't matter).
On the second example, you are defining a variable of some type, and then telling the compiler "hey, after this assignment, name is always null". So then it remembers the more-specific "name is null" instead of "name is String?".
The standard library has two methods called toLowerCase, one on Char and one on String. Both of them are valid matches now, and the compiler is telling you it doesn't know which one to pick. In the end that won't matter, because name is null, but the compiler apparently doesn't use that final thing to throw out the method call altogether.

What can `Nothing` be used for?

I am specifically asking for the non-nullable type Nothing.
I do know that Nothing? allows us to, for example, filter null to make an overload unambiguous, but I'm struggling to think of instances where Nothing would be useful.
Nothing? can have exactly one value, null. So Nothing can have exactly no value. What's the point? Why not simply use Unit?
1. Nothing is the counterpart to Any?
Just as Any? is the base type of any other type, Nothing is the sub type of any other type (even the nullable ones).
Knowing this it becomes clear that s will be of type String in the following example, given name is a String?.
val s = name ?: throw IllegalArgumentException("Name required")
The throw expression returns Nothing, and the common base type of String and Nothing is String. That's what we want, because that's the type we want to work with.
If we would use Unit instead of Nothing the common base type would be Any, which is certainly not what we want, because it would require a cast to String afterward.
That makes sense too, because if the exception will be thrown execution cannot continue there, thus s wouldn't be used any further anyway.
2. Nothing marks code locations that can never be reached
fun foo() {
throw IllegalArgumentException("...")
println("Hey") // unreachable code
}
3. Type inference
If null is used to initialize a value of an inferred type and there's no other information to determine a more specific type, the inferred type will be Nothing?.
val x = null // 'x' has type `Nothing?`
val l = listOf(null) // 'l' has type `List<Nothing?>
Further reading
Nothing is used to tell the compiler that it will never return. For example,
fun main() {
var name: String? = null
val notNullName = name ?: fail("name was null")
println(notNullName)
}
fun fail(message: String): Nothing {
throw RuntimeException(message)
}
fun infiniteLoop(): Nothing {
while (true) {
// Nothingness
}
}

Smart cast does not cast String? to String after !value.isNullOrBlank()

I want to add to the list objects only if they have all fields, and try this code:
data class Response(val id: String, val name: String)
val list = mutableListOf<Response>()
val id : String? = "test_id"
val name : String? = "test_name"
if (!id.isNullOrBlank() and !name.isNullOrBlank()) {
list.add(Response(id, name)) // Type mismatch. Required String, Found String?
}
But I got an error: Type mismatch. Required String, Found String?
What is the correct (and compact) way to do it?
As a recommendation: always try to use && instead of bitwise and to evaluate your conditions. Seldomly is there any reason to use a bitwise and (there are some use-cases, but in most cases you just want to have a short-circuiting evaluation of your conditions, even more so if there is some complex calculation/service calls within one of the functions).
From what I see and expect the smart cast should work, even more so because && already does work. I didn't find any appropriate or matching issue, so you may want to open a new ticket for this.
Note also that the smart cast should work due to the Kotlin contract defined in isNullOrBlank which basically checks whether the value underneath is null so it might be related to the evaluation of the contracts, and/or the inlining of the function and/or something of the former combined with the bitwise and.
An alternative approach I usually like to do, without involving smart casts:
val id : String = getId()?.takeIf { it.isNotBlank() } ?: return
val name : String = getName()?.takeIf { it.isNotBlank() } ?: return
list.add(Response(id, name))
Here you verify the value while you get it and abort if you cannot use it.

Difference between Any type and Generics in Kotlin

Suppose I have the following function definition.
fun<T> parse(a: Any): T = when (a) {
is String -> a
else -> false
}
I guessed it should be valid. However, the IntelliJ IDEA linter shows a type mismatch error
That being said, I would change the return type of my parse function to Any, right? So that, what is the difference between using Any type and Generics in Kotlin? In which cases should use each of those?
I did read the following question but not understood at all about star-projection in Kotlin due to the fact I am quite new.
Your return type it defined as T, but there is nothing assuring that T and a:Any are related. T may be more restrictive than Any, in which case you can't return a boolean or whatever you provided for a.
The following will work, by changing the return type from T to Any:
fun<T> parse(a: Any): Any = when (a) {
is String -> a
else -> false
}
Any alternate option, if you really want to return type T:
inline fun<reified T> parse(a: Any): T? = when (a) {
is T -> a
else -> null
}
Your example does not use T and thus it's nonsense to make it generic anyways.
Think about this: As a client you put something into a function, e.g. an XML-ByteArray which the function is supposed to parse into an Object. Calling the function you do not want to have it return Any (Casting sucks) but want the function return the type of the parsed object. THIS can be achieved with generics:
fun <T> parse(xml: ByteArray): T {
val ctx: JAXBContext = JAXBContext.newInstance()
val any = ctx.createUnmarshaller().unmarshal(ByteArrayInputStream(xml))
return any as T
}
val int = parse<Int>("123".toByteArray())
val string = parse<String>("123".toByteArray())
Look at the method calls: You tell with generics what type is expected to be returned. The code is not useful and only supposed to give you an idea of generics.
I guessed it should be valid
Why would it be? You return a String in one branch and a Boolean in the other. So the common type for the entire when expression is Any and that's what the compiler (and IDEA) says is "found". Your code also says it should be T (which is "required").
Your generic method should work for any T, e.g. for Int, but Any isn't a subtype of Int and so the code isn't valid.
So that, what is the difference between using Any type and Generics in Kotlin?
This is like asking "what is the difference between using numbers and files": they don't have much in common in the first place. You use generics to write code which can work with all types T (or with all types satisfying some constraint); you use Any when you want the specific type Any.