Menu

Option.Value and NonNull

Help
2014-11-10
2014-11-13
  • Mauricio Scheffer

    I'm currently using NonNull<t> and Option<t> quite extensively to clearly enforce nullability in the type system (as much as C# allows anyway).
    One annoyance I've found is that since the Option<t> constructor maps nulls to None (BTW we've talked about this years ago and since then I've realized you were absolutely right ( https://sourceforge.net/p/sasa/discussion/571785/thread/87bfff2d/ )), extracting the value should be a NonNull<t> instead of just a T.
    This affects the Value property (though I'm not sure that anything could be done about this) and perhaps more importantly Select/SelectMany/etc.
    The problem of course is that changing the current signature of Select/SelectMany would be a huge breaking change and it would also break LINQ syntax, so that's not an option.
    Adding overloads such as </t></t></t></t>

    Option<R> Select<T, R>(this Option<T> option, Func<NonNull<T>, R> some) where T: class
    

    might work but that has many drawbacks as well: it doesn't work with LINQ, there's still the other overload, it would probably mess with the poor type inference of the C# compiler, making it a breaking change for callers of the current Select.

    Anyway, what are your thoughts about all this? Is it even an actual problem? Do you see any other solutions?

    Cheers,
    Mauricio

     
  • naasking

    naasking - 2014-11-10

    Hmm, propagating NonNull as much as possible is an interesting idea. I can see the value, since it reduces the need to create NonNull types at call boundaries. Added safety is an important goal, but here it conflicts with Sasa's goal of integrating seamlessly with the standard .NET framework, so I'm torn.

    I didn't originally think NonNull would catch on, but if someone else is using it, I'm going to have to rethink this!

     
  • naasking

    naasking - 2014-11-10

    I was incorrect on one point in that thread you cited: it is possible to write a function that checks for null and that works for nullable and non-nullable structs and reference types. You just write it against T and compare to null as needed. It's non-obvious that this would work for structs, but it does!

    Option<t> and NonNull<t> are duals, so supporting both is technically redundant. Because of pervasive null, Option<t> is actually the less useful one, since every nullable type is already an implicit option type. NonNull<t> is really the only distinction of interest on the CLR. Option<t> is then merely documenting a property we're already aware of, when we should really be documenting the converse: when something can't be null. Unless you can think of some other reason to keep Option<t>?</t></t></t></t></t></t>

    This also raises a needed correction to NonNull<t>: the current restriction to reference types is incorrect, as it should also support Nullable<t>. I've pushed this change for the next release.</t></t>

    To be honest, I was considering giving up on NonNull and deferring null checking to code contracts. VS's static contract checking seems to be the canonical solution to null checking these days.

     
  • Mauricio Scheffer

    Interesting! I agree that NonNull<t> seems to be more important than Option<t> in this context of a type system with pervasive nulls. But I still think that Option<t> is valuable as it forces the programmer to be explicit about null checking.</t></t></t>

    Without Option<t> it probably wouldn't be clear how to deconstruct a nullable variable into both cases: a non-null value or a null. What would that look like? Maybe</t>

    public static bool Switch<T, R>(this T o, Func<NonNull<T>, R> NonNull, Func<R> Null) where T: class
    

    As for code contracts I can't say much as I've only really used the [PureAttribute] and just for informal reasoning, I've never really used the tooling. And even so I'm a bit wary about it as the MSDN description for the PureAttribute seems to be different from the usual usage of the term "pure": https://connect.microsoft.com/VisualStudio/feedback/details/780289/pureattribute-incorrect-description

     
    • naasking

      naasking - 2014-11-11

      Your switch is one possible solution, as is a standard Linq query API over T : class, in case the error monad is acceptable. This gets tricky though, because sometimes R would be a struct, so in the error monad case it should actually return R? not R.

      However, overloading in C# does not consider return types, and functions with two implementations, T:class and T:struct, is forbidden. This seems to be one place where Option<t> is actually needed.</t>

      Still, Option<t> seems like a poor man's Result<t>. It doesn't give you any information on specifically where the null value entered. Have you tried Sasa's Result<t> at all?</t></t></t>

       
      • Mauricio Scheffer

        And Result<t> is a poor man's Either ;-)
        I am aware of it, I just don't need that additional information here. When passing an argument from a T it just doesn't make sense to have the possibility of an exception there.</t>

         
  • Mauricio Scheffer

    Another point against using both is that I'm already ending up with some cases of Option<nonnull\<t>> which doesn't really make much sense.</nonnull\<t>

     
  • naasking

    naasking - 2014-11-11

    Can you provide a short sample that produces Option<nonnull\<t>>? Perhaps some overloaded methods or some implicit conversion could be used to automatically flatten it.</nonnull\<t>

     
  • naasking

    naasking - 2014-11-11

    I've opened a pervasive-nonnull branch to start exploring more widespread use of NonNull<t>. Feel free to point out more places that should use it, and I'll keep adding them and seeing how it propagates. It currently seems limited to Result<t>, and the Option test suite.</t></t>

     
  • Mauricio Scheffer

    The case was something along these lines: I had a function returning IEnumerable<nonnull\<foo>>. This was built from a collection of functions returning IEnumerable<option\<foo>>. I used the Choose method I mentioned in the other thread but that returned IEnumerable<foo>. So I lifted the collection of functions to return each a NonNull<option\<foo>>.
    Of course then I realized the nonsense and used a similar Choose but returning IEnumerable<nonnull\<t>> instead of IEnumerable<t>, which solved the problem.</t></nonnull\<t></option\<foo></foo></option\<foo></nonnull\<foo>

     
  • naasking

    naasking - 2014-11-13

    So in your experience, a better default for ExcludeNull is to return NonNull<t> instead of T?</t>

     
    • Mauricio Scheffer

      Hmm, not sure what you mean. I am returning NonNull<t> in some methods to indicate that it never returns null. So it's not just for parameters.</t>

       
      • naasking

        naasking - 2014-11-13

        I meant, the signature of Choose/ExcludeNull should return IEnumerable<nonnull\<t>> and not IEnumerable<t> to avoid the scenario of IEnumerable<option\<nonnull\<t>>>?</option\<nonnull\<t></t></nonnull\<t>

         
        • Mauricio Scheffer

          Oh, right. Yes I think it should return IEnumerable<nonnull\<t>>. It might be a bit annoying if you need an IEnumerable<t> but I'd rather unpack an existing NonNull<t> than creating a new one.</t></t></nonnull\<t>

           

Log in to post a comment.

MongoDB Logo MongoDB