6 ms·
Speaking of ergonomics, I seem to write a good deal of this for short-circuiting in Rust: let x = match x { Some(x) => x, None => return None,
by always_good 9y ago
Speaking of ergonomics, I seem to write a good deal of this for short-circuiting in Rust:
let x = match x {
Some(x) => x,
None => return None,
}
Rust doesn't have any other way to short-circuit and it gets pretty tedious.
I think Kotlin's named returns are most ergonomic of all. That way you can just short-circuit from anywhere whether in the middle of a fold or a deeply nested iteration.
fn foo() {
for x in xs {
for y in ys {
if y == 42 {
return@foo 42
}
}
}
}
The other thing I'd like for other languages to steal for Kotlin is how everything has the .let/.apply/etc. methods.
I'm rusty so this is more pseudocode, but these methods basically let you bring your own chaining to any value.
let y = x.let { x ->
x + 100
}.apply { x ->
println(x)
}.let { x ->
x * 2
}
Once you use it, it feels pretty silly that library authors in other languages have to deliberately design a chainable API for the same ergonomics. ...And infuriating when you want to chain some more in other languages but you've run out of chain context.
Like when you use `.unwrap_or()` in Rust but still want to continue chaining on to the value. But can't because it's not in a container anymore with a chainable API.
let x = maybe
.map(foo)
.and_then(bar)
.unwrap_or(42);
// ugh, want to apply a function to the result so
// far but can't use `.map(to_base16)` because
// it's been unwrapped into u32.
Just some things from my wishlist for the next language someone makes.
- glandium 9y agolet x = match x { Some(x) => x, None => return None, } This implies your function returns an `Option`, and that `x` is an `Option` too. In which case you can just do let x = x?;
- always_good 9y agoWell, you may be short-circuiting for any reason, from any signature. For example, look a Swift's guard statements. It even narrows the type down-scope after you short-circuit. If you borrow in the `match {expr}`, Rust still thinks it matters when you're trying to short-circuit in the `None` branch and will complain about the borrowed lifetime. Though this is something #![feature(nll)] fixes. You can implement std::ops::Try for a custom data structure, but that doesn't help you in any of the cases where you just want to do some day-to-day short-circuiting without inventing your own data structure. I shouldn't have used Option, though. It was a bad example.
- yjh0502 9y agoI used it frequently when mixing `Result` with `Future`. For example, fn do_something() -> Box<Future<Item=(), Error=Error>> { let val = match do_result() { Ok(x) => x, Err(e) => return Box::new(err(e.into())), }; // do something with `val` unimplemented!(); }
- curun1r 9y agoIf you want to cut down on lines, this is equivalent: let val = do_result().map_err(|e| Box::new(err(e.into())))?;
- bschwindHN 9y agoRust has ? for early returns when returning an Option, and it has named breaks as well. fn foo(xs: Vec<u32>, ys: Vec<u32>) { 'bar: for x in &xs { for y in &ys { if *y == 42 { break 'bar; } } } }
- gsg 9y agoA bunch of languages have something much like .let/.apply already, since it's pretty much just function application in reverse order: "foo" |> String.length |-> printf "%d\n" |> fun x -> x + 100
- twic 9y agoI often miss Ruby's 'tap' when writing Java. Happily, in Rust, it's easy enough to add yourself: https://play.rust-lang.org/?gist=d40d8ca9bd77bbd60626be727404a69e&version=stable https://play.rust-lang.org/?gist=d40d8ca9bd77bbd60626be72740... I only implement let and also from Kotlin, because Rust doesn't really have the idea of method receivers in quite the same way as Kotlin, so the others are pointless.
- zbentley 9y agoFor this example of yours: let y = x.let { x -> x + 100 }.apply { x -> println(x) }.let { x -> x * 2 } I think you've just reinvented imperative control flow. Isn't your example just modeling a reusable function that represents a series of instructions on a value? func(x) { let y = x + 100 println(y) let z = y * 2 return z }