8 ms·
Rust for functional programmers
- bpicolo 12y agoI really liked this article! The side-by-sides were very well done. Thanks!
- chrismorgan 12y agoThe stuff about boxing, Rc and Gc is simply wrong. For example: `box foo` is Box<T>, the old ~T, nothing to do with Gc or Rc. Gc is not at all clever and does not do cycle collection. There are quite a number of other wrong or slightly wrong things about Rust, and a total ignoring of many Rust conventions.
- dbpatterson 12y agoAlso, unless things have changed significantly in the past year or so (since I've last really used Rust), there are significant differences between Rust traits and Haskell type classes. You can have collections of heterogeneous types that all share the same trait, which is not true with type classes. Most descriptions I've seen say that they are more of a hybrid of Java interfaces and Haskell type classes.
- knz42 12y agoThe "stuff about boxing" has been discussed on the reddit thread, and I will have it fixed by tomorrow. Thanks for highlighting. As to "Gc not being clever", the code is clear that it is intended to be clever in the long run, so the essence of the message could stay. Maybe the wording could be made more precise, thanks again for highlighting. As to the "number of other wrong or slightly wrong things", I would be more than happy to fix the text if you have concrete remarks.
- berdario 12y agoI'd like to know what is exactly wrong, I tried to look for the reddit thread, assuming it would've been this one: http://www.reddit.com/r/rust/comments/2al19j/rust_for_functional_programmers/ http://www.reddit.com/r/rust/comments/2al19j/rust_for_functi... but there're no comments as of now EDIT: found it http://www.reddit.com/r/programming/comments/2akz51/rust_for_functional_programmers/ http://www.reddit.com/r/programming/comments/2akz51/rust_for...
- wging 12y agoIt might be useful, from a language-evangelism point of view, to show how much of this Go doesn't do. This isn't to criticize Go--but I have gotten the impression (perhaps wrongly) that these languages are competitors.
- Dewie 12y ago> It might be useful, from a language-evangelism point of view, to show how much of this Go doesn't do. I disagree, since Go is not a functional programming language.
- keeperofdakeys 12y agoRust and Go are often thought of as C++ replacements, which dcauses them to be compared, however the reality is more subtle. C++ is quite flexible, and so it finds itself in many niches where there were no languages to fit. Rust and Go are targeting two of these niches, and have the ability to compete with C++ in them. Go is designed to take advantage of large amounts of concurrency, while still providing good performance (think cloud). Rust allows low-level control while giving the OOP structure required for large programs, like web browsers, or even game engines. Go was also (mistakenly) referred to a systems language at one point, which didn't help the issue.
- bad_user 12y ago> Go is designed to take advantage of large amounts of concurrency, while still providing good performance (think cloud). Java/JVM and C#/.NET fall in that space as well and are doing a very good job, while at the same time exposing a managed runtime that can be used as a target for other languages, thus lowering the friction for bootstrapping such new languages. Because of this, in a sense, Java/.NET are better replacements for C, which has also been used for building portable libraries freely usable across multiple languages. I'm still getting thrills when using well tested, mature Java libraries from Scala, Clojure or JRuby. Where Java/.NET really suck is for building real-time or soft real-time systems, because they are managed and thus heap allocation must be managed by a garbage collector. Go has precisely the same limitation by design, with the difference that because it's younger, it doesn't have pauseless garbage collectors available for it - like say, the one from Azul Systems. Go GC is even less good than the garbage collectors available in the reference Java/.NET implementation - the one in version 1.0 at least wasn't even precise, let alone concurrent. And this is not about performance per se, btw. This is about dealing with real-time constraints. In certain contexts speaking of the "cloud", it's not enough if your server is able to respond to tens of thousands of requests per second in under 100ms per request, if you can't guarantee that kind of delivery for more than 99.99% of requests. For video decompression, another industry dominated by C/C++, the average throughput doesn't matter if you can't guarantee a consistent frame rate per second - how annoying would it be if when watching a video, the playback would suffer short freezes every couple of seconds? To make matters worse, if the GC stops the world, it can act like a global lock (GIL) and then you become aquatinted with Amdahl's law, as it severely limits vertical scaling, even if your logic is embarrassingly parallelizable. Go was referred to as a systems language, but I don't think that was a mistake on their part. If Go wouldn't have been referred as a systems language, then that would've meant admission that the real competition for Go is actually the JVM with all its languages running on top of it.
- thinkpad20 12y agoNice article! This Haskell is incorrect, though: {- Haskell -} type Maybe a = None | Just a {- x : Maybe t -} case x of None -> False Some _ -> True In that snippet, `type` needs to be `data`. Also the canonical constructors are `Nothing` and `Just` (but that doesn't have to do with correctness :)) There are also a few times where you use a single colon for a type annotation in Haskell, instead of a double colon. Also, I disagree with this: > Rust's “traits” are analogous to Haskell's type classes. > The main difference with Haskell is that traits only intervene for expressions with dot notation, ie. of the form a.foo(b). That's more of a syntactic distinction; the difference at a semantic level is much bigger: Haskell type classes extend to higher-order types. For example, Functor and Monad rely on a type with kind `* -> *`, and it can get more complex than that, for example with monad transformer classes (like State and Error). Also, an important difference is that Rust traits can be used as Java-style interfaces; for example, you can have a list of objects of different types, but that all implement `Show`.
- Argorak 12y ago> Also, an important difference is that Rust traits can be used as Java-style interfaces; for example, you can have a list of objects of different types, but that all implement `Show`. To be a tiny bit nitpicky here: you can have a list of pointers to objects of different types. Vec<Show> is illegal, Vec<&Show> is not. Still, it's like Java in that regard ;).
- dbaupp 12y agoRust traits only don't support higher order types because they are missing from the whole language, i.e. it's not a philosophical difference between traits and type classes. Also, Haskell has existential types, which are exactly like Rust's trait objects.
- thinkpad20 12y agoRust has generics, which are higher order types (you can't have just a vector, it needs to be a vector "of something"). I'm not sure if the rust team would describe them that way, but there doesn't seem to be that much of a theoretical difference. Thus if they wanted to, they could (presumably) allow for a higher-order trait like Functor: trait Functor { fn map<a, b> : (|a| -> b) -> Self<a> -> Self<b> } (I'm not sure if that's valid syntax but you get the idea). As for existential types, they're an area of Haskell I haven't really explored that much, but they seem to be seen as a bit more of an advanced feature than their equivalents in Rust or Java. Not just because they require a GHC extension and additional keywords, but because there's a bit more theoretical overhead to understand how they work, while the presentation in Rust/Java is more intuitive and straightforward.
- deleted 12y ago[deleted]
- kzrdude 12y agoWhat the for loop actually expands to is much easier to read. [<label>] for <pat> in <iterator expression> { ... } let __iterator = &mut <iterator expression>; <label> loop { match __iterator.next() { None => break, Some(<pat>) => { ... } } }
- sfvisser 12y agoMuch easier to read to who? Not to me at least. Code readability is subjective and depends on experience and familiarity.
- dbaupp 12y agoIt doesn't actually expand to that any more. You can see what it does expand to by running rustc --pretty expanded with a recent compiler: for pattern in iterator { body } becomes match &mut iterator { i => loop { match i.next() { None => break , Some(mut _value) => { let pattern = _value; { body } } } } }
- knz42 12y agoI did not know that for was actually expanded early in the compiler and that one could print out the expansion with --pretty expanded. Color me enlightened. I have incorporated this, thanks!
- CoffeeDregs 12y agoSounds as if there are some bits of the article that could be tightened up or corrected, but I found the article very helpful. It's been about 10 years since I've coded in a C-style language (Java, C#, C++), but I'm very familiar with significant-whitespace languages (Python, Haskell, Coffeescript). The C-style-ish-ness of Rust has colored my perception of the language: the features sound great... but it looks like C? Admittedly, an unfortunately shallow perspective, but we've all got a lot of ground to cover and the view from 10km is necessarily incomplete. The article's side-by-side examples really opened my eyes. I understand that Rust doesn't implement all of the functionality of Haskell, but it's comforting to see analogous functionality. So Traits aren't Type Classes; but as a beginner in Rust and as a first-order approximation, I can hear sorta-Type-Classes when I see Traits. That'll get me past the first steps, then I can dig into the detail of Traits. I would suggest the author include a section on Side Effects since that's a big talking point around functional languages. Some allow them pretty freely (Ocaml) and some don't (Haskell). The only place I really see mention of this Big Deal is a little header to a code comparison saying "Recursion with side effects:".
- deleted 12y ago[deleted]
- thinkpad20 12y agoI find it odd that you're dividing languages based on syntax. Saying "significant-whitespace languages (Python, Haskell, Coffeescript)" suggests that there's some sort of commonality between them, when they're really vastly different languages. It's almost like saying "blue-white-red countries (France, Cambodia, Russia)" :P
- sjwright 12y ago> "blue-white-red countries (France, Cambodia, Russia)" As distinct from "red-white-blue" countries (USA, Cuba, North Korea) or "blue-red-white" countries (Netherlands, Australia, Iceland). After all, most programming languages are written as lines of code that represent statements that the computer must execute in some comprehensible order. Wake me up when this metaphor is deemed passé!
- vq 12y agoAs a Haskell dev about to get into Rust (and appreciates occasional parallels to OCaml) I found this very insightful. I especially like the attention given to the tables of language literals. Not that it takes away from the purpose of the post but I couldn't help but see some issues in the Haskell code. The code under "Recursive data structures" is not valid, top-level code in a Haskell source-file isn't evaluated like in Ocaml, I'd remove the "let" keywords and put "main = " in front of the putStrLn function (which is also misspelled). As others have mentioned, the ADT example is wrong, you need to use "data" instead of "type", and the convention for the optional type in Haskell is "data Maybe a = Nothing | Just". hello x = test x This might be intentional, but it's very unconventional to put a newline after the equality sign in that short function. if v /= 1 then collatz v else return () This is better written as: when (v /= 1) (collatz v) and compares better with the Rust and OCaml code.
- haberman 12y agoIt's funny, this article is dripping with attitude about how Rust will bring poor naive C programmers who are caught in the 70s into modern language design. You could write just the same kind of article about how Rust will bring out-of-touch theorists into modern software engineering where you actually need a C-level of performance and predictability. And then show examples of all the same "for" loops with mutable variables that you're used to writing in C have direct analogs in Rust. But both bits of snark would miss the point, which is: Rust is awesome precisely because it merges these two schools of thought into something that preserves the advantages of both.
- walrus 12y agoYour comment about merging two schools of thought reminds me of two blog posts by the designer of Rust: Part 1: http://graydon2.dreamwidth.org/3186.html http://graydon2.dreamwidth.org/3186.html (discussion: https://news.ycombinator.com/item?id=8026868 https://news.ycombinator.com/item?id=8026868) Part 2: http://graydon2.dreamwidth.org/189377.html http://graydon2.dreamwidth.org/189377.html (discussion: https://news.ycombinator.com/item?id=8028255 https://news.ycombinator.com/item?id=8028255)
- haberman 12y agoGreat links, thanks! I'll have to sit down and read these in detail when I have some time.
- pjmlp 12y agoThey're already did. It was called Lisp Machines, but the market favoured the "cheapest" text UNiX systems instead.
- Dewie 12y ago> You could write just the same kind of article about how Rust will bring out-of-touch theorists into modern software engineering where you actually need a C-level of performance and predictability. Well the 'Motivation' section is about how low-level languages are a better (closer) fit to the contemporary machine architectures than functional programming. That could be interpreted to be dripping with attitude about functional programmers being out-of-touch with how machines 'really work'. (Which also seems to miss the point that functional programming languages that care about performance tries to achieve that by transforming idiomatic functional code to efficient imperative code, not trying to leverage some kind of machine model that actually matches the semantics of functional programming languages.) > And then show examples of all the same "for" loops with mutable variables that you're used to writing in C have direct analogs in Rust. Iterators seem to be more idiomatic in Rust than the loop alternatives.
- dbaupp 12y agoThis is interesting, although it contains a few misconceptions/mistakes/typos. http://www.reddit.com/r/rust/comments/2al19j/rust_for_functional_programmers/ciwlm2i http://www.reddit.com/r/rust/comments/2al19j/rust_for_functi...
- knz42 12y agoYou sir are a very patient and enlightened commenter. I am grateful for your detailed comments! I'll answer in detail in the reddit thread.
- charlieflowers 12y agoI've been very hungry for some rust doc ... as everyone knows, past some good introductory stuff, a lot of detail is not documented yet. I'd vote you up +100 if I could. Thanks for this.
- bad_user 12y agoAs an outsider with only a very superficial understanding of Rust, it seems that Rust has all the right features for functional programming, except for immutable/persistent data-structures exposed by the standard library. This is the missing link for people wanting to do FP in Rust and even though this is an issue of standard library and not necessarily one of language, this will be a source of pain for people wanting to do FP in Rust, as FP means working with referential transparency, which begets immutability. This I think is worth mentioning, being the biggest road-block when wanting to adopt an FP style in any language - after all, any language that supports higher-order functions can be used for FP (or OOP) with varying levels of pain. Again, as an outsider with a superficial understanding of Rust, it seems that Rust will make FP enjoyable in a language that doesn't need a garbage collector. But judging from articles and announcements made, Rust's primary objective to make concurrent programming safer isn't necessarily to make immutability effective, but rather to limit/control both shared reads and writes. And I can think of contexts in which you don't necessarily want that - after all, the shared reads of an immutable value are embarrassingly parallelizable, persistent data-structures working really well in say single-producer multiple-consumers scenarios. Plus immutable values are for having referential transparency and the common concurrency issues that we are commonly seeing are only a symptom. Truthfully, Rust must allow specialized algorithms and low level code, so I expect it to give the possibility of choosing your path instead of forcing a solution down your thrown. But herein we are ending with a problem also exposed when working with C++. People have been doing FP in C++ with varying levels of success. But the problem with persistent data-structures that are doing structural sharing is one of memory management. It's much harder to implement persistent data-structures without relying on a garbage collector. LISPs that encourage an FP style, Ocaml, Haskell, SML, F#, Scala and all languages advertised as FP I can think about are garbage collected. Rust can use an optional garbage collector for "shared references". But that negates the biggest advantage when using Rust, the community is moving away from shared references and Rc/Arm touched in this article would be awful solutions to this problem. Therefore I hope seeing progress in seeing a library of immutable data-structures that I could use in Rust or at least advice on how to deal with effectively immutable data-structures or something. On the bright side - Rust's type system seems to be solid (yay generics, yay type-classes), which means it's just a matter of time before the libraries will get there.
- transfire 12y agoI like a lot things about Rust. But on the downside it's syntax feels a little too much like C for comfort. Working with pointers to this extent has never been my idea of productive programming. In addition the whole Box, Rc and Arc business feels almost like a kludge. Are they part of the language or just some add on? The syntax makes it feel very much like the later.
- kzrdude 12y agoThere's a missing semicolon in the Rust collatz code, let .. match { ... } ; <- here. The Rust loop version of collatz is also missing a semicolon and missing a ! in println!.