3 ms·
I'd probably write this fn add(x: Option<i32>, y: Option<i32>) -> Option<i32> { match (x, y) { (Some(x), Some(y)) => Some(x + y),
by nicoburns 4y ago
I'd probably write this
fn add(x: Option<i32>, y: Option<i32>) -> Option<i32> {
match (x, y) {
(Some(x), Some(y)) => Some(x + y),
_ => None,
}
}
- Yoric 4y agoAgreed. That's way more readable.
- deleted 4y ago[deleted]
- dgb23 4y agoI like this the best. It is dumb and clear. Anyone can read this even with minimal to no Rust experience but it still flows elegantly.
- stouset 4y agoI'm sorry, but this perspective is absolutely bizarre to me. We should not avoid language features that reduce boilerplate and drastically increase comprehension for for people who have experience in a language in order to cater to people who have minimal or no experience in that language. Should we not use `?` in Rust because it might be obscure to someone who's never used the language? Should we not use any of the `Iterator` functions (including `zip` and `map`) because they might be confusing to C programmers who only know `for` loops? Functional concepts are everywhere these days. Most of them are not hard. `zip` and `map` do not require understanding of homotopy type theory to understand, they are essentially trivial functions. They are available across every type you could possibly iterate over in Rust, and if you understand what they do on one of those you essentially understand what they do on all of them. This is a toy example, but virtually every piece of hard evidence we have in this field shows that—within reason—more concise code has fewer bugs and is easier to comprehend than longer, more verbose equivalents. Writ large across a project, doubling or tripling the amount of code to bend over backwards accommodating complete novices is lunacy.
- Zababa 4y agoThe `zip` and `map` functions used here are actually functions of the module std::option, and not std::iter. While they are the same idea "in essence", they have different implementation. The std::option ones are a simple pattern match, while the std::iter ones are more complex. For example, std::iter::zip returns an std::iter::Zip, while std::option::zip returns an Option of a tuple. I'll also add that option's zip and map are also implemented with a pattern match, like above. One error and one deviation from the established norm for a toy example is a lot. At the scale of a codebase it would be a catastrophe.
- stouset 4y agoYes, I know. But if you look closely you'll notice that `zip` and `map` were called directly on an array here and not actually on an iterator. That's a third implementation of the same concepts. If Rust had HKTs they could all be the exact same implementation, but not today. The important thing, though, is that they all conceptually do the same thing. Understanding one essentially translates to understanding them all. If zip/map are called directly on two Options, you get an Option containing a tuple back out. If they're done on two arrays, you get an array containing tuples back out. If they're done on two iterators, you get an iterator containing tuples back out. There's nothing to be confused about.
- Zababa 4y ago> But if you look closely you'll notice that `zip` and `map` were called directly on an array here and not actually on an iterator. No, I don't think that's true, unless we're talking about two different things. In the article, and in the following post https://news.ycombinator.com/item?id=34428999 https://news.ycombinator.com/item?id=34428999, zip is user on an Option<i32>, takes another Option<i32>, return an Option<(i32, i32)> (which is a tuple, not an array), on which map is applied to extract the two values and add them. > If zip/map are called directly on two Options, you get an Option containing a tuple back out. If they're done on two arrays, you get an array containing tuples back out. If they're done on two iterators, you get an iterator containing tuples back out. But that's my whole point. std::option::map is not the same function as std::array::map, which is not the same function as core::iter::Iterator::map. One big difference, for example, is that core::iter::Iterator::map is lazy, while the others are not, hence the note to try to avoid chaining std::array::map, and being careful around it in performance-critical code: https://doc.rust-lang.org/src/core/array/mod.rs.html#466 https://doc.rust-lang.org/src/core/array/mod.rs.html#466. Even with HKTs, while you could share some code, that wouldn't solve the fact that the "direct map" (std::option::map for example) is strict, and the other map (std::option::iter::map) is lazy. Especially in a language often used for performance-sensitive tasks, I can't agree that understanding one map translates to understanding them all, since that would be ignoring part of their ergonomics, and more importantly their performance characteristics.