Skip to content

Commit

Permalink
Add a parametric Nullable{T} type
Browse files Browse the repository at this point in the history
  • Loading branch information
johnmyleswhite committed Aug 27, 2014
1 parent 5999f53 commit f983e0e
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 6 deletions.
14 changes: 10 additions & 4 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export
MathConst,
Matrix,
MergeSort,
Nullable,
ObjectIdDict,
OrdinalRange,
PollingFileWatcher,
Expand Down Expand Up @@ -143,6 +144,7 @@ export
KeyError,
LoadError,
MethodError,
NullException,
ParseError,
ProcessExitedException,
SystemError,
Expand Down Expand Up @@ -195,7 +197,7 @@ export
,
!==,
,
,
,
$,
%,
&,
Expand Down Expand Up @@ -962,7 +964,7 @@ export
rfft,
xcorr,

# numerical integration
# numerical integration
quadgk,

# iteration
Expand Down Expand Up @@ -1008,7 +1010,7 @@ export
toc,
toq,

#dates
# dates
Date,
DateTime,
now,
Expand Down Expand Up @@ -1226,7 +1228,7 @@ export
# shared arrays
sdata,
indexpids,

# paths and file names
abspath,
basename,
Expand Down Expand Up @@ -1320,6 +1322,10 @@ export
unsafe_pointer_to_objref,
unsafe_store!,

# nullable types
isnull,
unsafe_get,

# Macros
@__FILE__,
@b_str,
Expand Down
50 changes: 50 additions & 0 deletions base/nullable.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
immutable Nullable{T}
isnull::Bool
value::T

Nullable() = new(true)
Nullable(value::T) = new(false, value)
end

immutable NullException <: Exception
end

Nullable{T}(::Type{T}) = Nullable{T}()

Nullable{T}(value::T) = Nullable{T}(value)

function show{T}(io::IO, x::Nullable{T})
if x.isnull
@printf(io, "Nullable(%s)", repr(T))
else
@printf(io, "Nullable(%s)", repr(x.value))
end
end

get(x::Nullable) = x.isnull ? throw(NullException()) : x.value

get{S, T}(x::Nullable{S}, y::T) = x.isnull ? convert(S, y) : x.value

unsafe_get(x::Nullable) = x.value

isnull(x::Nullable) = x.isnull

function isequal{S, T}(x::Nullable{S}, y::Nullable{T})
if x.isnull && y.isnull
return true
elseif x.isnull || y.isnull
return false
else
return isequal(x.value, y.value)
end
end

=={S, T}(x::Nullable{S}, y::Nullable{T}) = throw(NullException())

function hash(x::Nullable, h::Uint)
if x.isnull
return h + uint(0x932e0143e51d0171)
else
return hash(x.value, h + uint(0x932e0143e51d0171))
end
end
3 changes: 3 additions & 0 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ importall .Profile
include("Dates.jl")
import .Dates: Date, DateTime, now

# nullable types
include("nullable.jl")

function __init__()
# Base library init
reinit_stdio()
Expand Down
1 change: 1 addition & 0 deletions doc/manual/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
linear-algebra
networking-and-streams
parallel-computing
nullable-types
interacting-with-julia
running-external-programs
calling-c-and-fortran-code
Expand Down
103 changes: 103 additions & 0 deletions doc/manual/nullable-types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
.. _man-nullable-types:

*******************************************
Nullable Types: Representing Missing Values
*******************************************

In many settings, you need to interact with a value of type ``T`` that may or
may not exist. To handle these settings, Julia provides a parametric type
called ``Nullable{T}``, which can be thought of as a specialized container
type that contains either zero or one values. ``Nullable{T}`` provides a
minimal interface designed to ensure that interactions with missing values
are safe. At present, the interface consists of five possible interactions:

- Construct a ``Nullable`` object.
- Check if an ``Nullable`` object has a missing value.
- Access the value of a ``Nullable`` object with a guarantee that a
``NullException`` will be thrown if the object's value is missing.
- Access the value of a ``Nullable`` object with a guarantee that a default
value of type ``T`` will be returned if the object's value is missing.
- Access the value of a ``Nullable`` object with no guarantees of safety if
the object's value turns out to be missing.

Constructing ``Nullable`` objects
---------------------------------

To construct an object representing a missing value of type ``T``, use the
``Nullable(::Type{T})`` function:

.. doctest::

x1 = Nullable(Int)
x2 = Nullable(Float64)
x3 = Nullable(Vector{Int})

To construct an object representing a non-missing value of type ``T``, use the
``Nullable(x::T)`` function:

.. doctest::

x1 = Nullable(1)
x2 = Nullable(1.0)
x3 = Nullable([1, 2, 3])

Note the core distinction between these two ways of constructing a ``Nullable``
object: in one style, you provide a type, ``T``, as an argument; in the other
style, you provide a single value of type ``T`` as an argument.

Checking if an ``Nullable`` object has a value
----------------------------------------------

You can check if a ``Nullable`` object has any value using the ``isnull``
function:

.. doctest::

isnull(Nullable(Float64))
isnull(Nullable(0.0))

Safely accessing the value of an ``Nullable`` object
----------------------------------------------------

You can safely access the value of an `Nullable` object using the `get`
function:

.. doctest::

get(Nullable(Float64))
get(Nullable(1.0))

If the value is not present, as it would be for ``Nullable(Float64)``, a
``NullException`` error will be thrown. The error-throwing nature of the
``get`` function ensures that any attempt to access a missing value immediately
fails.

In cases for which a reasonable default value exists that could be used
when a ``Nullable`` object's value turns out to be missing, you can provide this
default value as a second argument to ``get``:

.. doctest::

get(Null(Float64), 0)
get(NotNull(1.0), 0)

Note that this default value will automatically be converted to the type of
the ``Nullable`` object that you attempt to access using the `get` function.
For example, in the code shown above the value ``0`` would be automatically
converted to a ``Float64`` value before being returned. The presence of default
replacement values makes it easy to use the ``get`` function to write
type-stable code that interacts with sources of potentially missing values.

Unsafely accessing the value of an ``Nullable`` object
------------------------------------------------------

When you can prove that a ``Nullable`` object is not null, you can access its
value without safety guards using ``unsafe_get``:

.. doctest::

unsafe_get(Null(Float64))
unsafe_get(NotNull(1.0))

Note that the behavior of this function is formally undefined when the input
object is null.
Loading

0 comments on commit f983e0e

Please sign in to comment.