From 529893bbec15e82d1a7aa5b6acd3cb29a36ca4f0 Mon Sep 17 00:00:00 2001 From: John Myles White Date: Tue, 26 Aug 2014 18:46:44 -0700 Subject: [PATCH] Add a parametric Nullable{T} type --- base/exports.jl | 16 ++- base/nullable.jl | 50 ++++++++++ base/sysimg.jl | 3 + test/nullable.jl | 248 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 313 insertions(+), 4 deletions(-) create mode 100644 base/nullable.jl create mode 100644 test/nullable.jl diff --git a/base/exports.jl b/base/exports.jl index a9f2149c8475b..e39359ec2accd 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -61,6 +61,7 @@ export MathConst, Matrix, MergeSort, + Nullable, ObjectIdDict, OrdinalRange, PollingFileWatcher, @@ -143,6 +144,7 @@ export KeyError, LoadError, MethodError, + NullException, ParseError, ProcessExitedException, SystemError, @@ -195,7 +197,7 @@ export ≠, !==, ≡, - ≢, + ≢, $, %, &, @@ -962,7 +964,7 @@ export rfft, xcorr, -# numerical integration +# numerical integration quadgk, # iteration @@ -1008,7 +1010,7 @@ export toc, toq, -#dates +# dates Date, DateTime, now, @@ -1226,7 +1228,7 @@ export # shared arrays sdata, indexpids, - + # paths and file names abspath, basename, @@ -1320,6 +1322,12 @@ export unsafe_pointer_to_objref, unsafe_store!, +# nullable types + isnull, + NotNull, + Null, + unsafe_get, + # Macros @__FILE__, @b_str, diff --git a/base/nullable.jl b/base/nullable.jl new file mode 100644 index 0000000000000..62ffb5fb1e3dc --- /dev/null +++ b/base/nullable.jl @@ -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 diff --git a/base/sysimg.jl b/base/sysimg.jl index c9b6694af79fc..94dc3a0c1ee7f 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -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() diff --git a/test/nullable.jl b/test/nullable.jl new file mode 100644 index 0000000000000..2944744fd0e55 --- /dev/null +++ b/test/nullable.jl @@ -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