Hacker News new | ask | show | jobs
by josephg 1043 days ago
I normally love articles comparing programming languages at real tasks, but this article seems very low quality to me. The author clearly doesn't understand how rust thinks about programs. Instead, they're trying to pretend that rust is an alternate syntax for ocaml and being surprised to find it comes up short.

The same article could easily be written the other way around. We could start with a high performance rust program (which makes use of arena allocators, internal mutation and any other rust features you love) and then try and convert it line by line into ocaml. We would find that many of rust's concepts can't be clearly expressed in ocaml. The ocaml code would end up uglier and measurably slower than rust. And just like that the article would reach the opposite conclusion - that rust is clearly the better language!

But this is silly.

In general, you obviously can't translate between languages line by line like this and expect to have a good time. A beautiful C program is constructed using different ideas than a beautiful Lua program. And a beautiful Ocaml program is very different from a beautiful rust program.

Some obvious examples of ocaml ideas being overapplied to rust in this article:

1. The types don't really need to be wrapped in Rc here.

2. Rust generally prefers mutable imperative code over applicative code. And if you insist on applicative patterns, functions should take a &Foo.

3. Rust code usually doesn't rely on recursion that much, so the lack of guaranteed TCO isn't something people in the community care about.

4. Rust is optimized for runtime performance over code beauty or code size. Of course rust is less elegant looking than a garbage collected language! The trade is that it should also run faster. But where are the benchmarks to make the comparison fair?

The match example is just straight out bad rust code. This code:

    fn eval(term: &Term) -> Value {
        match term {
            Bool(b) => Value::Bool(*b),
            Not(m) => match eval(m) {
                Value::Bool(b) => Value::Bool(!b),
                _ => panic!("`Not` on a non-boolean value"),
            },
            // ... lots more nested matches & panics
        }
    }
Can be flattened, to approximately halve the length of the program like this:

    fn eval(term: &Term) -> Value {
        match term {
            Bool(b) => Value::Bool(*b),
            Not(Value::Bool(b)) => Value::Bool(!b),
            // ... (all other valid patterns)
            _ => panic!("{term} invalid"),
        }
    }
There's an old saying: "Every programming language you learn should teach you to see programs in a new way". Rust is not a crappy alternate syntax for ocaml any more than ocaml is a crappy, alternate syntax for rust. The only thing I learned from the article is that the author doesn't know rust well enough to evaluate it.
3 comments

Your example is more concise but the error message "{term} invalid" is less descriptive than the author's.

What would be the idiomatic way for the function `eval` to provide good error messages in Rust?

I would probably put a trait implementatoin on Value that does `.invert_bool`, and have that panic. That way eval is just `eval(m).invert_bool()` and if it panics it panics.

What you really probably want is to make this a Result type-returning thing, and then have have `not` be a function of type Value -> Result<Value,ErrType>, and then you can do not(eval(m)) and panic at the top-level.

Notice from the definition of `Term`

enum Term { Bool(bool), Not(Box<Term>), ... }

that your code simply does not typecheck. `Not` expects a `Box<Term>`, not a `Value`.

It's also worth noting that one would probably want to consider something like

Not(Not(Bool(true)))

a valid term, which your implementation wouldn't.

Ah yep, my mistake. I’ve missed out on the recursive inner evaluation. Traits might work better here, but it’s not so obvious.

In any case, I stand by all the other points I’ve made in my comment.

You know how hard it is to take someone seriously after they embarrass themselves like that while also trying to bring somebody's code down, & ultimately being wrong?
> Can be flattened, to approximately halve the length of the program like this

No it can't. You're missing the recursive call to `eval`.

It looks like if let guards could help with flattening it in nightly but not in stable[1]:

https://rust-lang.github.io/rfcs/2294-if-let-guard.html

[1] https://github.com/rust-lang/rust/issues/51114