Turns out Rust's Option type already has all this behavior, so I think
we're all on to something :)

Option is a little more powerful than nullable pointers because you can
have Options of non-pointer values. IIRC, Option<~T> is actually compressed
to be a nullable pointer. I actually really like the ?T syntax, but I'm not
sure it's worth special-casing Options. I think it's something macros could
handle (convert ?T into Option<T>).

Like your Hack type system, Rust's type system stops you from using an
Option<T> in place of a T (they are different types after all). The basic
way to convert is a match expression, which is equivalent to:

if($maybe_car) { /* Some / non-null case here */ }
else { /* None / null case here */ }

Leaving the else branch off, i.e. leaving None as None, actually
corresponds to either functorial map (Option.map) or monadic bind
(Option.and_then) depending on the return type of Some branch. So your
example could become:

fn demo(maybe_car: Option<&mut Car>, car: &mut Car) {
    car.start();
    maybe_car.map(|car| car.start()); // ignore the resulting option
}

Rust's Option has it's own version of your invariant function:
Option.expect.
If the Option is Some, then it returns the value therein. If the Option is
None, then it fails and displays a message. Option.unwrap is the same, but
with a default message.

fn demo(car: Option<&mut Car>) {
    let car = car.expect("Expected non-null car for the demo");
    car.start();
}

Your edge case presents an interesting difference between Hack and Rust.
In Hack, you know $car is non-null inside the if's consequent, but $car is
*still* a nullable pointer ?Car. In Rust, you know car is non-null in the
Some branch of a match, but it's not an Option<Car> anymore! It's just a
Car, so a smashCar method either isn't applicable (it's a method on
Option<Car>s) or it creates a new Option<Car> for us and sets it to None
(an example of a monadic function for Option). In the later case, we'd use
.and_then() to call smashCar and then chain the start call with .map(). The
.map() call would safely evaluate to None.

Thanks for the input!


On Tue, Feb 25, 2014 at 7:24 PM, Aran Donohue <[email protected]> wrote:

>  Hey,
>
>  I'm not sure how people feel about Option types but there seem to be a
> few hundred uses in the rust codebase. Wanted to share an idea we use in
> our custom type system ("Hack") at Facebook to make it a bit nicer to
> safely deal with null references. We don't use Option types directly. I
> don't think this adds any theoretical power or new possible efficiency
> gains, it's mostly for making the code a bit simpler.
>
>  Type declarations prefixed with a question mark represent references
> which might be null. So '?Foo' is somewhat like a shorthand for
> 'Option<Foo>'.
>
>  function demo(?Car $maybe_car, Car $car) {...}
>
>  We use these like possibly-null pointers. Usually you write an if
> statement prior to using a value.
>
>  function demo(?Car $maybe_car, Car $car) {
>   $car->start();
>   if($maybe_car) { $maybe_car->start(); }
> }
>
>  Sometimes we use these in combination with a special function
> "invariant", an assertion function that throws an exception if a condition
> is not met:
>
>  function demo(?Car $car) {
>   invariant($car, 'Expected non-null car for the demo');
>   $car->start();
> }
>
>  If you forget to check for null one way or the other, the type-checker
> complains. This is a static, ahead-of-time check.
>
>  function demo(?Car $car) {
>   $car->start(); // error
> }
>
>  There are some natural annoying edge cases to be covered.
>
>  class Smash {
>   private ?Car $car;
>
>    function demo() {
>     if ($this->car) {
>       $this->smashCar();
>       $this->car->start(); // error
>     }
>   }
> }
>
>  A downside of this approach vs. Option is that code written using
> pattern matching over Option is more easily upgraded to code using pattern
> matching over Result (or something else).
>
>  Anyway, we like this feature and I'd be happy to see it adopted
> elsewhere. Tossing it out there as I don't know anything about the Rust
> compiler or how language design decisions get made for it :)
>
>  -Aran
>
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to