
A practical introduction to functional programming (2013) - tosh
https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming
======
vvanders
This is a slight Rust tangent but I've been thinking a lot about the
properties that make functional programming interesting and are called out in
the article. Specifically the immutability providing an easy way to reason
about functions via only inputs and outputs.

I feel the the memory model of Rust(single mutable ref or unlimited non-
mutable refs) combined with the fact that there are no mutable globals(inside
safe code) gives you a much easier system to reason about. I know based on
inputs and outputs what could be mutated and since I know there's only one
mutable reference I can trace that single ref easily.

It really feels like getting a large majority of the benefits of functional
programming without sacrificing the performance and predictability we've all
become accustomed to in non-functional languages.

~~~
wyager
Immutability is one of many advantages of functional programming. Other
advantages include higher-order functions (which Rust cannot support very well
with its memory model), stronger type systems (only possibly because the
Lambda calculus is on much better mathematical footing than any production
imperative language semantics), and better/safer state management via ADTs
(which Rust has decent support for).

That's the risk of reading articles like the OP, where they show you a couple
functional tricks in an imperative language that inherently can't give you the
whole picture. You get too limited an idea of what it means to use modern
functional techniques. You simply cannot do anything approaching cutting-edge
functional programming in a language like Java or JavaScript or Rust. They all
lack some important aspects of what makes functional programming useful.

~~~
tps5
Can you explain why Rust "cannot support higher order functions very well?"

I'd also like to hear about "what makes functional programming useful" if you
have a minute.

~~~
wyager
Sure. It's just because when you're passing around functions of type (a -> b)
the only fixed-size representation is every possible input/output pair, which
is obviously not a good representation. Instead we use a variable-size
representation in the form of code (passed around as a code pointer) and
captured variables (the format of which depends on the function being passed
around). Together these are called a closure. Using variable-sized closures
created at runtime requires dynamic allocation, which rust allows for but does
not make convenient (intentionally). Sometimes you can get around this but not
always. So the kind of stuff you can do with higher-order functions in rust is
more restricted and usually has extra rigamorole required for memory
management. It's not that rust is doing something wrong, it's just a necessary
consequence of having such excellent guarantees about allocation (or lack
thereof).

> I'd also like to hear about "what makes functional programming useful" if
> you have a minute.

This is sort of a book-length topic and I'm on my phone, but a few points
worth looking up are (Generalized) Algebraic Data Types, Typeclasses (in
particular Functor, Foldable, Applicative, Traversable, Monad (in particular
Maybe, Either, and State)), Higher Kinded Types, corecursion, the Y
combinator. The gist of it is that you can do a bunch of cool stuff you
wouldn't be able to do (or even think about doing) in an imperative language.
For some reason we're not entirely sure of, it seems to be way easier to
isolate the essence of what we're trying to accomplish when using functional
programming than when using imperative programming, and therefore to automate
the boring work normally associated with doing things to data, like writing
loop definitions.

It's kind of like how natural functional type systems have the same derivation
semantics as various logical systems, even though it's not entirely clear why
that ought to be the case. We seem to have just stumbled upon an abstraction
that meshes nicely with the platonic universe of useful computer programs, as
opposed to an abstraction that only exists because of the particulars of how
our computers work.

~~~
hota_mazi
> The gist of it is that you can do a bunch of cool stuff you wouldn't be able
> to do (or even think about doing) in an imperative language

That's an odd claim and one I've never heard. Surely you can do everything you
can with imperative programming as you can with FP, it's just that you will do
it differently and, arguably, in a way that will present some downsides (e.g.
lack of safety).

Also, for what it's worth, you are enumerating a list of characteristics of
functional programming but you're not answering OP's question, which was: what
makes FP useful?

~~~
madmax96
Obviously, functional and imperative languages are equivalent in terms of
theoretical expressive power. That being said, functional languages often make
it easier to build useful abstractions that are more correct. Okasaki's
implementation of Red Black trees are an excellent example of this...

~~~
hota_mazi
How do you define "more correct"?

Sure, the CH isomorphism can help you prove some properties of your code based
on its types, but that doesn't mean you can't create an equally correct
program imperatively.

~~~
wyager
> that doesn't mean you can't create an equally correct program imperatively.

You're getting hung up on the fact that the languages are all Turing complete.
That's true, but not really relevant. The important thing is that it's _much
easier_ to do things correctly with functional constructs like (G)ADTs and
strong type systems.

~~~
hota_mazi
You're preaching to the choir about the superiority of static type systems,
but your argument was about imperative vs/ functional, not dynamic vs/ strong.

GADT's and composition are trivial to achieve with imperative languages.

~~~
wyager
There's a reason not a single production imperative language supports GADTs.
Imperative sequencing and mutability semantics, as done in practice, are
inimical to rigorous formalization. When imperative language designers try to
make a nice consistent type system, they hit road blocks early on and give up.
Rust doesn't even support HKTs. GADTs are just an example of something that
you rarely see in imperative languages because they almost never even make it
that far.

One of the other nice things you get with languages with good
functional/pure/immutable(/lazy) semantics are free theorems, where you can
actually make strong useful statements about the nature of your program like
"this will not crash", "this is memory safe", "this will terminate", "this
follows the functor laws", etc. whereas it's effectively impossible to make
such claims in a language with messier abstractions like loops and mutable
variables.

Basically, the reason functional languages have better type systems is that
their underlying untyped semantics are more sensible and simpler than the
untyped semantics of mutable imperative (strict) languages, and this is
reflected in the safety records of languages through mechanisms other than
type safety.

~~~
hota_mazi
> There's a reason not a single production imperative language supports GADTs.

Scala?

OCaml would also qualify as a counter example, although you cheated a bit by
adding all these adjectives and you could argue that OCaml is not
"production".

Haskell can easily do imperative as well.

I agree with you overall but I think you're drawing too strict a line between
these concepts. Most of the guarantees you obtain from these languages do not
even come close to "this will not crash" (this is very hard to guarantee in
languages with no totality guarantee, i.e... most of them), let alone "this
will terminate" (since most of these are Turing complete).

~~~
wyager
> Scala?

Well, I'd say Scala is definitely a functional language, but even then its
GADT (and even plain old ADT) support is pretty limited and uncomfortable.
"Case classes" are one of those things in Scala where it's obvious they're
running into the limits of the JVM.

> OCaml would also qualify as a counter example

Again, I'd say OCaml is absolutely a functional language. It's also definitely
a production language; I know of a number of firms that use it.

I should have said "Imperative and not Functional languages", a la C, C++,
Java, Javascript, etc.

> Most of the guarantees you obtain from these languages

You're right, most of the guarantees you get from e.g. Haskell rely on people
following the typeclass laws for whatever you're doing, which isn't
necessarily the case. It's not a guarantee in the sense that e.g. Coq or Agda
give you a guarantee; it's just a guarantee in the sense that if you follow
some simple rules, you get good behavior.

As for totality and termination, it's true that you can't guarantee either in
plain old Haskell, but Haskell (and some others) do support checking for
pattern match completeness, and then it's not very hard to (informally)
guarantee totality by sticking to certain pre-defined operations that operate
on data (as opposed to codata) and have good decreasing/tightening rules. For
example, if I saw some code composed entirely of functor/foldable/traversable
operations over well-behaved data structures, I could be quite confident in
correctness and termination.

------
dispose13432
The thing with functional languages is that most imperative languages can "do
functional things", but not vice versa.

For example, many algorithms are beautiful when expressed in a recursive
manner (Fibonacci, for example) , and pretty much all languages permit one to
do recursion (even old fashioned C).

But many algorithms are much more elegant when expressed through for loops.
For example, if one needs to iterate through lines keeping state into account,
a

for (int i =0;i<len;i++){ if(lines[i] == "a"){ i+=2; } }

seems much cleaner than an equivalently functional algorithm.

\-----

EDIT.

I (purposefully) left out the "main" logic (as it's not really relevant to the
post).

Of course in a "real" example, the code would look like: for (int i
=0;i<len;i++){ if(lines[i] == "a"){ i+=2; } else{ parseLine(lines[i]); } }

The goal is to skip the line after a line starting with the letter "a".

~~~
gizmo686
>for (int i =0;i<len;i++){ if(lines[i] == "a"){ i+=2; } }

This algorithm can, without modification, be done in a functional language.
For example, consider the following Haskell:

    
    
      import Control.Monad.Loops 
      import Control.Monad
    
      for_ :: (Monad m) => m () -> m Bool -> m () -> m ()
      for_ init guard step body = 
          init >>
          whileM_ guard (body >> step)
    
     
      loop :: [string] -> State Int ()
      loop lines = for_ (put 0) (get (<(length lines))) (modify (+1)) (when (get >>= \i -> (lines!!i == "a")) (modify (+2)))
    
    

I did not use do notation to avoid accusations of cheating; and am defining
for_ here because Haskell's standard library for loop only iterates through
data-structures (like the for loop of many imperative languages). I will also
admit that the Haskell version is ugly due to its verbosity (because, even
though it is possible, it is not how you are supposed to do things in
Haskell).

EDIT: Made for_ generic and added type signature for loop.

If you want multiple "variables" in this approach, you can define a datatype
to store them:

    
    
        data S = S { i :: Int ... }
    

In this case, we can access i by replacing "get" with "gets i" in the above
example ( "i" being an accessor function that Haskell automatically defines
when we create a datatype with a field called "i"). You can imagine the
generated value "i" being a more complicated datatype that can be both a
getter and setter, in which case we can define "puts i" to be the analog of
"gets i" and "put".

I think I have seen this approach done in Haskell but cannot remember what it
is called.

~~~
dvfjsdhgfv
> This algorithm can, without modification, be done in a functional language.
> For example, consider the following Haskell:

The OP clearly stated the iterative version is cleaner, and you seem to
confirm his pint.

~~~
mathetic
Except it is not cleaner. It is just more familiar to you because most
languages in use in the past 40 years have been influenced by Algol family of
languages for their syntax.

Now the author is being modest here:

First, `for` is defined out of simpler function `while`. You can do the same
for the for loop in C of course, but only as a function call and to pass
arbitrary body you would need to use a function pointer, which I argue is
overwhelmingly more confusing. You may have different opinions merits of of
baking non-orthagonal features into the compiler, but surely you'll appreciate
the clarity here.

Second, `do` notation is not used which would make it look more like Algol
family of languages with semicolons to delimit (if "statements" (really,
monadic expressions) are on the same line otherwise line feed is enough.) and
less `>>=` would appear.

Third, giving type annotations which can always be inferred unless you are
using advanced extensions such as Generalised Algebraic Data Types (GADTs),
which are not used here. Also less characters do not mean cleaner. For example
the `loop`'s type annotation produces a `State` monad with `Int` as the state.
This means that `loop` will not be connecting the Internet, it will not be
throwing an exception [0], etc. Even in the narrow effect of manipulating
state it guarantees it will only simulate mutation of a single variable of
type `Int`. All of that guaranteed from a single line is a remarkable
manifestation of clarity.

[0] I don't account for undefined and the problem of strictness, that's a
discussion for another day.

------
etaty
If your function for the same input give you a different output, then you are
in dysfunctional world. Hence don't put random() in the middle of your
supposed pure function.

    
    
      Remove state
    
      This is a functional version of the car race code:
    
      from random import random
    
      def move_cars(car_positions):
        return map(lambda x: x + 1 if random() > 0.3 else x,
                   car_positions)

~~~
JustSomeNobody
How, then, is randomness handled?

~~~
bbotond
For example, a random number is passed as a parameter, so that the function
can be kept pure.

~~~
Kenji
Then how do you obtain such a random number that you use as a parameter? After
all, it cannot be the result of a function, because then we'd be in a
'dysfunctional world'. Yet, it cannot be a constant either. I guess you'd have
to handle it like file IO, that is, you are forced to introduce that impurity
into your code.

~~~
hepta
You use a generator that receives a state and returns a random number and the
modified state. You'd want some additional abstractions to make that work
well.

~~~
junke
The abstraction then becomes indistinguishable from a language feature, and
you have come back full-circle to where you started. You call "random" without
managing the state explicitly, except when you need to.

[http://clhs.lisp.se/Body/f_random.htm](http://clhs.lisp.se/Body/f_random.htm)

~~~
caconym_
I might just not understand your meaning but I don't think what you're saying
about abstractions becoming indistinguishable from language features is true.
Here's a (pure) Haskell function that is passed a random number generator and
"returns" an infinite list of random numbers:

    
    
      infiniteRandoms :: (RandomGen g, Random r) => g -> [r]
      infiniteRandoms = unfoldr (Just . random)
    

This just uses a standard function from the ubiquitous `Data.List` module
(`unfoldr`) and the most basic functionality from the `System.Random` module.
Nothing about that is "indistinguishable from a language feature", IMO.

In other languages, of course, this operation would look different and
potentially have side effects depending on the libraries used.

~~~
junke
> Nothing about that is "indistinguishable from a language feature", IMO.

You are right in this case.

The parent comment said "You'd want some additional abstractions to make that
work well.". Maybe passing around a random generator explicitly is not what we
want. We abstract things away because we want to focus on "what" some code
does, not necessarily "how". Most of the time, I _want_ my random numbers to
be random; the case where I need to replicate a specific sequence of random
events is actually rare. That's why "random" is available as a system facility
and does not need to take the random generator's state explicitly ("There is a
single, implicit, global random number generator",
[https://hackage.haskell.org/package/random-1.1/docs/System-R...](https://hackage.haskell.org/package/random-1.1/docs/System-
Random.html#g:3))

My point is that hidden state is not bad when the explicit simulation of state
is cumbersome to use. As an aside, that's also the reason I dislike Go's
approach to error handling. There is so much praise about having everything
visible, based on the claim that "there is no happy path"

[https://www.reddit.com/r/programming/comments/30ad8b/why_gos...](https://www.reddit.com/r/programming/comments/30ad8b/why_gos_design_is_a_disservice_to_intelligent/)

 _There is no happy path. This is a very common misconception which causes so
many programs to be unreliable POSes... handling errors is very important,
just as important as handling success. Hell, 99% of the time, it 's not an
error, it's just an alternate option. Hey, the file isn't there... that's not
an error, it's just a different possible state of the universe. Error code is
application code._

And yet, error handling code follow certain patterns that can be abstracted
away (99% of the time!).

Besides, in Go forums or blogs, you can spot code samples where "error
checking is omitted for clarity", which I find quite representative of the
kind of noise explicit error handling code introduce:

[https://medium.com/@nate510/don-t-use-go-s-default-http-
clie...](https://medium.com/@nate510/don-t-use-go-s-default-http-
client-4804cb19f779#.eni0ruiek)

[https://devhub.io/repos/kshedden-gonpy](https://devhub.io/repos/kshedden-
gonpy)

[https://github.com/alecthomas/mph](https://github.com/alecthomas/mph)

[https://groups.google.com/forum/#!topic/golang-
nuts/f-OH4nas...](https://groups.google.com/forum/#!topic/golang-
nuts/f-OH4nasrzg)

[http://stackoverflow.com/questions/37723883/fill-an-
object-i...](http://stackoverflow.com/questions/37723883/fill-an-object-in-
golang)

~~~
hepta
> Maybe passing around a random generator explicitly is not what we want.

That's fair. What I often want is to pull a function out of my program and
test it without a lot of work. This is easy if the function is pure.

> My point is that hidden state is not bad when the explicit simulation of
> state is cumbersome to use.

That depends on your goal. If your intention is to make state explicit, then
by definition there has to be more code to make it so. Again, a preferable
abstraction for this makes the boilerplate go away, while keeping everything
pure. That added effort gives you testability but I know there are instances
where that effort is not worth it.

------
kp25
Discussions related to the same post 2 years back..

[https://news.ycombinator.com/item?id=8943356](https://news.ycombinator.com/item?id=8943356)

------
rockshassa
I don't understand the couple other posters who are dismissing this article.
it is exactly what it claims to be, a practical introduction to functional
programming. I'd also like to echo poster vvanders sentiments about Rust and
how it compliments the functional style. I've experienced the same feelings,
although I did it while working in Swift. And for those out there asking "why
is FP better", I submit that it allows for fewer ways for me to shoot myself
in the foot.

------
dmitriz
Here is the best written piece I know about the FP benefits (Hint: it is not
only vs imperative):

[https://drboolean.gitbooks.io/mostly-adequate-
guide/content/...](https://drboolean.gitbooks.io/mostly-adequate-
guide/content/ch2.html)

------
happynewyear
>> runs the function on each item in the original collection and inserts each
return value into the new collection.

some poor schmuck new to FP will come around, read that and scratch head
saying "isn't that iterating?"

------
Veedrac
I wasn't impressed with this the first time I read it, and my opinions haven't
changed since then. A lot of the comparisons here are straw men, and a lot of
the proposed solutions are unnecessarily hard to understand. I'm not going to
repeat all my points, so here's a link to the previous criticism.

[https://www.reddit.com/r/Python/comments/30w1bn/an_introduct...](https://www.reddit.com/r/Python/comments/30w1bn/an_introduction_to_functional_programming_using/cpwkm4v/)

------
visarga
Functional looks good for applying x^2 to a list of numbers, but when you need
to do something a little more complex, it becomes hard to put in in a single
line/lambda.

~~~
bhrgunatha
You decompose the complex task into separate other tasks and compose them
together the same way that you would in any other paradigm. You wouldn't
expect to express a complex task in a single line in an OO program for
example.

documents = filter(pdf, folder)

summaries = map(summarise_document, documents)

Is pretty clear despite summarising a document being a complex task.

~~~
visarga
I appreciate how nice the functional style is, but sometimes a for loop acting
over a few local variables is simpler to read than a bunch of map's and
lambdas. Does this look readable and nicer to you than a for loop?

    
    
        print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                                    call(lambda x: x.replace('.', ''), 'name'),
                                    call(str.title, 'name'),
                                    extract_name_and_country]
    

Sometimes it's easier to implement directly what you want done than to
remember the name of the function that plucks a field from a list of records
or something like that.

~~~
caconym_
You seem to be obsessed with this idea that "functional programming" is a
fancy term for squishing your entire program onto one line via gratuitous use
of anonymous functions, which simply isn't true. I don't know what this
snippet of code is supposed to do, and it's not very pretty, but guess what:
it is just as easy to produce unreadable garbage in an "imperative style".
I've seen a lot of both, but more of the latter.

