Hacker News new | ask | show | jobs
by klibertp 4685 days ago
The example of match usage is really poor. From the looks of it there is no "pattern matching" going on at all here. I don't know the syntax, but even something like this would be a better demonstration:

    let computed_key = match (key.len() > self.block_size, key.len() < self.block_size) {
        (true, false) => self.zero_pad(self.hash(key).digest),
        (false, true) => self.zero_pad(key),
        (false, false) => key
    }
At least there is some matching here, unlike in the original example. But in real world this should be if..else if..else statement, not match - the latter being used makes no sense if there is no matching going on. In Erlang there is an if statement, which is a 'case' (equivalent of match here) but with matches taken out and only guards allowed - that's what would fit here, but I doubt Rust has something like this.

Also, does the compiler complain about my example above being not exhaustive? I think in OCaml it would, which is sometimes nice.

4 comments

>but I doubt Rust has something like this.

Oh, but it does! There's the cond! macro:

    let computed_key = cond!(
        (key.len() > self.block_size) { self.zero_pad(self.hash(key).digest) }
        (key.len() < self.block_size) { self.zero_pad(key) }
        _                             { key }
    );
As noted by masklinn, the compiler will complain about the non-exhaustive match. Stuff like this makes me want a dependently typed language for practical programming, in which I can express the concept of "this case ought to be impossible", and convince the compiler by supplying a proof. Also, this would eliminate the false dichotomy between "fast, unchecked, unsafe array indexing" and "slow, checked, safe array indexing", which would be a huge win for safe systems programming.
There are some languages heading towards this. ATS [1] provides a restricted form of dependent types [2] that allows you to do what you want and is designed for systems programming. You can do safe pointer arithmetic for example.

Idris [3] is another language with dependent types (and is more Rust-like) designed for systems programming. I don't think Idris has type level presberger arithmetic though making some things a bit verbose.

[1] http://www.ats-lang.org/ [2] http://bluishcoder.co.nz/2010/09/01/dependent-types-in-ats.h... [3] http://www.idris-lang.org/

I assume you're familiar with ATS? What don't you like about it?
I am not the original poster but I'll respond to this one:

I am familiar with ATS and I don't like that I am only familiar with it from the Language Shootout contest website. I haven't heard of it used in production, no blogs, no news on HN.

It is a bad excuse and rather sad but my main charge against it is that it is not popular enough.

I agree not much is heard about it. I blog about it a fair amount on [1]. There's also /r/ats [2]. I use it in production. The backend of my bitcoin mining pool [3] is written in ATS and has about 1% of the total bitcoin mining capacity and has been operating for a couple of years. The front end of the pool is written in Ur/Web.

[1] http://bluishcoder.co.nz/tags/ats/ [2] http://www.reddit.com/r/ATS/ [3] http://mmpool.bitparking.com

No tactics.
Dependent typing would be throwing out the entire type system, and also be incredibly ambitious. Dependent type systems are great, but I think the Rust team made the right decision here.
> Also, does the compiler complain about my example above being not exhaustive?

Yes, although the error message isn't very good:

    crypto.rs:55:23: 59:5 error: non-exhaustive patterns: true not covered
    crypto.rs:55     let computed_key = match (key.len() > self.block_size, key.len() < self.block_size) {
    crypto.rs:56         (true, false) => self.zero_pad(self.hash(key).digest),
    crypto.rs:57         (false, true) => self.zero_pad(key),
    crypto.rs:58         (false, false) => key
    crypto.rs:59     };
    error: aborting due to previous error
And technically that's not a good thing, since (true, true) is not possible considering the input.
it should be expressed with conditionals anyway.

    match key.len() {
    k if k > self.block_size => ..
    k if k < ... => ..
    k  => ...
or even

    match key.cmp(self.block_size) {
    Equal => ..
    Greater => ..
    Less => ...
Absolutely.
Thanks for the tip!
You're right that it's not an ideal example. I think I said that in the post. The context of the article was the week I spent with Rust and the project I actually did with it. There was not a better example in my code to show. I could have contrived something, but it wasn't really in spirit of what I was writing about.
You probably should fix this in your code too. As noted in this thread, that's not a proper way to use pattern matching. Anyone reading your code will be confused by this usage and that's not a good thing. It probably would be best to use this:

    match key.cmp(self.block_size) {
        Equal => ..
        Greater => ..
        Less => ...
    }
Which is much prettier (and more succinct) than both your original code and my example. You could also use a `cond!()` macro here, but that would mean you don't have an example for a blog post, so I think the above code is the best option. Thanks to kzrdude for posting.