5 ms·
The fact that you can even do stuff like this in Rust is amazing. It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.
by cobbzilla 7y ago
The fact that you can even do stuff like this in Rust is amazing. It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.
- capableweb 7y agoNot sure I agree with "relatively clean code" when one of the examples show "fn compose <T>(f1: impl Fn(T)->T, f2: impl Fn(T)->T) -> impl Fn(T)->T {" which is just a mix-match of keywords and other things, with tons of syntax embedded in just one line. But as always, depends on where you come from. I mostly deal with lisp languages nowadays, so guessing it's just my view that the line quoted above seems complex enough to not be interested one bit in Rust.
- optimuspaul 7y agoWhile that may be an extreme example I agree. nothing clean about that code. I've only just begun to dive into rust, but I feel like I'd need a concordance to navigate the meaning of that snippet alone. Bt then again, maybe it depends on where you come from.
- bschwindHN 7y agoTakes a week or two of studying the language. It's well worth it, in my opinion.
- aratakareigen 7y agoThat line is extremely simple to me. What makes it seem like a "mix-match" of symbols to you? The equivalent in Haskell would be "compose :: (a -> a) -> (a -> a) -> a -> a", which lacks "impl" because it boxes all closures (each closure in Rust has its own type), and lacks "Fn" because it doesn't care about what kind of access the closure needs to its context (Rust distinguishes between Fn, FnMut and FnOnce). Still, the Rust is only a tiny bit more complicated than the Haskell.
- the8472 7y agoTrait aliases will make some of those unwieldly declarations a bit easier on the eyes #![feature(trait_alias)] trait Map<T> = Fn(T) -> T; fn compose <T>(f1: impl Map<T>, f2: impl Map<T>) -> impl Map<T> { |n| f1(f2(n)) }
- The_rationalist 7y agoThis is a nice feature, do you know any other language that has something similar?
- lachlan-sneff 7y agoThat line can be simplified to: fn compose<T, F: Fn(T)->T>(f1: F, f2: F) -> F {
- tomjakubowski 7y agoLambdas have unique types, and you can't use a generic parameter in a return type (which is impl Trait's raison d'etre), so I think it would have to be this: fn compose<T, F: Fn(T) -> T, G: Fn(T) -> T>(f: F, g: G) -> impl Fn(T) - > T or: fn compose<T, F, G>(f: F, g: G) -> impl Fn(T) -> T where F: Fn(T) -> T, G: Fn(T) -> T
- Diggsey 7y agoMaybe you just miscommunicated, but you absolutely can use generic parameters in a return type: https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.collect https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.ht... ... and `impl Trait` exists to be able to have return types which are difficult, verbose, or impossible to name.
- YatoRust 7y agoYes, I think he meant that it would be a bad idea to lock all of the type parameters to the same type, because all closures have different types. So you wouldn't be able to do something like this compose(|x| x, |x| x) because the closures have the same type.
- lmm 7y agoBecause the closures don't have the same type, if I'm following you correctly.
- mathw 7y agoBecause the parameters demand the same type (and for the return value), but the supplied parameters are not the same type because the only way to get that is to pass in exactly the same instance of the closure. It's a fun little thing, and a good example of why impl Trait in argument position is a really nice addition to Rust, even though based on what we were originally excited about (impl Trait in return position for returning Iterators and other such things), the argument position form didn't seem so important.
- deleted 7y ago[deleted]
- iknowstuff 7y agofn compose<T>( f1: impl Fn(T)->T, f2: impl Fn(T)->T ) -> impl Fn(T)->T { // both arguments as well as the returned value // are functions which take and return the generic type T. // (to be precise, they implement the Fn trait). } let returned_function = compose(|val| val == 1, |val| val < 0); let value: bool = returned_function(true);
- imtringued 7y agoPerhaps you should pick your examples more carefully. That function just repeats impl Fn(T)->T three times which is exactly what yo would expect from the compose function. If there was a mix of multiple different types of functions you could demonstrate the additional complexity of Rust. What you did is just prove that Rust is the same as most languages.
- elfexec 7y ago> The fact that you can even do stuff like this in Rust is amazing. Stuff like this? You mean closures? That's what you find amazing? What is considered ordinary in other programming languages is considered amazing in Rust. Amazing. > It’s simultaneously: relatively clean code, very efficient and type-safe. I like it. You like it? Do you work for mozilla? If there is a tech evangelist of the year award, I will vote for you. I've never seen someone take absolutely nothing and try to spin it into something positive. Edit: Instadownvotes by the evangelists. I like it!
- dang 7y agoThis comment breaks quite a whole lot of the site guidelines. Please don't post in the flamewar style to HN. If you'd please review https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html and take the intended spirit of this place more to heart, we'd be grateful.
- wtetzner 7y agoThe reason it's amazing is that it's being done without a GC, while still giving you control over allocation, and even being able to inline the lambdas in many places. It's not that lambdas themselves are hard, it's that they made it work with all of the other constraints of the language.