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 4078c04
Show file tree
Hide file tree
Showing 5 changed files with 314 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
248 changes: 248 additions & 0 deletions test/nullable.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
types = [
Bool,
Char,
Float16,
Float32,
Float64,
Int128,
Int16,
Int32,
Int64,
Int8,
Uint16,
Uint32,
Uint64,
Uint8,
]

# Nullable{T}() = new(true)
for T in types
x = Nullable{T}()
@test x.isnull === true
@test isa(x.value, T)
end

# Nullable{T}(value::T) = new(false, value)
for T in types
x = Nullable{T}(zero(T))
@test x.isnull === false
@test isa(x.value, T)
@test x.value === zero(T)

x = Nullable{T}(one(T))
@test x.isnull === false
@test isa(x.value, T)
@test x.value === one(T)
end

# immutable NullException <: Exception
@test isa(NullException(), NullException)
@test_throws NullException throw(NullException())

# Nullable{T}(::Type{T}) = Nullable{T}()
for T in types
x = Nullable(T)
@test x.isnull === true
@test isa(x.value, T)
end

# Nullable{T}(value::T) = Nullable{T}(value)
for T in types
v = zero(T)
x = Nullable(v)
@test x.isnull === false
@test isa(x.value, T)
@test x.value === v

v = one(T)
x = Nullable(v)
@test x.isnull === false
@test isa(x.value, T)
@test x.value === v
end

p1s = [
"Nullable(Bool)",
"Nullable(Char)",
"Nullable(Float16)",
"Nullable(Float32)",
"Nullable(Float64)",
"Nullable(Int128)",
"Nullable(Int16)",
"Nullable(Int32)",
"Nullable(Int64)",
"Nullable(Int8)",
"Nullable(Uint16)",
"Nullable(Uint32)",
"Nullable(Uint64)",
"Nullable(Uint8)",
]

p2s = [
"Nullable(false)",
"Nullable('\0')",
"Nullable(float16(0.0))",
"Nullable(0.0f0)",
"Nullable(0.0)",
"Nullable(0)",
"Nullable(0)",
"Nullable(0)",
"Nullable(0)",
"Nullable(0)",
"Nullable(0x0000)",
"Nullable(0x00000000)",
"Nullable(0x0000000000000000)",
"Nullable(0x00)",
]

p3s = [
"Nullable(true)",
"Nullable('\x01')",
"Nullable(float16(1.0))",
"Nullable(1.0f0)",
"Nullable(1.0)",
"Nullable(1)",
"Nullable(1)",
"Nullable(1)",
"Nullable(1)",
"Nullable(1)",
"Nullable(0x0001)",
"Nullable(0x00000001)",
"Nullable(0x0000000000000001)",
"Nullable(0x01)",
]

# show{T}(io::IO, x::Nullable{T})
io = IOBuffer()
for (i, T) in enumerate(types)
x1 = Nullable(T)
x2 = Nullable(zero(T))
x3 = Nullable(one(T))
show(io, x1)
takebuf_string(io) == p1s[i]
show(io, x2)
takebuf_string(io) == p2s[i]
show(io, x3)
takebuf_string(io) == p3s[i]
end

# get(x::Nullable)
for T in types
x1 = Nullable(T)
x2 = Nullable(zero(T))
x3 = Nullable(one(T))

@test_throws NullException get(x1)
@test get(x2) === zero(T)
@test get(x3) === one(T)
end

# get{S, T}(x::Nullable{S}, y::T)
for T in types
x1 = Nullable(T)
x2 = Nullable(zero(T))
x3 = Nullable(one(T))

@test get(x1, zero(T)) === zero(T)
@test get(x1, one(T)) === one(T)
@test get(x2, one(T)) === zero(T)
@test get(x3, zero(T)) === one(T)
end

# unsafe_get(x::Nullable)
for T in types
x1 = Nullable(T)
x2 = Nullable(zero(T))
x3 = Nullable(one(T))

@test isa(unsafe_get(x1), T)
@test isa(unsafe_get(x2), T)
@test isa(unsafe_get(x3), T)
end

# isnull(x::Nullable)
for T in types
x1 = Nullable(T)
x2 = Nullable(zero(T))
x3 = Nullable(one(T))

@test isnull(x1) === true
@test isnull(x2) === false
@test isnull(x3) === false
end

# function isequal{S, T}(x::Nullable{S}, y::Nullable{T})
for T in types
x1 = Nullable(T)
x2 = Nullable(T)
x3 = Nullable(zero(T))
x4 = Nullable(one(T))

@test isequal(x1, x1) === true
@test isequal(x1, x2) === true
@test isequal(x1, x3) === false
@test isequal(x1, x4) === false

@test isequal(x2, x1) === true
@test isequal(x2, x2) === true
@test isequal(x2, x3) === false
@test isequal(x2, x4) === false

@test isequal(x3, x1) === false
@test isequal(x3, x2) === false
@test isequal(x3, x3) === true
@test isequal(x3, x4) === false

@test isequal(x4, x1) === false
@test isequal(x4, x2) === false
@test isequal(x4, x3) === false
@test isequal(x4, x4) === true
end

# function =={S, T}(x::Nullable{S}, y::Nullable{T})
for T in types
x1 = Nullable(T)
x2 = Nullable(T)
x3 = Nullable(zero(T))
x4 = Nullable(one(T))

@test_throws NullException (x1 == x1)
@test_throws NullException (x1 == x2)
@test_throws NullException (x1 == x3)
@test_throws NullException (x1 == x4)

@test_throws NullException (x2 == x1)
@test_throws NullException (x2 == x2)
@test_throws NullException (x2 == x3)
@test_throws NullException (x2 == x4)

@test_throws NullException (x3 == x1)
@test_throws NullException (x3 == x2)
@test_throws NullException (x3 == x3)
@test_throws NullException (x3 == x4)

@test_throws NullException (x4 == x1)
@test_throws NullException (x4 == x2)
@test_throws NullException (x4 == x3)
@test_throws NullException (x4 == x4)
end

# function hash(x::Nullable, h::Uint)
for T in types
x1 = Nullable(T)
x2 = Nullable(T)
x3 = Nullable(zero(T))
x4 = Nullable(one(T))

@test isa(hash(x1), Uint)
@test isa(hash(x2), Uint)
@test isa(hash(x3), Uint)
@test isa(hash(x4), Uint)

@test hash(x1) == hash(x2)
@test hash(x1) != hash(x3)
@test hash(x1) != hash(x4)
@test hash(x2) != hash(x3)
@test hash(x2) != hash(x4)
@test hash(x3) != hash(x4)
end
5 changes: 3 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ testnames = [
"resolve", "pollfd", "mpfr", "broadcast", "complex", "socket",
"floatapprox", "readdlm", "regex", "float16", "combinatorics", "dates",
"sysinfo", "rounding", "ranges", "mod2pi", "euler", "show",
"lineedit", "replcompletions", "repl", "test", "examples", "goto", "llvmcall"
"lineedit", "replcompletions", "repl", "test", "examples", "goto",
"llvmcall", "nullable"
]
@unix_only push!(testnames, "unicode")

Expand All @@ -34,7 +35,7 @@ end

cd(dirname(@__FILE__)) do
n = 1
if net_on
if net_on
n = min(8, CPU_CORES, length(tests))
n > 1 && addprocs(n; exeflags=`--check-bounds=yes`)
blas_set_num_threads(1)
Expand Down

0 comments on commit 4078c04

Please sign in to comment.