Nullable{Date}

203 views
Skip to first unread message

Michael Landis

unread,
Feb 8, 2016, 10:08:25 PM2/8/16
to julia-users
I'm so confused....

I thought this would be cool:
isLeapYr(yr::Int64) = yr % 400 == 0  ||  (yr % 4 == 0  &&  yr % 100 != 0)
LeapDay(yr) = isLeapYr(yr) ? Date(yr,2,29) : Nullable{Date}()

lp15 = LeapDay(2015) --> Nullable{Date}()
lp16 = LeapDay(2016) --> 2016-02-29

no surprises there, but when I test the return values ...

isnull( lp15 ) --> true
isnull( lp16 ) -->  MethodError: `isnull` has no method matching isnull( ::Date )

What IS the secret Nullable{Date} formulation?

Erik Schnetter

unread,
Feb 8, 2016, 10:11:06 PM2/8/16
to julia...@googlegroups.com
You need to write

LeapDay(yr) = isLeapYr(yr) ? Nullable(Date(yr,2,29)) : Nullable{Date}()

so that the return value is always a Nullable{Date}.

-erik
--
Erik Schnetter <schn...@gmail.com>
http://www.perimeterinstitute.ca/personal/eschnetter/

Michael Landis

unread,
Feb 8, 2016, 10:31:09 PM2/8/16
to julia...@googlegroups.com
I know there's a language theorist out there that thinks having typed Nullables confers some sort of linguistic purity, but I think it's a royal pain.  Every function needs to know whether Nullable is possible from all of it's callers?  What you gain in semantic purity, you pay for in the configuration back end.  I thought the whole point of the language was write what you mean.  Typed Nullables seem counter to that strategic objective.

Michael Landis

unread,
Feb 8, 2016, 10:32:54 PM2/8/16
to julia...@googlegroups.com
Thanks Erik.  Much appreciated...

On Mon, Feb 8, 2016 at 7:11 PM, Erik Schnetter <schn...@gmail.com> wrote:

Christopher Alexander

unread,
Feb 8, 2016, 10:35:32 PM2/8/16
to julia-users
For a null date, I usually just use Date() (Dates.Date()), which returns Jan 1, 0001.  Also, you know that there is already a method to check whether or not a year is a leap year right? 

Dates.isleapyear(y), returns a Bool.  http://docs.julialang.org/en/release-0.4/manual/dates/

Michael Landis

unread,
Feb 8, 2016, 10:38:54 PM2/8/16
to julia...@googlegroups.com
ah, cool.  Missed that.  Thanks.  The pain in returning a Nullable{Date} is that now I have to unpack it for things like dayofyear().  Nullable introduces a rat's nest of unnecessary complications.

Greg Plowman

unread,
Feb 8, 2016, 10:49:41 PM2/8/16
to julia-users
If only Nullables can be null, could we formally define this?

isnull(x::Nullable) = x.isnull     # already defined in nullable.jl
isnull
(x) = false                  # extra definition for everything else

Christopher Alexander

unread,
Feb 8, 2016, 10:55:28 PM2/8/16
to julia-users
I really like that construction!

Jacob Quinn

unread,
Feb 8, 2016, 11:14:21 PM2/8/16
to julia...@googlegroups.com
The problem with that proposition is that it introduces type instability. i.e. the user would be tempted to write code like Michael's original example like

LeapDay(yr) = isLeapYr(yr) ? Date(yr,2,29) : Nullable{Date}()

where the function `LeapDay` can actually return two different, distinct types: `Date` or `Nullable{Date}`. Those familiar with efficient codegen know that these kinds of type instabilities kill code performance.

-Jacob

Michael Landis

unread,
Feb 8, 2016, 11:57:30 PM2/8/16
to julia-users, quinn....@gmail.com
Why can't there be a base type upon which all others are based (perhaps by default)?  The base class could handle the Nullable situation and everything else would magically inherit that capability.  Making a union of a NULL and the actual type is pretty painful for the programmer.  Weren't we going for a smart compiler that would make life easier on the programmer?  What we have now is programmers going out of their way to make life easy for the compiler.  That seems back-assward to me.

Kevin Squire

unread,
Feb 9, 2016, 2:48:43 AM2/9/16
to julia...@googlegroups.com
To be fair, writing code like this is perfectly fine, if you're not concerned about performance (in the area of code that this is written in).  

I find for my own code that I'll typically write something in a clean but possibly inefficient way, and the profile and optimize where I find bottlenecks.  

Conversely, you'll find the trend in Julia mainline is to write very efficient code for the core system, and remove or discourage design patterns which produce inefficient code.

Cheers!
   Kevin

Milan Bouchet-Valat

unread,
Feb 9, 2016, 4:18:50 AM2/9/16
to julia...@googlegroups.com
Le lundi 08 février 2016 à 19:31 -0800, Michael Landis a écrit :
> I know there's a language theorist out there that thinks having typed
> Nullables confers some sort of linguistic purity, but I think it's a
> royal pain.  Every function needs to know whether Nullable is
> possible from all of it's callers?  What you gain in semantic purity,
> you pay for in the configuration back end.  I thought the whole point
> of the language was write what you mean.  Typed Nullables seem
> counter to that strategic objective.
I think one of the points of Nullable is to force you to explicitly
handle the possibility of it being NULL. For example, if LeapDay()
returns NULL, it's likely that you will have to use a different code
path in your function. If you don't and pass that NULL value to the
caller, which carries it as if it was a valid date, you'll get a
NullException somewhere down the road, typically in code which isn't
prepared to handle this at all. OTOH, if your function is documented to
return a Nullable{Date}, the caller knows that NULL can happen, and
will do something reasonably meaningful in that case.

This is the lesson from the NullPointerException nightmare in Java and
to crashes due to null pointer dereferences in C. For more details, see
e.g. the links at https://groups.google.com/d/msg/julia-dev/WD7-vQeweJE
/Wn2VqwAOLzgJ

That said, some syntactic sugar could make things nicer to use (see for
example the null-coalescing operator in C#). But in the present case,
if you are absolutely sure the result won't be null, or don't care
about getting an immediate exception when it is, you can simply write:
d = get(LeapDay(yr))


Regards

Tim Holy

unread,
Feb 9, 2016, 6:58:26 AM2/9/16
to julia...@googlegroups.com
If you don't want to use a typed-nullable, you always have this (older) option
available:

LeapDay(yr) = isLeapYr(yr) ? Date(yr,2,29) : nothing

The whole point of Nullable is to provide a means to prevent type-instability
problems. If you don't want to think about types (and don't care about the
resulting type instability), then you don't have to use Nullable.

Best,
--Tim

Michael Landis

unread,
Feb 9, 2016, 11:22:39 PM2/9/16
to julia...@googlegroups.com
# wishful thinking...
using Dates;
leapDay = isleapyear(yr) ? Date(yr,2,29) : nothing
if ! leapDay
  dow = dayofyear( leapDay )
  ...   clean and concise (thought that was the point), but we get

leapDay = isleapyear(yr) ? Nullable{Date}( Date(yr,2,29) : Nullable{Date}()
if ! isnull( leapDay )
  dow = dayofyear( get(leapDay) )
  ...

If I am dumb enough to forget to check for a null date, I deserve the exception - the code would be wrong.  Making me type two or three times as many characters, obscuring what is actually going on, ... all to eliminate NullPointerExceptions?  I have to write exception free code anyway, so all I have 'gained' is a lot of superfluous verbosity.  I'm going to side with salience over verbosity every time.  The type safe argument just doesn't sell me, sorry.

Michael Landis

unread,
Feb 9, 2016, 11:28:51 PM2/9/16
to julia...@googlegroups.com
missed a paren above (for the people that are going to past the code into a shell and try it out) - something that I am not doing.  Still, this is closer:

# wishful thinking...
using Dates;
leapDay = isleapyear(yr) ? Date(yr,2,29) : nothing
if ! leapDay
  doy = dayofyear( leapDay )
  ...   clean and concise (thought that was the point), but we get

leapDay = isleapyear (yr)?  Nullable {Date} (Date (yr, 2:29)): Nullable {Date} ()
if ! isnull( leapDay )
  doy = dayofyear( get(leapDay) )

Christopher Alexander

unread,
Feb 10, 2016, 2:09:12 AM2/10/16
to julia-users
Why couldn't you do something like this?  It is type stable:

using Base.Dates
leapDay = isleapyear(yr) ? Date(yr, 2, 29) : Date()

if leapDay != Date()
   doy = dayofyear( leapDay )
end

Again, Date() returns this: 0001-01-01

It works nicely as a "null" date.

Chris

Joshua Ballanco

unread,
Feb 10, 2016, 3:50:11 AM2/10/16
to julia...@googlegroups.com
The problem is not so much when you are (potentially) generating and handling null dates in your own code, but how one interacts with library code that may or may not return a null. Especially in a dynamically typed language, in the absence of a Nullable type the only way to determine if a method *might* return null is to check the documentation.

That said, I think dealing with Nullable’s could be made more convenient in Julia. For example, Swift’s `if let` construct gracefully handles checking null-ness and unwrapping a nullable type in one go. This would change your code to something like:

    leapDay(yr) = isleapyear(yr) ? Nullable(Date(yr,2,29)) : Nullable{Date}()
    # …
    if let ld = leapDay(yr)
      doy = dayofyear(ld)
      # …

Obviously since `let` is already in use for other purposes in Julia, this exact example wouldn’t work. I’m sure with more experience and examples like this, though, patterns will emerge. (For one thing, @if-let should be trivially implementable as a macro.)

Milan Bouchet-Valat

unread,
Feb 10, 2016, 4:08:49 AM2/10/16
to julia...@googlegroups.com
Le mardi 09 février 2016 à 20:28 -0800, Michael Landis a écrit :
> missed a paren above (for the people that are going to past the code into a shell and try it out) - something that I am not doing.  Still, this is closer:
>
> # wishful thinking...
> using Dates;
> leapDay = isleapyear(yr) ? Date(yr,2,29) : nothing
> if ! leapDay
>   doy = dayofyear( leapDay )
>   ...   clean and concise (thought that was the point), but we get
>
> leapDay = isleapyear (yr)?  Nullable {Date} (Date (yr, 2:29)): Nullable {Date} ()
> if ! isnull( leapDay )
>   doy = dayofyear( get(leapDay) )
First, you don't need to specify {Date} when passing a value, as the
type can be inferred:
leapDay = isleapyear(yr) ? Nullable(Date(yr, 2:29)) : Nullable{Date}()

Note that you can also rely on implicit conversion to write this as:
leapDay::Nullable{Date} = isleapyear(yr) ? Date(yr, 2:29) : nothing

My not-so-secret dream is that you would be able to write this like in
C# and Swift as:
leapDay::Date? = isleapyear(yr) ? Date(yr, 2:29) : nothing


Then, regarding the if block, writing leapDay != nothing or
!isnull(leapDay) is the same IMHO. What's a bit annoying is the call to
get(). Again, I wish we would be allowed to write leapDay? instead of
get(leapDay). Other than that, one could easily write a macro similar
to what Joshua proposes in his post. Let us know if you'd find it
useful.


Regards
Reply all
Reply to author
Forward
0 new messages