Skip to content

Commit

Permalink
add nulled constant, closes #9364
Browse files Browse the repository at this point in the history
I used this instead of `null`, since `null` is deprecated, and could
cause confusion with general null values from other languages.
But I'm willing to change to `null` if opinion is heavily that way.
  • Loading branch information
JeffBezanson committed Feb 16, 2015
1 parent 389c909 commit c966812
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 0 deletions.
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,7 @@ export

# nullable types
isnull,
nulled,

# Macros
@__FILE__,
Expand Down
2 changes: 2 additions & 0 deletions base/nullable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Nullable{T}(value::T) = Nullable{T}(value)

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

const nulled = Nullable{Union()}()

function convert{T}(::Type{Nullable{T}}, x::Nullable)
return isnull(x) ? Nullable{T}() : Nullable{T}(convert(T, get(x)))
end
Expand Down
5 changes: 5 additions & 0 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@ Nullables

Is the ``Nullable`` object ``x`` null, i.e. missing a value?

.. data:: nulled

A constant ``Nullable`` value in the null (missing value) state. Used, for example,
as a compact way to initialize ``Nullable`` fields: ``x.field = nulled``.


System
------
Expand Down
5 changes: 5 additions & 0 deletions test/nullable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,8 @@ for T in types
@test isa(convert(Nullable{Number}, Nullable(one(T))), Nullable{Number})
@test isa(convert(Nullable{Any}, Nullable(one(T))), Nullable{Any})
end

@test isnull(nulled)
@test_throws NullException get(nulled)
@test isa(convert(Nullable{Int}, nulled), Nullable{Int})
@test isnull(convert(Nullable{Int}, nulled))

7 comments on commit c966812

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

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

I really don't like calling it nulled :-. I'd rather call it NULL or null or something. Another option would be providing conversions from nothing to Nullable{T} that construct null instances. Of course, that would make constructing Nullable{Void} objects ambiguous, but that's a pretty strange thing to want to do.

@JeffBezanson
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 votes for null seem to be rolling in.

Using nothing is an interesting idea; it's very tempting to make do with less nothingness. It also makes Nullable code more interoperable with code that uses Union(T,Void).

Nullable is a great example of wrapping vs. converting. If you do Nullable(nothing) under this scheme, it will still work fine and give a Nullable{Void}. Only convert(Nullable, nothing) might be surprising. However, that same issue already exists, since if you do convert(Nullable, x), but x is already Nullable and you didn't expect it to be, you get the same surprise. For example

function attempt(action)
  try
    convert(Nullable, action())
  catch
    Nullable{Union()}()
  end
end

If the action succeeds but returns an empty Nullable, this code makes it look like the action failed. The fix is simply to use Nullable(action()) instead. And all of this holds true with or without conversions from nothing to empty Nullables, so I think there's a good chance we should go for it.

@nalimilan
Copy link
Member

Choose a reason for hiding this comment

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

I think we was discussed elsewhere: with return type declarations, your example above could be automatically converted if the function was declared as returning a Nullable{typeof(action)} object. Thus code could use return null or return nothing without any typing issues.

Crazy idea: could nothing be made equal to Nullable{Union()}() , making Void an alias for Nullable{Union()}?

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

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

Currently, there's the issue that Nullable{Union()}() is 16 bytes whereas nothing is zero bytes. If we could figure out a way to make Nullable{Union()}() zero bytes, then it could be workable.

@JeffBezanson
Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately not (yet), since Nullable is a struct with a Bool, and Void needs to be size 0. Good thought though.

You'd have to be careful with a return type declaration, since that somewhat hides the issue of conversion vs. wrapping. But generally yes, conversion via return type declarations combines well with this, since you could return nothing (perhaps even by accident!) without type instability.

@JeffBezanson
Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I think I'm sold on replacing this commit with a conversion from Void. Any objections?

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

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

Nope.

Please sign in to comment.