Shouldn't isempty(nothing) return true?

As subject says, instead of

isempty(nothing)
ERROR: MethodError: no method matching start(::Void)
1 Like

nothing is not a container or an iterable, it is a value. While some languages conflate these things, Julia distinguishes the empty tuple (), the boolean false, and special values like nothing (which, are in fact, nothing special, just some conventions make them seem so, cf Missings.missing).

4 Likes

2 posts were split to a new topic: Rhetorical technique side discussion

I don’t know what “Julia” is expecting.

Personally, I hope that newcomers recognize the fact that languages are different, and when learning a new language, it is inevitable that some initial assumptions that are invalidated. Every single design choice invalidates someone’s expectations, so this is not a useful guideline. Other aspects, eg consistency and clarify, are more important.

In this case is better to have a third answer, eg an error indicating that the question does not make sense. Only containers can contain things.

7 Likes

Sorry wanting to know if a variable contains something or not does make a lot of sense me. Period, no more technical details. Is it empty or not?

I do have a solution for this but it’s my firm believe that Julia is making a big mistake making the life of users harder than it needs.

function isempty_(arg)
	if (arg == nothing)
		return true
	end
	empty = false
	try
		empty = isempty(arg)
	end
	return empty
end

Why does it make sense to ask if nothing is empty?

2 Likes

Personally, I find the “box” mental model for variables to be wholly confusing and not helpful — especially for newcomers to the language. Julia’s variables don’t really “contain” values. They’re just names. That’s it.

3 Likes

For example because findfirst may return a nothing and I find the most reasonable to ask

isempty(findfirst(...))

1 Like

Why wouldn’t you write findfirst(...) == nothing?

2 Likes

And why wouldn’t isempty(nothing) give the obvious answer, which is true?

If you want to know if a value is nothing the obvious way to ask that is to check if it’s equal to nothing. How is asking if it’s empty obvious? It seems equally possible that nothing would be a 1-element container containing only itself, which means that it’s not empty.

5 Likes

One thing that could maybe be useful is some sort of trait that would tell whether I’m allowed to call isempty on a given type: say a function iscontainer that would specify whether the argument is a container and so if I can try checking if it has elements. Does something like that exist? It would be along the lines of the Base.HasLength style tools to know whether one can call length on some iterator, for example.

isempty is part of the extended iteration protocol. Now, nothing isn’t iterable, but if it was it would be a scalar for all intents and purposes. Scalars iterate themselves and therefore they are not empty.

In that case it seems a bit odd to me that

julia> isempty(3)
false

If anything, shouldn’t isempty(nothing) be false?

See
https://github.com/JuliaLang/julia/issues/7903
https://github.com/JuliaLang/julia/pull/19700

IMO this is indeed a wart, but opinions are divided on this.

2 Likes

I have to say that I’m not a huge fan of Numbers being iterable either. Even leaving aside whether this makes sense from a mathematical perspective, it seems like a ripe opportunity for creating code with unintended behavior.

I think the reason we may be stuck with it forever is that expressions like 1 .+ 1 are just too convenient. It makes it far easier to make all sorts of code generic and I have found myself using it rather often.

As far as nothing goes, one might think of Number, Missing and Nothing as implementing some sort of “4-valued logic” where the default value for missing is missing and the default value for nothing is “throw error”. I think I’m ok with that.

To whatever extent it makes sense for nothing to have container-like properties, it definitely seems that, in light of the above isempty(nothing) should return false, if anything. Would isempty([nothing]) be true? What about isempty([nothing, nothing])? isempty(nothing) being true just seems totally inconsistent.

This is the kind of feature request that sounds great until you actually use it. It’s like the mass of things in Python that are false.

http://www.thomas-cokelaer.info/tutorials/python/boolean.html#what-is-false

You start by going “shouldn’t the empty string mean the same thing as false? Then you’d be able to do if str and it would just work because it returns false. Isn’t that intuitive?”. Then you start doing the MATLAB “wouldn’t it be nice if vectors were just 1-column matrices, and then you’d be able to easily switch between the two ideas? Aren’t they intuitively just a 1-column matrix?”.

These things are false equivalences, usually based on an isomorphism. The empty string is in some sense “false” if you map the space of strings to the space of booleans. It is the identity element and all of that. But that’s not the same thing as it actually being false.

The vote is always for simple… until it hurts you. if str is great, until str was accidentally an empty array instead of a string… and your program still ran! Isn’t it nice it ran? No, because the output is wrong, and now you have no idea everything keeps running perfectly fine but spits out non-sense. This is why in software engineering communities the lack of type-safety in languages like Python and Javascript are seen as such a huge issue: it naturally causes bugs which not even the runtime can find. The only way to find it is a human or a test checking the output!

Since safety is becoming such a big deal in programming (Rust’s key feature is that it’s “safe”, Haskell is all about type-safety), wouldn’t hordes of programmers want to flock to a language with type-safety? The issue has always been that this type of feature is stuck is the realm of statically compiled languages, so there hasn’t been a good answer. But Julia is dynamic, interpreted, and has key elements of type-safety (checked mostly at runtime, but maybe static checks could be in the future?). That’s a great feature to pitch!

This kind of feature is then against one of the big reasons to use Julia. isempty is part of the iteration protocol. Why not pun it to mean false on some non-iterables like nothing? The reason of course is that it will cause code that was meant for iterables to silently work and throw mysterious values on non-iterables. This is bad for scaling your code, like really really bad!

Lastly, in Julia its really not a big deal. Why not use:

x == nothing || isempty(x)

? Since nothing is a singleton of type Void, if your code is type stable the x==nothing part is computed at compile-time (if type-stable), and thus it doesn’t even have a runtime cost. So it’s a free computation that you can use to explicitly say you’re including Void with iterables. If you really want to save characters, just define isnempty.

20 Likes

I think that joa-quim expect more compact protocol from findfirst.

You could do

if contains(A, k) 
  r = findfirst(k, A)
end

but I think it has better performance if you just call findfirst and check result if it found anything.

But findfirst seems to be bad designed because it has problem with Dict(nothing=>1), findnext for Dict has not meaning and there is not simple function (joa-quim expected something like isempty) to check return value if something was found.

BTW:
findfirst on Set is not defined although [i for i in Set] is doable.

Analyzing it I found interesting error message:

julia> next(Set([1,2,3]),17)
ERROR: BoundsError: attempt to access 16-element Array{Int64,1} at index [17]

I am constantly getting annoyed that I can’t just half-ass my code like I can in python by asking if myvariable and have it pretend to be false for empty strings, empty arrays, regexes that don’t match etc.

But once I actually make it work, I’m usually grateful in hindsight that the language forced me to be more clear and explicit, and it’s (usually) not even that much more verbose.

10 Likes

You can make a version of isempty return true for voids if you like
I had a similar trouble with isnumber(type numeric)

module xmodifier
  function isempty(x::Void)
    return(true)
    end;

  function isempty(x)
    return(Base.isempty(x));
    end;
  end;
xmodifier.isempty(nothing)
> true