Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a parametric Nullable{T} type #8152

Merged
merged 1 commit into from
Sep 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You really don't care for one-liners, do you?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't fit on 80 characters, so I can't read it on my devserver. I'm not a fan of the one-liner syntax when it's split across multiple lines.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I think that 80 lines chars is pretty short these days, but that's ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 80 char rule is alive and aggressively enforced at Facebook.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

80 char is great for having two columns on a 13 inch laptop. It is also great for viewing on a 7 inch tablet.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

52 char is great for having three columns on a 13 inch laptop.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

short lines are also necessary for when working on windows, since longer ones don't fit on the screen (console)


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
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I often like using the getindex syntax for this (x = Nullable(a); return x[])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could implement that, although it wouldn't handle the default case.


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