Can the Kotlin Compiler Require non-null assertion? - kotlin

Can the Kotlin Compiler require non-null assertions?
for example. I'm getting a query result from JOOQ, and the relting type is Record!. But the compiler allows me to access members without checking for null.
val orderResult = RowanDatabaseConnector.runInContext(DatabaseContext.BOOKSTORE) {
it.select(SPREE_ORDERS.asterisk())
.from(SPREE_ORDERS)
.where(SPREE_ORDERS.ID.eq(orderId.toInt()))
.fetchOne()
}
return Cart(
user = currentUser,
subTotal = orderResult.get(SPREE_ORDERS.ITEM_TOTAL).toFloat(),
taxTotal = orderResult.get(SPREE_ORDERS.ADDITIONAL_TAX_TOTAL).toFloat(),
total = orderResult.get(SPREE_ORDERS.TOTAL).toFloat(),
lineItems = listOf()
)
Is there something similar to Typescript's strictNullCheck that would require that I assert that orderResult is not null?

For platform types (any type ending with !), you can use an explicit type specification to declare whether it is supposed to be nullable or not.
If you want to make it explict that orderResult might be null, then you could write it like this:
val orderResult: Record? = /* trimmed */
Conversely, if you want to make it explicit that orderResult can't possibly be null, then you could write:
val orderResult: Record = /* trimmed *
By choosing one of these two options, the type will be Record? or Record instead of Record!. And, as such, you will get the null safety you're used to from Kotlin.

Related

Kotlin - Is it possible to check for operator precedence

Let's say I have the following class:
data class Foo(var name: String) {
operator fun plus(foo: Foo): Foo {
name += foo.name
return this
}
}
Which is then used like this:
val foo1 = Foo("1")
val foo2 = Foo("2")
val foo3 = Foo("3")
foo1+foo2+foo3
println(foo1.name) // 123
Now, what if I wanted different behavior depending on whether the operations are chained like this:
foo1+foo2+foo3
Or like this:
(foo1+foo2)+foo3
In both cases foo1's name would be 123, but let's say that in the second case I would want foo1's name to be (12)3.
Is there a way to add a condition to the plus function, which checks whether the foo that it is called on originates from within parentheses/has a higher precedence or not.
No, that is not possible, because that makes no sense tbh. The compiler will just resolve the order of operations, brackets just indicate that 1+2 should resolve first and the result should be added to 3. There is no concept of brackets anymore in that result, you just have the outcome.
What is confusing you is that you are abusing the plus function to do something people wouldn't expect. You should not use the plus function to mutate the object it is called upon, this is not expected behaviour. Users will expect the plus function to return a new object not a mutation of the left or right operand.
In your case:
operator fun plus(foo: Foo): Foo {
return Foo(name += foo.name)
}
Don't do something different lest you want other people to be really confused. Fyi plusAssign is a mutating function, but still wouldn't allow you to do what you want. To achieve that you'd probably need to write your own parser and parse the operands and operators yourself.

Kotlin "let{}" Doesn't Provide Smart Cast

Just learned Kotlin Nullable type and let{} function which replaces the if (xx != null) {} operation.
But one thing I am confused is that, we all know and I Think the Complier Should Know that when we use let{}, the variable/object who is calling this function is possiblly null, however the complier still requires me to add the safe call operator "?" after the variable name instead of providing Smart Cast like it does in if (xx != null) {}. Why?
My piece of code:
fun main() {
var number1: Int? = null
//val number2 = number1.let { it + 1 } ?: 10 //doesn't work, not quite "smart"
val number2 = number1?.let { it + 1 } ?: 10 //works, must have "?"
println(number1)
println(number2)
}
You've already got answers in the comments, but just to explain the ? thing...
Kotlin lets you make null-safe calls on nullable variables and properties, by adding ? before the call. You can chain this too, by doing
nullableObject?.someProperty?.someFunction()
which evaluates nullableObject, and if it's non-null it evaluates the next bit, otherwise the whole expression evaluates to null. If any part of the chain evaluates as null, the whole expression returns null.
So it has this short-circuiting effect, and you can use the elvis "if null" operator to create a default value if you can't evaluate the whole chain to a non-null result:
nullableObject?.nullableProperty?.someFunction() ?: defaultAction()
and once you introduce the null check in the chain, you have to add it for every call after that - it's basically propagating either the result of the previous bit, or the null it resolved to, so there's a null check at each step
The let block is just a scope function - you use it on a value, so you can run some code either using that value as a parameter or a receiver (a variable or this basically). It also has the side effect of creating a new temporary local variable holding that value, so if the original is a var it doesn't matter if that value changes, because your let code isn't referring to that variable anymore.
So it's useful for doing null checks one time, without worrying the underlying value could become null while you're doing stuff with it:
nullableVar?.let { it.definitelyIsNotNull() }
and the compiler will recognise that and smart cast it to a non-null type. An if (nullableVar != null) check can't guarantee that nullableVar won't be null by the time the next line is executed.

In OCaml using Base, how do you construct a set with elements of type `int * int`?

In F#, I'd simply do:
> let x = Set.empty;;
val x : Set<'a> when 'a : comparison
> Set.add (2,3) x;;
val it : Set<int * int> = set [(2, 3)]
I understand that in OCaml, when using Base, I have to supply a module with comparison functions, e.g., if my element type was string
let x = Set.empty (module String);;
val x : (string, String.comparator_witness) Set.t = <abstr>
Set.add x "foo";;
- : (string, String.comparator_witness) Set.t = <abstr>
But I don't know how to construct a module that has comparison functions for the type int * int. How do I construct/obtain such a module?
To create an ordered data structure, like Map, Set, etc, you have to provide a comparator. In Base, a comparator is a first-class module (a module packed into a value) that provides a comparison function and a type index that witnesses this function. Wait, what? Later on that, let us first define a comparator. If you already have a module that has type
module type Comparator_parameter = sig
type t (* the carrier type *)
(* the comparison function *)
val compare : t -> t -> int
(* for introspection and debugging, use `sexp_of_opaque` if not needed *)
val sexp_of_t : t -> Sexp.t
end
then you can just provide to the Base.Comparator.Make functor and build the comparator
module Lexicographical_order = struct
include Pair
include Base.Comparator.Make(Pair)
end
where the Pair module provides the compare function,
module Pair = struct
type t = int * int [##deriving compare, sexp_of]
end
Now, we can use the comparator to create ordered structures, e.g.,
let empty = Set.empty (module Lexicographical_order)
If you do not want to create a separate module for the order (for example because you can't come out with a good name for it), then you can use anonymous modules, like this
let empty' = Set.empty (module struct
include Pair
include Base.Comparator.Make(Pair)
end)
Note, that the Pair module, passed to the Base.Comparator.Make functor has to be bound on the global scope, otherwise, the typechecker will complain. This is all about this witness value. So what this witness is about and what it witnesses.
The semantics of any ordered data structure, like Map or Set, depends on the order function. It is an error to compare two sets which was built with different orders, e.g., if you have two sets built from the same numbers, but one with the ascending order and another with the descending order they will be treated as different sets.
Ideally, such errors should be prevented by the type checker. For that we need to encode the order, used to build the set, in the set's type. And this is what Base is doing, let's look into the empty' type,
val empty' : (int * int, Comparator.Make(Pair).comparator_witness) Set.t
and the empty type
val empty : (Lexicographical_order.t, Lexicographical_order.comparator_witness) Set.t
Surprisingly, the compiler is able to see through the name differences (because modules have structural typing) and understand that Lexicographical_order.comparator_witness and Comparator.Make(Pair).comparator_witness are witnessing the same order, so we can even compare empty and empty',
# Set.equal empty empty';;
- : bool = true
To solidify our knowledge lets build a set of pairs in the reversed order,
module Reversed_lexicographical_order = struct
include Pair
include Base.Comparator.Make(Pair_reveresed_compare)
end
let empty_reveresed =
Set.empty (module Reversed_lexicographical_order)
(* the same, but with the anonyumous comparator *)
let empty_reveresed' = Set.empty (module struct
include Pair
include Base.Comparator.Make(Pair_reveresed_compare)
end)
As before, we can compare different variants of reversed sets,
# Set.equal empty_reversed empty_reveresed';;
- : bool = true
But comparing sets with different orders is prohibited by the type checker,
# Set.equal empty empty_reveresed;;
Characters 16-31:
Set.equal empty empty_reveresed;;
^^^^^^^^^^^^^^^
Error: This expression has type
(Reversed_lexicographical_order.t,
Reversed_lexicographical_order.comparator_witness) Set.t
but an expression was expected of type
(Lexicographical_order.t, Lexicographical_order.comparator_witness) Set.t
Type
Reversed_lexicographical_order.comparator_witness =
Comparator.Make(Pair_reveresed_compare).comparator_witness
is not compatible with type
Lexicographical_order.comparator_witness =
Comparator.Make(Pair).comparator_witness
This is what comparator witnesses are for, they prevent very nasty errors. And yes, it requires a little bit of more typing than in F# but is totally worthwhile as it provides more typing from the type checker that is now able to detect real problems.
A couple of final notes. The word "comparator" is an evolving concept in Janestreet libraries and previously it used to mean a different thing. The interfaces are also changing, like the example that #glennsl provides is a little bit outdated, and uses the Comparable.Make module instead of the new and more versatile Base.Comparator.Make.
Also, sometimes the compiler will not be able to see the equalities between comparators when types are abstracted, in that case, you will need to provide sharing constraints in your mli file. You can take the Bitvec_order library as an example. It showcases, how comparators could be used to define various orders of the same data structure and how sharing constraints could be used. The library documentation also explains various terminology and gives a history of the terminology.
And finally, if you're wondering how to enable the deriving preprocessors, then
for dune, add (preprocess (pps ppx_jane)) stanza to your library/executable spec
for ocamlbuild add -pkg ppx_jane option;
for topelevel (e.g., ocaml or utop) use #require "ppx_jane";; (if require is not available, then do #use "topfind;;", and then repeat).
There are examples in the documentation for Map showing exactly this.
If you use their PPXs you can just do:
module IntPair = struct
module T = struct
type t = int * int [##deriving sexp_of, compare]
end
include T
include Comparable.Make(T)
end
otherwise the full implementation is:
module IntPair = struct
module T = struct
type t = int * int
let compare x y = Tuple2.compare Int.compare Int.compare
let sexp_of_t = Tuple2.sexp_of_t Int.sexp_of_t Int.sexp_of_t
end
include T
include Comparable.Make(T)
end
Then you can create an empty set using this module:
let int_pair_set = Set.empty (module IntPair)

Why should I assign the data type of variables in Kotlin if giving it values initially it is capable of inferring the data type of the variable

As mentioned in the code provided. My question is if there is any advantage by mentioning the data type early on when I am just giving the variable it's value. As said in line 2 it can understand that it is int type.
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided.
A few reasons you might want to specify a type explicitly:
You want a supertype of the value (perhaps so you can to reassign it later), e.g.:
var myList: List<String> = ArrayList<String>()
You want to protect against changes to other code (especially if it's outside your control), e.g.:
val x: MustBeThisType = SomeLibrary.getValue()
(That would give an error if SomeLibrary.getValue() ever changes to returns something other than MustBeThisType or a subtype.)
You want to avoid an explicit numeric conversion, e.g.:
val x: Long = 2
instead of:
val x = 2.toLong()
You want to make it very clear to someone reading your code (especially if that might not be in an IDE).
As Michael says, you may need to specify the nullability of types returned from Java, e.g.:
val x: String = someJavaClass.getAString() // Never returns null
None of those is particularly common in my experience, though.

What is the difference between nullabe and non-nullable type in Kotlin

Please explain the difference between nullable and non-nullable type. I am new to kotlin and i am confused. Thanks
Nullable types can hold nulls. When type is nullable the question mark is set after it's type:
val str: String? = null
Non-nullable types can't hold nulls:
val str: String = "some value"
If we try to set null value to Non-nullable type, IDE will give an error and code will not be compiled:
val str: String = null // error, the code won't compile
Here you can read more about Null Safety.
when a variable has a nullable type then the variable can have value or it can also have value null and the program will not force close like most java based programs with null pointer exeption error messages.
for example :
val data: DataResponse? = null
its more save then you use val data: String because when your data variabel dont have value or null when you use it your program not force close at that time.
you can use your data variabel like this:
your_text.text = data
and your code will not force close.
but if your code like this, it means nonNullable.
val data: DataResponse
your apps will force close at that time you use your variabel