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 529893b
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 4 deletions.
16 changes: 12 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,12 @@ export
unsafe_pointer_to_objref,
unsafe_store!,

# nullable types
isnull,
NotNull,
Null,
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

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

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

function show{T}(io::IO, x::Nullable{T})
if x.isnull
@printf(io, "Null(%s)", repr(T))
else
@printf(io, "NotNull(%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())

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

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

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

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

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

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

# show{T}(io::IO, x::Nullable{T})
io = IOBuffer()
for (i, T) in enumerate(types)
x1 = Null(T)
x2 = NotNull(zero(T))
x3 = NotNull(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 = Null(T)
x2 = NotNull(zero(T))
x3 = NotNull(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 = Null(T)
x2 = NotNull(zero(T))
x3 = NotNull(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 = Null(T)
x2 = NotNull(zero(T))
x3 = NotNull(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 = Null(T)
x2 = NotNull(zero(T))
x3 = NotNull(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 = Null(T)
x2 = Null(T)
x3 = NotNull(zero(T))
x4 = NotNull(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 = Null(T)
x2 = Null(T)
x3 = NotNull(zero(T))
x4 = NotNull(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 = Null(T)
x2 = Null(T)
x3 = NotNull(zero(T))
x4 = NotNull(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

0 comments on commit 529893b

Please sign in to comment.