##############################################################################
##
## NA's via the NAtype
##
## Inspirations:
##  * R's NA's
##  * Panda's discussion of NA's:
##    http://pandas.pydata.org/pandas-docs/stable/missing_data.html
##  * NumPy's analysis of the issue:
##    https://github.com/numpy/numpy/blob/master/doc/neps/missing-data.rst
##
## NAtype is a composite type representing missingness:
## * An object of NAtype can be generated by writing NA
##
##############################################################################

"""
    NAtype

The type of a missing value, `NA`.
"""
struct NAtype
end

"""
    NA

A value denoting missingness within the domain of any type.
"""
const NA = NAtype()

Base.show(io::IO, x::NAtype) = print(io, "NA")

struct NAException <: Exception
    msg::String
end
NAException() = NAException("NA found")

Base.length(x::NAtype) = 1
Base.size(x::NAtype) = ()
Base.size(x::NAtype, i::Integer) = i < 1 ? throw(BoundsError()) : 1
Base.ndims(x::NAtype) = 0
Base.getindex(x::NAtype, i) = i == 1 ? NA : throw(BoundsError())

"""
    isna(x) -> Bool

Determine whether `x` is missing, i.e. `NA`.

# Examples

```jldoctest
julia> isna(1)
false

julia> isna(NA)
true
```
"""
isna(x::NAtype) = true
isna(x::Any) = false

# TODO: Rethink this rule
Base.promote_rule{T}(::Type{T}, ::Type{NAtype} ) = T

Base.isnan(::NAtype) = NA
