
Rust for functional programmers - valarauca1
http://science.raphael.poss.name/rust-for-functional-programmers.html
======
CoffeeDregs
Sounds 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:".

~~~
thinkpad20
I 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

~~~
CoffeeDregs

         suggests that there's some sort of commonality between them
    

There is a commonality: they use significant whitespace. I understand that
this is a syntactic difference and, given that I said I had used each quite a
bit, that they're quite different languages. That said, syntax is chosen for a
reason and I feel most comfortable in the significant whitespace world. I'm
excited by Rust but, shame on me, the syntax had confused me into thinking the
language was more C-ish than Haskell-ish. My point in the grandparent was that
the article did a nice job of changing my (admittedly shallow) opinion.

------
chrismorgan
The 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.

~~~
knz42
The "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
I'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_functi...](http://www.reddit.com/r/rust/comments/2al19j/rust_for_functional_programmers/)

but there're no comments as of now

EDIT: found it

[http://www.reddit.com/r/programming/comments/2akz51/rust_for...](http://www.reddit.com/r/programming/comments/2akz51/rust_for_functional_programmers/)

------
haberman
It'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
Your 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
Great links, thanks! I'll have to sit down and read these in detail when I
have some time.

------
vq
As 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.

~~~
knz42
Thanks for those comments, I have incorporated them in the document.

------
dbaupp
This is interesting, although it contains a few misconceptions/mistakes/typos.

[http://www.reddit.com/r/rust/comments/2al19j/rust_for_functi...](http://www.reddit.com/r/rust/comments/2al19j/rust_for_functional_programmers/ciwlm2i)

~~~
knz42
You 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
I'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.

------
thinkpad20
Nice 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`.

~~~
dbaupp
Rust 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
Rust 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.

~~~
dbaupp
> Rust 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.

No, there's no first class "type level functions", i.e. you _always_ need
`Vec<T>`, and you can't ever write `Vec` by itself, which is what a true
higher-order type is (and what Functor needs:

    
    
      impl Functor for Vec { ... } 
      impl Functor for Option { ... }
    

Thus, your example isn't at all possible now or even in the near future.

~~~
thinkpad20
I'm aware that my example isn't possible, which is why I said, "if they wanted
to, they (presumably) could allow for..."

My point being that Rust restricts what one can do with higher-order types,
but those types still exist and are expressible in the language. From a
theoretical point of view, there doesn't seem to be any reason that they
_couldn 't_ allow for traits on higher-kinded types -- they simply choose not
to. Perhaps for performance reasons, or for simplicity, or for syntax or
whatever, but I haven't seen any reason why they couldn't. That was the point
that I was making.

------
bpicolo
I really liked this article! The side-by-sides were very well done. Thanks!

------
kzrdude
What 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>) => { ... }
            }
        }

~~~
dbaupp
It 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 } }
              }
          }
      }

------
transfire
I 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.

------
wging
It 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.

~~~
keeperofdakeys
Rust 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
> _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.

------
kzrdude
There'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!.

------
bad_user
As 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.

~~~
steveklabnik
I think once you gain a more than superficial understanding, you'll be happy
with Rust. :)

> the shared reads of an immutable value are embarrassingly parallelizable,

This is absolutely true, and Rust lets you do that. You just have to dot your
is and cross your ts:
[https://gist.github.com/steveklabnik/8de9b9b0ae2e45383d85](https://gist.github.com/steveklabnik/8de9b9b0ae2e45383d85)
Rc adds reference counting, to ensure that this reference will be okay for the
duration of all of the threads. If you just used a regular old boxed value,
one of the threads could invalidate the reference.

That's just one example. As you say, there'll be libraries eventually: people
have been focused on the language itself, and given how easy Cargo makes it
(will make it) to share libraries, I wouldn't be surprised to see an awesome
persistent/immutable data structures library appear.

~~~
bad_user
Well I definitely want to play with Rust and seems really promising, even from
the point of view of a superficial observer.

My impression is that reference counting, while deterministic, has some
problems due to reliance on atomics (which gives rise to contention issues,
even in cases in which intuitively there shouldn't be any contention) and
because of cyclic references - but maybe this isn't such a problem in practice
when used for persistent data-structures or if there are multiple versions of
an interface suited for different scenarios.

My intuition has definitely been proven wrong before. And I have high hopes
for Rust - seemingly being a language allowing for FP and for fine grained
memory management while not setting my hair on fire :-)

~~~
kimundi
Rust actually has two different reference counted smart pointers: `Rc`, which
uses non-threadsafe reference counting, and hence is not sendable across
thread boundaries; and `Arc`, which uses atomic reference counting and can be
send across thread boundaries.

You can definitely create leaking cycles with both though, but Rc and Arc also
support Weak pointers to safely break up cyclic ownership trees.

