One suggestion is to have it named as the more intuitive Optional{T}.
Júlio.
I really liked your thoughts during JuliaCon regarding the difference between Epistemological missingness(Absence of knowledge of value, but value exists) and Ontological missingness (Value does not exist) [1]. Is there a reason that this Option type is better suited to cover the former, and not the latter?
+1 for Nullable (I have a .Net background). Data{T} seems like a very generic name for a very specific concept. For people that have not read the doc and would come across code that used this construct, the name wouldn’t give the slightest hint what this might be about, whereas something like Nullable would probably point people at least in the right direction (also, much more googleable). I’m with your dislike for the name DataArray, again I think that is a generic name that doesn’t point people to what it might mean. Maybe better to rename DataArray to something like NullableArray? I guess the really nice syntax would just be that Array{Nullable{Float64}} would end up creating the same thing as a DataArray right now, but as far as I understand the type system that wouldn’t work, right?
Cheers, David
My previous comments on option types versus union types for DataArray indexing are here. To elaborate on John's summary above, my present views are:Some more specific notes WRT performance:
- In many other language, including languages that are lower-level than Julia such as Rust, option types are union types and it's the compiler's job to make them fast. Making union types fast would simply mean that the presence of Union in the output of code_typed is no longer to be dreaded.
- The option type approach forces special handling of data that could potentially be missing but isn't, and this breaks generic code. I feel it is better if, once we know a datum is not missing, it has the same concrete type as if it were known to exist in advance.
- I'm not sure how missing data ought to fit into the type hierarchy, but neither the Union(NA, T) nor Option{T} approaches seem completely ideal.
- Storing option types in an Array{Option{T}} would require nearly twice as much memory as storing them in a DataArray in most cases. While a Bool is only 1 byte, there is additional padding needed for alignment. On x86_64 for types <= 64 bits the alignment is usually the size of the type.
- As long as option types live in registers and not in memory, they should consume no additional space as compared with scalar indexing into both the data and na arrays of a DataArray, as indexing into the na array would already require a register. There are, however, some cases where the BitArray representation of NAs can be exploited for performance. In John's example code forsum above with dropna = false above, the Option type approach would read every bit of the naarray individually. It is faster to first check if any values are NA, which can be done 64 bits at a time, and throw an error if there are NAs or sum the values if not.
To address Simon’s general points, which are really good reasons to avoid jumping on the Option{T} bandwagon too soon:
* I agree that most languages use tagged union types for Option{T} rather than a wrapper type that contains a Boolean value. It’s also totally true that many compilers are able to make those constructs more efficient than Julia currently does. But what we should expect from Julia in the coming years isn’t so clear to me. (And I personally think we need to settle on a solution for representing missing data that’s viable in a year rather than viable in five years.) This is an issue that I’d really like to have input on from Jeff, Keno, Jameson or someone else involved with the internals of the compiler. Getting input from the broader community is the main reason I wanted to put a demo of OptionTypes.jl out in front of other folks.
* I’m not clear how we could come to know that a datum is not missing without a resolution step that’s effectively equivalent to the get() function for Option{T}. I agree that the enforced use of get() means that you can’t hope to use generic functions like sum on collections of Option{T}. But I’m also not sure that’s such a bad thing: I think the easiest way to express to the compiler that you know that all of the entries of a DataArray are not NA is to convert the DataArray to a straight Array. But maybe you have other mechanisms for expressing this knowledge. Certainly my proposal to do conversions to Arrays isn’t the most elegant strategy. It’s just all that I’ve got so far.
* I kind of like the idea of Option{T} standing outside of the main type system in a kind of mirror type system. I’m less happy about Union(NA, T) being a super type of T, even though there are some good reasons that you’d like to view T as a specialization of Union(NA, T). But I agree that I don’t have a good feel about where missing data belongs in the type hierarchy. This is another question for which I’d love to get input from others.
Real <- Union(NAtype, Real) | / | | / | | / | Float64 <- Union(NAtype, Float64)
To address Simon’s general points, which are really good reasons to avoid jumping on the Option{T} bandwagon too soon:
* I agree that most languages use tagged union types for Option{T} rather than a wrapper type that contains a Boolean value. It’s also totally true that many compilers are able to make those constructs more efficient than Julia currently does. But what we should expect from Julia in the coming years isn’t so clear to me. (And I personally think we need to settle on a solution for representing missing data that’s viable in a year rather than viable in five years.) This is an issue that I’d really like to have input on from Jeff, Keno, Jameson or someone else involved with the internals of the compiler. Getting input from the broader community is the main reason I wanted to put a demo of OptionTypes.jl out in front of other folks.
* I’m not clear how we could come to know that a datum is not missing without a resolution step that’s effectively equivalent to the get() function for Option{T}. I agree that the enforced use of get() means that you can’t hope to use generic functions like sum on collections of Option{T}. But I’m also not sure that’s such a bad thing: I think the easiest way to express to the compiler that you know that all of the entries of a DataArray are not NA is to convert the DataArray to a straight Array. But maybe you have other mechanisms for expressing this knowledge. Certainly my proposal to do conversions to Arrays isn’t the most elegant strategy. It’s just all that I’ve got so far.
* I kind of like the idea of Option{T} standing outside of the main type system in a kind of mirror type system. I’m less happy about Union(NA, T) being a super type of T, even though there are some good reasons that you’d like to view T as a specialization of Union(NA, T). But I agree that I don’t have a good feel about where missing data belongs in the type hierarchy. This is another question for which I’d love to get input from others.
In regard to Simon’s performance points:
* Yes, memory usage alone argues strongly for working with DataArray{T} rather than Array{Option{T}}.
* Exploting tricks that make operations like anyna() faster is another good argument for keeping DataArray{T} around.
* I’m not sure how to deal with inlining concerns or the undefined reference checks. Do you have ideas for improving this within DataArrays or do we need supporting changes in the compiler?

Sounds good. We could maybe include the Nullable type in Base and thus avoid the issue of what to call the module. If we need a module, how about Nullables? Although, I have to say that name makes me think of The Expendables.
<expendables.png>
Sounds good. We could maybe include the Nullable type in Base and thus avoid the issue of what to call the module. If we need a module, how about Nullables? Although, I have to say that name makes me think of The Expendables.
<expendables.png>