
Functional exceptionless error-handling with optional and expected - signa11
https://blog.tartanllama.xyz/optional-expected/
======
_ZeD_
Just thinking about the example... Let's say... just use excepions! This code

    
    
        std::optional<image_view> get_cute_cat (image_view img) {
            auto cropped = find_cat(img);
            if (!cropped) {
                return std::nullopt;
            }
    
            auto with_tie = add_bow_tie(*cropped);
            if (!with_tie) {
                return std::nullopt;
            }
    
            auto with_sparkles = make_eyes_sparkle(*with_tie);
            if (!with_sparkles) {
                return std::nullopt;
            }
    
            return add_rainbow(make_smaller(*with_sparkles));
        }
    

Becomes

    
    
        image_view get_cute_cat (image_view img) {
            auto cropped = find_cat(img);
            auto with_tie = add_bow_tie(*cropped);
            auto with_sparkles = make_eyes_sparkle(*with_tie);
    
            return add_rainbow(make_smaller(*with_sparkles));
        }
    

And it could throw a no_cat_found, a cannot_see_neck, or a cat_has_eyes_shut
excepion

~~~
kccqzy
The syntactic baggage is bothering me too. Sometimes this functional style is
syntactically too heavyweight if your language doesn’t support it very well.
Compare that example translated to Haskell:

    
    
        getCuteCat img = findCat img >>= addBowTie >>= makeEyesSparkle >>= addRainbow . makeSmaller
    

No more manually checking whether a value is null or not, and the chaining
Simply Just Works. Syntactically lightweight and conveys the important
information without line noise.

Yes this example uses monads, and there’s a reason why monads aren’t that
popular in other languages. Most of the time you also need parenthesis-less
function application, currying, and lightweight lambdas to make monad-heavy
code syntactically acceptable. Yes Rust has .and_then which is same as >>=
(sometimes also called flatMap or bind), but still the syntax isn’t
lightweight enough.

I wish people could just stop copying this idea from Haskell and use what’s
reasonable in their language. Shoehorning monads and monad-heavy code into a
typical language isn’t reasonable.

~~~
greydius
A lot of people dislike the terseness of that style (especially the use of
operators.) I'm not one of those people, but I can understand their point of
view. Fortunately, Haskell also lets us write code in a way that managers can
understand.

    
    
        getCuteCat img = do
            cropped <- findCat img
            withTie <- addBowTie cropped
            withSparkles <- makeEyesSparkle withTie
            return (addRainbow (makeSmaller withSparkles))

~~~
davidgay
However this style breaks down as soon as you have to do something more
complicated than thread a single value, or have to do something more
complicated than pass failures through.

~~~
haar
We've been using this style a lot in Elixir via the `with` macro

    
    
        def get_cute_cat(image_id) do
          with {:ok, cropped}       <- find_cat(image_id),
               {:ok, with_tie}      <- add_bowtie(cropped),
               {:ok, with_sparkles} <- make_eyes_sparkle(with_tie)
          do
            with_sparkles 
            |> make_smaller() 
            |> add_rainbow()
          end
        end
    

If any of the values on the rhs of a `<-` cannot be pattern matched, it
returns up the returned value. This value can be pattern matched easily inside
an `else` clause underneath the main block. The use of the `:ok,` vs `:error,`
tuple convention allows multiple values to be destructured and passed around
if needed (rather than an _optional_ type).

If you find the code getting unwieldily, as runeks said you can break it down
into simpler constructs.

------
kthielen
I think that this dichotomy can be summarized with the old logical identity:

A or B = not(A) implies B

Considered as a statement about types, 'A or B' is a variant type over two
constructors A/B (this is equivalent to an optional type, where A=unit).

Also as a statement about types, 'not(A) implies B' is a function taking a
continuation on A (the exception handler) and returning a B (if it decides not
to raise an exception).

So (as we often find in CS) this is a very old idea, older than computers and
programming languages. :D

------
anderspitman
Ernest question: If it supports the platform you're targeting and you're not
missing any critical libraries, what other reasons are there not to use Rust
over C++ for new projects? Assuming of course you're not already proficient in
C++.

~~~
sidlls
Maturity of the language, quantity of developers available, knowledge base
ecosystem (this is more than just library availability, as it includes
implementations to solve problems resulting from years of practice by
thousands of engineers), interoperability with other languages' libraries, to
name a few. In some cases Rust's "escape hatch" to get unsafe has more
boilerplate than equivalent implementations in C++: when Rust's safety model
isn't as important it might not make sense to use it.

Personally, as a person very experienced with C++ I prefer Rust when it's
available on the target platform. The low level, high efficiency computations
I might do use numerical algorithms that neither Rust nor C++ are very good
for, although C++ is slightly better in this case.

------
dep_b
It almost makes me want to write some C++. Still an interesting observation
that people want to add new stuff to an already humongous language like C++

~~~
zrb
Doing the equivalent of "maybe chaining" isn't hard in c++ & quite elegant in
c++17 in my view

[https://stackoverflow.com/questions/7690864/haskell-style-
ma...](https://stackoverflow.com/questions/7690864/haskell-style-maybe-type-
chaining-in-c11)

------
jwatte
Welcome to Monad Burger. Would you like a fmap with that?

