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 Sep 20, 2014
1 parent dea5766 commit 892e746
Show file tree
Hide file tree
Showing 7 changed files with 382 additions and 6 deletions.
13 changes: 9 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 @@ -145,6 +146,7 @@ export
KeyError,
LoadError,
MethodError,
NullException,
ParseError,
ProcessExitedException,
SystemError,
Expand Down Expand Up @@ -197,7 +199,7 @@ export
,
!==,
,
,
,
$,
%,
&,
Expand Down Expand Up @@ -964,7 +966,7 @@ export
rfft,
xcorr,

# numerical integration
# numerical integration
quadgk,

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

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

# paths and file names
abspath,
basename,
Expand Down Expand Up @@ -1323,6 +1325,9 @@ export
unsafe_pointer_to_objref,
unsafe_store!,

# nullable types
isnull,

# 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}(value::T) = Nullable{T}(value)

function convert{S, T}(::Type{Nullable{T}}, x::Nullable{S})
return isnull(x) ? Nullable{T}() : Nullable(convert(T, get(x)))
end

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

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 @@ -270,6 +270,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
87 changes: 87 additions & 0 deletions doc/manual/nullable-types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.. _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 can contain 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 four 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.

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

To construct an object representing a missing value of type ``T``, use the
``Nullable{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 a function parameter; 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(Nullable{Float64}(), 0)
get(Nullable(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.
Loading

0 comments on commit 892e746

Please sign in to comment.