To State
The non-nullability and (within a module) inference of immutability of Kotlin is great. But in some situations it is not possible to start with immutable properties with the correct nullability. This makes the code for further processing more cumbersome than needed. For example: having to add additional local variables and/or not-null assertions (!!/requireNotNull(...)/checkNotNull(...)).
In short: ToState provides a concise, type-safe way to convert an object to another object with (if desired) non-null, immutable properties. But it is more flexible than that:
The source does not have to be an object, but can actually be any set of values.
The construction of an object is not required, any function (not just constructors) can be invoked when the values are in the correct state.
See com.zybber.to_state for the main function toStateOrNull(...) and the property states, and com.zybber.to_state.property_state_test for a number of basic property state tests.
What ToState Is Not
ToState only has a few use cases. Below are some of the use cases ToState is not intended for.
Validation
Although you can check the state of a set of values with ToState, it is not a validation library. It can only return a Boolean value indicating whether all conditions are met. There is no output with structured error messages indicating which conditions were not met.
Think of ToState as a require...(...) or check...(...) function to assert that the input is in a state that can be processed. But without the exception, so the handling of an unexpected state can be done using standard control flow.
Transformations
It is not possible to transform the input values to other types after a property state has been determined. If you need to do transformations, you can write a function that returns Correct if the transformations are successful.
For example, to convert any value to a finite Float:
fun isFiniteFloat(value: Any?): PropertyState<Float> {
val floatOrNull = (value as? String)?.toFloatOrNull()?.takeIf { it.isFinite() }
return if (floatOrNull == null) Incorrect else Correct(floatOrNull)
}