
Option Monads in Rust - mmastrac
http://www.hoverbear.org/2014/08/12/Option-Monads-in-Rust/ 
======
gamegoblin
As a Haskell programmer and a casual Rust observer (yet to take the dive), I
am constantly impressed with the nice balance Rust seems to have struck
between Haskell and C.

Currently my #1 and #2 languages are Haskell and C. I will definitely jump on
the Rust bandwagon with the 1.0 release -- maybe sooner, we'll see.

~~~
sriku
I too am getting into Rust and am liking what I'm seeing so far. However, I
find its type abstraction facilities limited. For example, the concept of a
"monad" is not expressible using the trait system so that a bunch of related
types can reuse the abstraction. Instead what we have to do is to declare a
ListMonad<T>, OptionMonad<T>, with separate signatures for map/join/whatever.
Ideally, something like Monad<M<T>> could be the trait specification.

edit: Not Monad<M<T>>, but just Monad<M> with the facility to use M<T> in the
body of the trait. I don't have a clear idea of how references, lifetimes,
etc. would pan out in this case.

~~~
jmgrosen
What you are describing is essentially higher-kinded types (HKT). It is known
that we want this, but it's probably going to happen post-1.0.

~~~
Bougainville
Shouldn't this happen before 1.0, so that the standard library can be designed
to make use of it before it's frozen?

~~~
dbaupp
The standard library is not being frozen as a whole at 1.0, we are only
providing strong backwards compatibility guarantees about the language itself.
There are precise markers and compiler diagnostics that individual parts of
the standard library will use to convey their stability properties (including
graceful deprecation warnings).

~~~
Bougainville
> The standard library is not being frozen as a whole at 1.0, we are only
> providing strong backwards compatibility guarantees about the language
> itself.

That's unfortunate. :( Which version will have a full backwards compatibility
guarantee?

~~~
pcwalton
I don't think it matters whether the full standard library is marked stable.
What matters is whether _the portion of the standard library that you 're
using in your particular app_ is stable. To that end, we have a lot of tooling
to make sure that you can stay within the stable parts of the standard library
if you wish. Version 1.0 will have enough of a stable base for most apps.

Freezing the entire standard library in one fell swoop would freeze design
mistakes, and delaying 1.0 until we're sure about every last symbol in every
last library would delay 1.0 for no reason. I'm confident in our approach.

~~~
Bougainville
> I don't think it matters whether the full standard library is marked stable.

For example, unless IO is stable no serious program can be written. But IO
would benefit from monads and HKT.

~~~
pcwalton
try! is good enough for 1.0.

------
millstone
The "Symphony of Functions" example is worth dissecting, since it's a good
example of when NOT to use an Option monad.

The code computes sqrt(-1 * (log(-1 * (20 * 2)))^2).

The potentially failing functions are log and sqrt. Failure is determined by
checking if the result is a "normal" floating point value. But 'double' and
'square' may also produce non-normal values, e.g. squaring a large float may
produce infinity. So why doesn't double() return an Option as well?

Furthermore, checking if a value is normal is not correct. If x slightly
exceeds 1, then log x is a small positive value, i.e. a denormal, and
therefore log() will incorrectly report failure.

Floating point arithmetic already has a serviceable "Option" type in its non-
finite values (NaN and infinities), so the best way to write this code is the
naive way:

    
    
        let result: f64 = sqrt(pow(-1 * (log(-1 * (20 * 2))), 2));
        let success: bool = is_finite(result);
    

The Option monad only makes this code longer, slower, and buggier.

~~~
SamReidHughes
> If x slightly exceeds 1, then log x is a small positive value, i.e. a
> denormal, and therefore log() will incorrectly report failure.

No it won't, for the neighboring values of 1, which are 1-2^-53 and 1+2^-52,
log will return approximately -2^-53 and 2^-52, which are not denormal.

------
carterschonwald
I'm really excited that PCWalton got some basic associated types support into
Rust in time for the 1.0 milestone, that plus post 1.0 HKT (higher kinded
traits/types) will make Rust a really compelling language for building amazing
libraries :)

with those in place, some really beautiful api technology becomes possible to
port to Rust. I really appreciate that the core dev team has those partly
underway, and partly planned for the post 1.0 stuff.

------
tetha
Now do one about either.

Given Java 8 and Optional, I recently used Either to replace 80 something
lines of crazy interconnected try-catch-bullstuff in a server with 4 straight
either-based lines, "do this, in good case try this, in good case try that,
... and finally send a good result to the client, or log a bad result and send
the error to the client".

Had to rework and introduce some methods to make this work, of course, but
those methods turned into try { foo = doWork(); return Either.goodResult( foo
); } catch ( Exception e ) { return Either.badResult(e); }. It's structured,
and neat, and readable. :)

~~~
Dewie
Either is called Result in Rust.

~~~
innguest
That's a terrible name - they should change it to Either. We always call
things "result" in code, but I've never called a variable "either". That's why
I think Either is a better name - it's less likely to clash with valid names.

~~~
Dewie
I agree with you on that. "Either" suggests 'either this or that', to me.
"Result" feels too generic. Any value can be called a 'result'.

Though I don't know what the reasoning is on the naming of that type; for all
I know it might make a lot of sense within the context of rust and be very
much in line with the conventions of the rest of the standard library.

~~~
dbaupp
Result is essentially trying to only be an error-handling technique, its two
variants are Ok and Err (less generic than Left/Right). I believe the 'Result'
name actually comes from OCaml.

Rust used also to have an isomorphic type called Either, but error handling
was found to be by far the most common use of these two types, so it was
decided to standardise on a single one with obvious variant names (i.e. it's
clear with Ok/Err which one is the error and which one is the real value,
while Either requires getting the Left/Right convention right).

~~~
rbonvall
I remember having read that the convention was using Right when the value was
right and Left for the error (the error that's left, I imagine).

------
wyager
Am I wrong in thinking this is using Option as a functor, and not a monad?

Edit: looks like rust's and_then() is similar to bind, so it's reasonable to
call this monadic.

~~~
gamegoblin
EDIT: This is wrong

I initially thought the same thing, due to the use of `map` as something of an
equivalent of the Haskell `bind` (>>=) operator. It reminded me of just using
`fmap`.

But no, they are using it as a full monad. Consider the type of fmap:

    
    
        Functor f => (a -> b) -> f a -> f b
    

and the type of (>>=)

    
    
        Monad m => m a -> (a -> m b) -> m b
    

The key difference is the function argument. In a Functor, the argument is (a
-> b), but in a Monad, it's (a -> m b). The distinction is that when you
`fmap` a function over a `Just` value (`Some` in Rust), you always get back a
`Just` value, but when you bind a `Maybe` value into a function, you might get
back a `Nothing`.

So don't let Rust's use of `map` deceive you -- it seems to be the same as
(>>=).

That being said, I'd be interested to see Rust introduce something akin to do-
notation syntactic sugar.

=======

EDIT: I am wrong, the `map` function is basically equivalent to `fmap` and it
seems the `and_then` function is similar to (>>=). See user tel and user
wyager's responses.

~~~
TheHydroImpulse
Currently, it's impossible to introduce a generic `Monad` trait (the same with
`Functor`) because we're missing Higher-kinded types. Once Rust has such a
type system extension, it'll be possible to introduce some monadic sugar.

~~~
pcwalton
Note that you could have monadic sugar via macros today; it'd just be duck-
typed, not strongly-typed.

------
1_player
I don't understand the second example:

    
    
      fn some_on_even(val: int) -> Option<int> {
        match val {
            // Matches an even number.
            even if even % 2 == 0 => Some(even),
            // Matches anything else.
            _                     => None
        }
      }
    

Where is _even_ coming from? Is that a typo, or that construct is actually
defining a variable _even_ from val if val % 2 == 0 ?

That seems absurdly confusing to me.

~~~
dbaupp
It's binding a variable `even`, and then using a pattern guard to
conditionally take that arm depending on the value of `even`; it's (basically)
the same as

    
    
      match val {
          even => {
              if even % 2 == 0 {
                  Some(even)
              } else {
                  None
              }
          }
          _ => None
      }
    

which is an absurd construct (and the example pretty absurd too). It should be
written as just

    
    
      if val % 2 == 0 {
          Some(val)
      } else {
          None
      }
    

or, if you really like the formatting a match provides, just avoid creating a
new name binding

    
    
      match () {
          _ if val % 2 == 0 => Some(val), 
          _ => None  
      }
    

`match` and pattern guards make far more sense when not just binding directly
to a single variable like that, e.g.

    
    
      let val: Option<int> = ...;
    
      match val {
          // even
          Some(x) if x % 2 == 0 => Some(x),
          // odd
          Some(x) => Some(x + 1),
          None => None
      }
    

(Of course this may still be clearer using `.map` instead `match` but it's a
better example than the original one.)

------
omaranto
There's a typo in the title, it should say "Option Monad in Rust".

------
kazinator
Exact dupe:
[https://news.ycombinator.com/item?id=8184300](https://news.ycombinator.com/item?id=8184300)

~~~
dang
Reposts are ok when a post hasn't had significant attention in about a year:
[https://news.ycombinator.com/newsfaq.html](https://news.ycombinator.com/newsfaq.html).

15 points is a fair bit of attention, but 0 comments not so much, so I think
this repost is ok.

~~~
agapos
That one doesn't seem to be a year old, but the lack of comments make this
bump reasonable IMO.

~~~
dang
I fear that what I wrote wasn't clear. The rule about significant attention
only applies to stories that are _less_ than a year old. After a year or so,
reposts are typically ok.

~~~
Tomte
Aren't submissions folded to the last one, if the URL is the same?

I could swear I have seen that even when the prior submission was a few years
old.

Does the algorithm to decide that take comment activity into account?

~~~
dang
The algorithm doesn't currently take the time of submission or number of
comments into account, but we're working on a new implementation that will
handle all of this.

