
Rust and the Blub Paradox - filsmick
http://www.jonathanturner.org/2016/01/rust-and-blub-paradox.html
======
bluejekyll
> What does the C++ code print?

And there it is. I programmed in C++ long enough to "know" this, but I still
remember more jr. Engineers and even arguments with more sr. Engineers around
this type of question.

Rust has removed this kind of question almost entirely from the language.

Personally, I have no intention of ever going back to C/C++ for any new
project I work on.

~~~
alimw
> What does the C++ code print? If you guessed wrong, don't worry. You're in
> good company. If you guessed right, congrats!

How do I knowwww????

~~~
nothrabannosir

        $ g++ test.cpp
        $ ./a.out
        EDIT---answer removed to let others guess first.

~~~
eximius
Aw. As a user on my phone you make me sad.

~~~
nothrabannosir
Sorry about that. [http://pastebin.com/T8fBtUnk](http://pastebin.com/T8fBtUnk)

------
lobster_johnson
I think articles like these beg the question. It introduces a claim
("many/most people think some languages are too weird") and tries to refute it
without first showing that the claim is true.

Sure, I see lots of evidence of junior developers living in a happy bubble
where they apply language X to everything, when they could expand their
horizons a bit by looking into Y and Z.

But this also ignores important factors such as:

* Convenience — if you've highly proficient in X, there's a low barrier to entry for anything you want to accomplish.

* Private ecosystem — if all your code is in X, you have potentially tons of reusable modules as part of your own stack.

* Public ecosystem — lots of libraries and community support.

* Mature documentation

* Stability

* Ease of hiring

and so on.

Also, not least: The journey of every developer is to broaden one's horizons
gradually. As a junior you'll likely slog through a few fad languages until
you reach a point where every technology out there, no matter how advanced,
will suddenly become approachable.

The Blub idea also ignores the fact people can accomplish great things with
poor tools. Choosing a minimal tool does not necessarily say anything about
you as a developer. Sticking to one tool for many years probably says a lot
about your ability to develop your skills, of course.

In short, I think Blub is a red herring, and I think it's a condescending one,
a product of survivorship bias — you're not superior because you succeeded
using a certain set of tools. You're probably successful for other reasons
(which may correlate with your ability to choose the right tools, or not).

Graham's observation that "[languages] dictate the way [developers] think
about programs" is the more important lesson to draw from his essay, though
it's not exactly a new idea.

~~~
pekk
The implication that a developer is junior if they mainly use one language is
completely absurd.

~~~
lobster_johnson
I don't see how you could interpret my comment that way.

A junior dev might use a single language because they apply what they know and
don't go outside their comfort zone. A senior dev might use a single language
for every other reason.

------
hsivonen
In his CppCon 2015 keynote, Herb Sutter talked about retrofitting Rust's core
concepts (not phrased like that!) onto C++ as a static analysis pass distinct
from the actual compile. Unlike Stroustrup, he acknowledged the existence of
Rust but said the lifetime annotations are too verbose.

Rust has lifetime elision for the common cases, though. It's unclear to me if
the criticism was based on a pre-elision version of Rust.

In some talk, Andrei Alexandrescu said D is pursuing GC removal.

It'll be interesting to see if D and C++ with lifetime annotations can achieve
useful results with less syntax for the cases where Rust's elision doen't work
and you need explicit lifetime annotations in Rust. I have doubts, but of
course both Alexandrescu and Sutter are working on Rust's competitors, so one
would expect them not to say that Rust's more awesome than their languages.

~~~
pcwalton
> Unlike Stroustrup, he acknowledged the existence of Rust but said the
> lifetime annotations are too verbose.

As far as I can tell, this was an incorrect claim. The ISO Core C++ lifetime
elision rules aren't meaningfully more aggressive than Rust's. We could easily
add more elision to Rust if it turned out to be necessary, but the cases in
which ISO Core C++ has extra lifetime elision rules that Rust doesn't don't
come up often enough to make a difference.

Lifetime elision is a double-edged sword anyway. It's somewhat controversial
in the Rust community, because it's not reading the lifetime annotations that
causes the cognitive overhead: it's the semantics and what the compiler will
enforce. Having fewer lifetime annotations can actually make the code a lot
more confusing. Based on experience, I would caution C++ to not go overboard
with it: being able to show pretty code on slides is not worth confused and
frustrated users.

~~~
Manishearth
> It's somewhat controversial in the Rust community

We added a lint to clippy that detects places where you _could_ rely on
elision but don't. I'm particularly fond of it, because many Rust programmers
(including me) still default to the pre-input-output rule regime by explicitly
typing out `fn<'a>(&'a u8)-> &'a u8`. It's caught a lot of this and made code
nice and clean.

However, we've have multiple people ask the lint to be off-by-default for
input-output cases where one of the two sides has a struct/enum lifetime
(`Foo<'a>`, not `&'a _`), because it's nice for that to be explicit. (At the
same time, multiple people feel like it should stay)

We'll have to see what happens when we RfC the clippy defaults.

------
saurik
I think "blub" is relative. I look at people who think that cluttering code
with boilerplate for error handling, and even those who think that boilerplate
is sufficient is sufficiently papered over using macros, have yet to
understand why exceptions are interesting or (often) how to use them correctly
(as many uses of exceptions that people like to poke at are entrenched
wrongness, much like how many people who hate the entire idea of relational
databases really just hate MySQL, its limitations, and the attitude of its
ecosystem). I feel one of the reasons I have been as productive as I have been
over the years is that I have spent a lot of time studying error handling and
even now have a sort of "theory of errors" that I will sometimes draw out for
people on a blackboard. From my perspective, Rust is currently "unusable",
though very compelling and will hopefully fix this problem. As much as I agree
with other things it has built, and as much as I agree that my attempts to
simulate those things in other languages have disappointing holes, after
having spent a lot of time studying languages like Erlang and Haskell (and
even as someone who teaches a class at a college on programming languages at
the college level), not having exceptions or anything better than try! is a
deal breaker for me, and while this person jokes about monads and how learning
them might not be important, they would be well served learning why monads are
interesting. One could even argue the entire section about error handling and
how this developer is happy about how "straightforward" Rust is in comparison
to monads is the "blub" issue rearing its head, but between Rust and Haskell.
(The use of the word "straightforward" is always particularly concerning to
me, as it is the general argument one uses for programming in C or Java
instead of anything that hides intent.)

~~~
pcwalton
Rust's error handling is, empirically, not unusable. I use it every day.

Even Go's error handling clearly isn't unusable, as controversial as it is,
and try! is pretty much just a more sugary version of it.

Also, we were well aware of monads when we designed the Rust error handling
system and in particular why they do not work very well in languages that have
rich, imperative control flow structures. try! is basically just monads for
imperative languages. To see this, work through what happens if you try to add
break/continue/return to Haskell's system.

~~~
steveklabnik
I still don't fully understand the break/continue/return argument, at all. I
should bug someone to actually write this up.

~~~
pcwalton
An oversimplified version: the monad rules/type signature only allow for
possibly-recursive sequences of statements. That admits straight-line code as
well as if, while, and for. But it doesn't allow for any exceptional loop
exits (more formally, doesn't allow for any backwards edge from node A to B
unless A postdominates B).

There are ways to transform code to allow this to work (there has to be, since
Haskell is Turing-complete), but it's not straightforward.

~~~
steveklabnik
I guess I'm just not sure why this is a problem, exactly. Even if you couldn't
use those statements inside of a `do` block, that shouldn't be an issue? Just
like using `break` outside of a loop is invalid, it would be the same here.

The Option monad already is a sort of early return. Inside of monadic
combinators, you use the monad for control flow instead of those statements.
Seems fine to me, though admittedly my Type Theory Wizardry isn't the
strongest.

~~~
pcwalton
do is only really good if you use it for the whole function, or at least have
some way to get the error out of the do block to the code that's supposed to
handle it. But if break isn't allowed inside a do block, then if you need to
break you have to split up your do blocks into blocks before the break and
after the break. Now if there's an error thrown by one of the statements in
one of those do blocks, then you have no straightforward way to propagate it
out of the function. You would need to pattern match on the result of each do
block and return the error if there was one--in other words, you would need to
write try!

This is why I called try! monads for imperative languages: the early return
that is expands to is the key to playing nice with imperative constructs like
break and continue.

~~~
steveklabnik
The usual implementation of the Either/Option monads already does this though:

    
    
        instance Monad Maybe where  
            return x = Just x  
            Nothing >>= f = Nothing  
            Just x >>= f  = f x  
            fail _ = Nothing  
    

and

    
    
      instance (Error e) => Monad (Either e) where  
          return x = Right x   
          Right x >>= f = f x  
          Left err >>= f = Left err  
          fail msg = Left (strMsg msg) 
    

The end result is still a value of that type. You get the short-circuting
behavior as soon as you hit Nothing/Left.

~~~
pcwalton
It only short-circuits to the end of the do block. But once you leave the do
block the error isn't propagated anymore.

~~~
steveklabnik
Right. Maybe this is because I tend to write small functions, but the amount
of things that end in `Ok(())` especially with IO still makes me think it
would be useful.

------
chao-
I haven't written C++ with any significance in a few years, and done little
with Rust at all. So while I don't feel qualified to speak to the comparison
at the core of the blog post, I did find the first example to be disingenuous.
It rests entirely on assuming the C++ programmer will rely on inheritance to
achieve polymorphism, simply because they can?

Additionally, the C++ example doesn't produce the same output ("Value: (x: 7)"
and "Value: (x: 5, y: 10)"). The addition of that goal might lead the
programmer to treat the C++ version of print_me as something more abstract,
accepting a string and having class Foo and class Bar simply return their own
string representations.

I acknowledge that the premise was "a beginner C++ developer", getting hyped
up about inheritance and using it as the only tool in their toolbelt, but how
many people are learning C++ as a first language these days? It used to be the
norm, back in the OO-will-save-us-all heydey, but has anyone run into it
lately?

~~~
bluejekyll
Don't forget about how awesome C++'s multiple inheritance is! <sarcasm/>

edit: It's worth the down votes on this. Multiple inheritance is pure evil and
confusion. In fact, even in Java I now rely on aggregation over inheritance.

[http://stackoverflow.com/questions/269496/inheritance-vs-
agg...](http://stackoverflow.com/questions/269496/inheritance-vs-aggregation)

~~~
mercurial
Unfortunately, in some languages, inheritance of abstract classes is the only
way to get the equivalent of interfaces.

That said, yeah, the less class inheritance, the better. Ideally, work in a
language where interfaces can include default implementations.

~~~
bluejekyll
Totally, no problem with default implementations as long as they are generic.
Java finally supports this, as well as Rust and others.

------
draw_down
This article is a bit all over the place, and the basic premise is not very
good.

The Andrei guy says that Rust places too much emphasis on "clerical" memory
management. This is not like a person who learned PHP from a couple w3schools
article deciding that Lisp is "weird". The criticism is not that Rust is
"weird" or somehow unintelligible, it's a direct critique of the language
designers' choices.

This is the danger of using rules like the Blub paradox, you have to be
careful or everyone who doesn't agree with you or doesn't like what you like
is a Blub-programming dullard.

I actually do think there is something to the paradox, I would even say I have
occupied different parts of that "ladder" myself. (That is, I'd like to think
I'm higher on it now than I used to be.) But maybe JS programmers aren't
interested in type-checking in their JS precisely _because_ they prefer JS for
its dynamically-typed nature. This is as opposed to someone coming to JS from
a different language and bringing their preference for strong typing with
them.

Then the author goes on to talk about a bunch of stuff that does nothing to
explain Rust's apparent emphasis on memory. It's pretty much just a list of
why Rust is cooler and better than C++. That's fine but none of it addresses
the point made at the outset.

~~~
pekk
I have a vague question: how can you avoid putting emphasis on memory
management, to the extent Rust does, without introducing mandatory garbage
collection?

~~~
tormeh
My understanding is that Rust is, to be wildly inaccurate, kind of like a
language without a heap. Normal languages manage stack memory without GC. Rust
kind of does the same: When deleting a stack frame, Rust deletes the objects
created when on that stack frame, except when a reference is returned by the
scope corresponding to the frame, in which case the "ownership" of the heap
object is passed to the frame under the deleted-to-be frame.

Sort of. Don't cite me.

------
yakcyll
For a C++ developer, Rust introduces a lot of seemingly arbitrary rules and
constraints on how data can be managed, moved around and referenced. In order
to develop things that employ a lot of composing, one has to either make the
code nigh unreadable with tons of unnecessary chaining (which is unavoidable,
since simple dereferencing and assigning a value of a field in a structure to
a variable means the structure is now borrowed and you can't reference the
field nor the structure until the variable goes out of scope; please, for the
love of all that is holy, prove me wrong!) or the entire data model has to be
rethought with those rules in mind. Not sure about others, but I sure as hell
still have a lot of trouble wrapping my head around boxes, mutability and
lifetimes; maybe I'm making some fundamentally flawed assumptions trying to
draw similarities between C++ and Rust, but those issues are showstoppers for
me for now.

~~~
steveklabnik

      > seemingly arbitrary rules
    

Most of them are about Rust's core guarantee: data race freedom. Some of them
are due to a certain conservativeness of any static analysis, and may be
relaxed in the future.

    
    
      > for the love of all that is holy, prove me wrong
    

It depends on exactly what you're doing. There are always ways to get around
things, but it can depend on knowing Rust and its standard libraries well. As
a younger language, some patterns are still being developed, and aren't always
as obvious as they could be. We'll get there...

Rust is certainly a different language, and if you try to port C++ code
directly over, you may have problems. Such is life. :)

~~~
Manishearth
> Most of them are about Rust's core guarantee: data race freedom

IMO this is just a part of it (and I think you agree, based on previous
conversations). The actual thing is that the rules enforce a discipline about
data, similar to the discipline in functional languages (except here it's
allowing sharing XOR mutation instead of forbidding mutation entirely). This
discipline gets us many things -- memory safety, safety from iterator
invalidation-y things (there's a whole class of memory safety bugs that happen
when you modify the exterior of a things whilst holding a pointer to the
interior -- from iterator invalidation to invalidating pointers to a vector
after truncation to invalidating enums), and clarity in code. Whilst the
chronology of it's design may not be such, I personally look at data race
freedom as something we got for free from this discipline, instead of the core
focus of it.

~~~
dbaupp
I strongly disagree: the goal with Rust is to offer safe, low-level
programming, not to be a test-bed (or whatever) for some programming paradigm.
The "discipline" is just a tool to reach the goal. You can see this in the
evolution of Rust: the goal hasn't changed, but the tool used to (try to)
reach it has. (I know that you mention ignoring the chronology, but ignoring
the intent doesn't make sense.)

Put another way: Rust isn't _aiming_ to be top of the pack in terms of
enforcing a certain programming style, where as it is aiming to be top of the
pack in terms of safe systems programming. (It might happen to be the best
language for the former, but that is a consequence of the latter, not the
other way around.)

~~~
Manishearth
You misunderstand me: I don't disagree that Rust is all about safe systems
programming. I disagree that "data race freedom" is Rust's (only) core
guarantee, and I disagree that the "seemingly arbitrary rules" are about "data
race freedom". They're about so much more, since the same rules get us memory
safety as well, among other things.

I don't think that Rust is trying to test out a programming paradigm or
whatever, I'm saying that these "seemingly arbitrary rules" get us a lot of
things, by proxy of a certain paradigm, and reducing it to "data race freedom"
(when it's so much more) is something we should avoid.

(My comment seems to focus on the discipline, I was just using it as a proxy
for all of the things it gets us)

~~~
dbaupp
As we discussed on IRC, "data race freedom" is equivalent to guaranteeing
memory safety, and so, in a kind-of pedantic way, data race freedom is the
core guarantee. That said, the original comment would've been better phrased
as "one of Rust's core guarantees".

Furthermore, I still think the best phrasing of the rules is for that
guarantee: if a rule is removed one can usually construct fairly simple
programs that have data races/memory unsafety. Of course, it is definitely
true that the arbitrary rules have other benefits, but if there was a simpler
scheme that gave the core memory safety without the other things, I think Rust
would've adopted it.

------
eximius
"No, you don't have to learn monads."

Ha! This is ironic because a present discussion over some syntactic sugar
(maybe more) to make Rust error handling feel more part of the language and
more ergonamic may be ad-hoc monads. Not that the programmer needs to know
that - it's just something that should happen to play nice in the rest of the
Rust type ecosystem

~~~
M2Ys4U
I was under the impression that `Option<T>` and `Result<T, E>` _were_ monads.

~~~
u320
Yes but the Rust type system cannot express monads, so they aren't monads in
the type sense (they don't implement a specific Monad trait).

~~~
steveklabnik
Fun Trivia Fact: Rust's type system was[1] Turing complete. So you could
actually have monads. You just wouldn't want to.

1: The program that proved this no longer compiles, so we're not sure if the
type system is or is not at the moment.

~~~
eximius
I would love to see this program. Maybe even spend some time trying to get it
to compile.

~~~
steveklabnik
[https://www.reddit.com/r/rust/comments/2o6yp8/brainfck_in_ru...](https://www.reddit.com/r/rust/comments/2o6yp8/brainfck_in_rusts_type_system_aka_type_system_is/cmkrjz2)
was one of them

------
shams93
I'm slowly learning rust. Ive worked with c and jni in android for sound apps
but now Im ignoring my reactions over the weird parts of rust so i can use it
to builda killer music application for raspberry pi

~~~
kitd
If it helps, I've called Rust over JNI. Fairly easy in fact though I recommend
judicious examination of examples on the web first.

Cargo is also very helpful in this regard.

------
jbandela1
Wow, this is the first post I have ever read that even kind of implied that
Andrei Alexandrescu was a Blub programmer.

Part of being a Blub programmer is that you don't even think about the issues.
In regards to the weird features brought up, the underlying issues behind
these features have been in the C++ consciousness for some time. Below are
some talks and resources covering at least some of these issues.

Sean Parent - Inheritance is the Base Class of Evil -
[https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&c...](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjDxbLRp8DKAhVB9R4KHRW7A2sQtwIIIzAB&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DbIhUE5uUFOA&usg=AFQjCNHGTLJ5VCeo9fzZD7foEiT0q4lLhw&sig2=KFJ3vZoxQNbRb4CBpOjS-A)

Herb Sutter - You don't know const and mutable -
[http://herbsutter.com/2013/01/01/video-you-dont-know-
const-a...](http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-
mutable/)

Andrei Alexandrescu - Systematic error handling in C++ -
[https://channel9.msdn.com/Shows/Going+Deep/C-and-
Beyond-2012...](https://channel9.msdn.com/Shows/Going+Deep/C-and-
Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C)

Herb Sutter - Writing Good C++ by Default -
[https://www.youtube.com/watch?v=hEx5DNLWGgA](https://www.youtube.com/watch?v=hEx5DNLWGgA)

Andrew Sutton - Generic Programming with Concepts Lite -
[https://www.youtube.com/watch?v=qwXq5MqY2ZA](https://www.youtube.com/watch?v=qwXq5MqY2ZA)

One of the things that helps with avoiding "Blubness" in C++ is the
significant interest in languages such as Haskel (which is not considered a
Blub language by anyone except Agda programmers) and in applying the
techniques and insights where possible to C++ whether through writing new
libraries (see for example Fit by Paul Fultz II
-[https://github.com/pfultz2/Fit](https://github.com/pfultz2/Fit)) or through
new language features (see this post by David Sankel -
[http://davidsankel.com/uncategorized/c-language-support-
for-...](http://davidsankel.com/uncategorized/c-language-support-for-pattern-
matching-and-variants/))

Finally, is also interesting that Paul Graham's blub paradox is being brought
up in regards to Rust, C++, and D. The Paul Graham article is in large part
talking about the power of meta-programming. In this regard, D and C++ are
significantly more powerful in that regard. Features like higher kinded types
(template templates), variadics, and non-type template parameters help in this
matter. When you can write a function that can generically work with a tuple
with any number and type of parameters, you can do some pretty neat stuff. For
some examples take a look at Boost.Hana by Louis Dionne
([http://boostorg.github.io/hana/](http://boostorg.github.io/hana/)). In this
regards, Rust is actually the Blub compared to D and C++.

~~~
steveklabnik

      > In this regards, Rust is actually the Blub 
    

I'm not so sure. We are aware of the features that they have; we just prefer
the strongly-typed versions to the stringly-typed[1] versions.

We do desire more meta-programming features, and they will happen. Like all
decisions, we don't want to rush into them.

1: This is not _entirely_ accurate, but I'm slightly at a loss for how to
exactly characterize this at this particular moment. Rust's metaprogramming
guarantees that you always generate valid Rust code, D's does not.

~~~
mst
Is that mostly a faster feedback loop and better errors thing? It seems like
so long as the generated code doesn't get as far as compiling, it doesn't make
that much of a difference?

(this could easily be a stupid question; I know I've come across as
condescending to you before by mistake and if I've somehow done it again
please believe that I meant to come across as genuinely curious :)

~~~
pcwalton
There are a number of benefits to concepts:

\- They make namespacing of functions easier, eliminating the need for C++'s
"argument-dependent lookup". The issues here are somewhat subtle and would
take a lot of space to explain. But the end result is that we can remove a
massive amount of confusion by getting rid of ADL.

\- Error messages are much better, because the insides of templates never leak
to users of the template.

\- Typechecking is simplified, because we only have to typecheck each template
once, at the time of definition, as opposed to repeatedly typechecking it from
scratch every time it's instantiated. This, in theory, allows compilation to
be faster.

~~~
mst
> Error messages are much better, because the insides of templates never leak
> to users of the template

This is unequivocally a good thing - I think maybe my disconnect here in how
excited I am compared to others is that I'm comparing it to the experience of
debugging lisp macros and everybody else is comparing it to the bottomless pit
of despair that is C++ template errors ;)

------
sago
How very patronizing. There's a tendency we all have to assume that someone
would agree with us, if they weren't so ignorant/stupid/naive. None of us want
to feel that we'd agree with them if only _we_ understood.

Seems this is the programming equivalent. Programmer X turns their nose up at
my pet language Y. It can't be because of me, it has to be their
naivitee/ignorance/lack of experience/bias, etc, etc.

That the article was a big "you're ignorant" to Andrei Alexandrescu, of all
people, was telling.

------
pklausler
Some programmers can write BASIC in any language. But on the up side, some
programmers can write Haskell in any language.

