missing < 0 produces missing because < is a "standard" comparison operator.
Now consider a question: is missing less than 0? The answer is: we do not know. missing stands for some unknown value - it can be greater or less than 0. Therefore missing < 0 produces missing.
As you can see in the documentation of <:
this operator implements a partial order.
This means that it does not guarantee that all values are comparable. A similar (but not identical) situation is with NaN:
julia> NaN < 0.0
false
julia> NaN > 0.0
false
julia> NaN == 0.0
false
Also notice that although -0.0 and 0.0 are different floating point values we have:
julia> -0.0 < 0.0
false
Now let us discuss isless. It is needed because sometimes you want an operator that defines total order, as you can see in its documentation:
Test whether x is less than y, according to a fixed total order (defined together with isequal).
This is useful in cases you want to make sure that you can safely assume that every value is comparable. For example sort uses isless.
Now in order to make sure isless defines total order we need to define how missing is positioned in this total order. It was decided that missing is larger than any other value. Therefore isless(missing, 0) is false. Of course this is an arbitrary decision. It was made so that e.g. when you sort
a vector missing is put at the end:
julia> sort([1, missing, 2])
3-element Vector{Union{Missing, Int64}}:
1
2
missing
which is what people usually want.
Now notice the behavior of NaN and -0.0:
julia> isless(NaN, missing)
true
julia> isless(Inf, NaN)
true
julia> isless(-0.0, 0.0)
true
So as you can see NaN is greater than Inf but less than missing and -0.0 is less than 0.0. Again - since isless is a total order so distinct values have to be put in a distinct place in this total order.