3 ms·
data.iter() .filter(|w| w.alive) .map(|w| w.id) .collect() collect(map(filter(iter(data), |w| w.alive), |w| w.id)) The second approach is
by mrkeen 2y ago
data.iter()
.filter(|w| w.alive)
.map(|w| w.id)
.collect()
collect(map(filter(iter(data), |w| w.alive), |w| w.id))
The second approach is open for extension - it allows you to write new functions on old datatypes.
> Quick challenge for the curious Rustacean, can you explain why we cannot rewrite the above code like this, even if we import all of the symbols?
Probably for lack of
> weird operators like <$>, <*>, $, or >>=
- esafak 2y agoExtension methods to the rescue: https://en.wikipedia.org/wiki/Extension_method https://en.wikipedia.org/wiki/Extension_method Examples: https://kotlinlang.org/docs/extensions.html https://kotlinlang.org/docs/extensions.html https://docs.scala-lang.org/scala3/reference/contextual/extension-methods.html https://docs.scala-lang.org/scala3/reference/contextual/exte... See also: https://en.wikipedia.org/wiki/Uniform_function_call_syntax https://en.wikipedia.org/wiki/Uniform_function_call_syntax
- vips7L 2y agoI really wish you couldn't write extensions on nullable types. It's confusing to be able to call what look like instance functions on something clearly nullable without checking. fun main() { val s: String? = null println(s.isS()) // false } fun String?.isS() = "s" == this
- usrusr 2y agoThe difference between .let{} and ?.let{} has great utility. You'd either have to give that up or promote let from regular code in the standard library to magic language feature. And you'd lose all those cases of extension methods where the convenience of accepting null left of the dot is their sole reason to be. Null is a valid state, not something incredibly scary best dealt with with a full reboot or better yet throwing away the container. Kotlin is about making peace with null, instead of pretending that null does not exist. (yes, I'm looking at you, Scala) What I do agree with is that extension methods should be a last ditch solution. I'd actually like to see a way to do the nullable receiver thing defined more like regular functions. Perhaps something like fun? name() = if (this==null) "(Absent)" else this.name that is defined inside the regular class block, imported like a regular method (as part of the class) and even present in the class object e.g. for reflection on the non-null case (and for Java compat where that still matters)
- mrkeen 2y ago> Null is a valid state, not something incredibly scary best dealt with with a full reboot or better yet throwing away the container. Agreed. It should be a first-class construct in a language with its own own proper type, Null null; rather than needing to hitch a ride with the Integers and Strings like a second-class construct.
- vips7L 2y agoPersonally I find ?.let et al to be terrible for readability; most of the time you're better off doing a standard != null check. The same with nullable extension functions; they hurt readability. > Null is a valid state, not something incredibly scary best dealt with with a full reboot or better yet throwing away the container. Kotlin is about making peace with null, instead of pretending that null does not exist. (yes, I'm looking at you, Scala) I honestly find this to be such a weird thing to say or imply. No one is "scared" of null.
- rikthevik 2y agoCame here for the Uniform function call syntax link. This is one of the little choices that has a big impact on a language! I love it! I wrote a little pipeline macro in https://nim-lang.org/ https://nim-lang.org/ for Advent of Code years ago and as far as I know it worked okay. ``` import macros macro `|>`\* (left, right : expr): expr = result = newNimNode(nnkCall) case right.kind of nnkCall: result.add(right[0]) result.add(left) for i in 1..<right.len: result.add(right[i]) else: error("Unsupported node type") ``` Makes me want to go write more nim.
- Mond_ 2y ago> The second approach is open for extension - it allows you to write new functions on old datatypes. I prefer to just generalize the function (make it generic, leverage traits/typeclasses) tbh. > Probably for lack of > weird operators like <$>, <*>, $, or >>= Nope btw. I mean, maybe? I don't know Haskell well enough to say. The answer that I was looking for here is a specific Rust idiosyncrasy. It doesn't allow you to import `std::iter::Iterator::collect` on its own. It's an associated function, and needs to be qualified. (So you need to write `Iterator::collect` at the very least.)
- higherhalf 2y ago> It doesn't allow you to import `std::iter::Iterator::collect` on its own. It's an associated function, and needs to be qualified. You probably noticed, but it should become a thing in RFC 3591: https://github.com/rust-lang/rust/issues/134691 https://github.com/rust-lang/rust/issues/134691 So it does kind of work on current nightly: #![feature(import_trait_associated_functions)] use std::iter::Iterator::{filter, map, collect}; fn get_ids2(data: Vec<Widget>) -> Vec<Id> { collect(map(filter(Vec::into_iter(data), |w| w.alive), |w| w.id)) } fn get_ids3(data: impl Iterator<Item = Widget>) -> Vec<Id> { collect(map(filter(data, |w| w.alive), |w| w.id)) }
- Mond_ 2y agoOh, interesting! Thank you, I did not know about that, actually.
- pornel 2y agoRust has such open extensibility through traits. The prime example is Itertools that already adds a bunch of extra pipelining helper methods.