100 comments

[ 1.7 ms ] story [ 377 ms ] thread
Smashing Magazine on functional programming, getting most of it wrong. This is the very moment functional programming jumps the shark.

It's too mainstream for me now, I'll have to switch to reactive programming.

Too late, Erik Meijer already declared the term reactive was washed out.
There's still dependant types!
Reactive programming is functional programming for events. You can run but you can't hide.
"Functional" reactive programming is the functional programming for events. Reactive programming is actually pretty broad.
No, Functional Reactive Programming is not about events.
Well, some signals can be event streams if they aren't continuous behaviors. But event streaming systems like Rx provide none of the determinacy guarantees of FRP.
Isn't it? It's about event streams, right?
Nah, FRP is fundamentally about continuous time.
Imperative reactive programming via Esterel has been a thing since the 80s, you know.
one of the points of the article is wrong, fp isn't stateless at all it just tends to represent state in a different way - look at monads in haskell.
Well Haskell can still "stateless" in some sense, even if your program seems to take advantage of state. The State monad (and other monads), for example, just facilitate a bit of clever syntax to sneak computational context, which can include state, into the lambda calculus. The IO and STM monads (which deal with side effects) are a bit different, and can introduce true state into a program (with a bit of cleverness to make the existence of state mesh well with the lambda calculus).
No, it is indeed stateless. The fact that it represents state is proof of that. FP uses representations for things instead of the real things. For instance, OO purposefully causes changes in RAM to make things happen, where in FP changes in RAM are incidental and the programmer need not manage them. So it is stateless because you program indirectly by using these conceptual representations that are free of implementation.
Modulo IORef and similar.
No, even IORef is stateless.

You will know if there's state in a language if you can build a function that returns different results given the same values over time; IORef does not allow you to do that.

The bit that I was intending to modify was your statement that "in FP changes in RAM are incidental and the programmer need not manage them". With IORef, changes in RAM are not incidental, and the programmer is explicitly managing them. If IORef isn't direct enough for you, substitute Ptr. The fact that it happens in the runtime system does not necessarily mean that it is incidental or that it does not need to be managed by the programmer.

As to your broader claim here, I while I understand what you're getting at (and probably wouldn't have bothered responding, but for the above) I think it's most precise to say that IORef is an encoding of state. This does not have to mean that state gets updated by means of side effects.

"Don't be scared" rhetoric is generally used to promote arguments not primarily through logic, but by painting the audience as fearful luddites, scared of progress and the inevitable better future based on whatever the speaker is advocating. The goal is to encourage the listener to convert to the new religion or paradigm, or be considered backwards, behind the times, and ignorant or stubborn. It's an effective tactic because any push-back is pre-framed as evidence the listener is among the ignorant backwards people.
Hear, hear. I had exactly the same reaction. Maybe the author is pitching the article toward novice programmers and is genuinely trying to reassure. OTOH, such phrasing is far more often used as a pure rhetorical ploy.

Personally, since I work in storage which is where we have to store all the "state" that the FP folks have defined into Somebody Else's Problem, I have little tolerance for that particular flavor of the month.

You seem to be confusing state with data. Storage is supposed to store data, not state.

Still, even if you store your internal state, that's hardly what functional programming means when opposing state.

That kind of response starts to sound like "no true Scotsman" pretty darn quick. How do you distinguish between state that is on the stack (which FP still has), state that is on the heap, or state that is on external storage? Are those the same distinctions/evasions that I'd get from the next three FP advocates I asked? I keep hearing about how FP saves us from all that evil mutable state, but every time I look at a program written in a functional language I see plenty of mutable state. Half of it is entangled with control flow, which I do not see as an unalloyed win. When that doesn't suffice, those same programs often resort to externalizing their state (e.g. into a database), often incurring a significant and unnecessary performance penalty just so they can give it a name and treat it as something outside of their program. Aristocrats never like to get their hands dirty, and that's exactly how most FP advocacy comes across. "Let them eat monads." Can't wait for the guillotine.
There are answers to those questions but they do not help you to understand functional programming as a practice. Because you're asking the equivalent of "But how does OOP distinguish stack from heap from storage?" and it's not a practical question.

The answer is something along the lines of: the compiler is usually very different from most, it uses graph reduction and rewriting techniques, garbage collectors optimized for block reusage for all the heap churn that all those function frames would generate if implemented naively (that's why recursion ends up being as fast as a loop); this is my cursory understanding of it. But yes, I would suggest Lisp in Small Pieces and Appel's compiler books.

For practical purposes what is important is that FP is inviting you to program in this world where it is not important to talk about how those things end up getting implemented on the machine. In fact it lets you quickly escape that world whenever possible by allowing you to alias the String type to FirstName for instance; so you can talk about the problem you're trying to solve, and not the computer that will run it. That's the point of declarative programming and FP is declarative.

It's the same in Prolog, I'd think. I'm sure they don't care about how these things are actually being created and destroyed on the machine; the power of Prolog's declarative style is precisely that it allows you to never talk about the machine if you don't want to. That ends up being more general than the usual abstractions of the day that still model themselves after the turing machine.

There's no state to speak of. FP builds the blueprint of the program (one level removed from the way OOP and procedural see things) and gives it to the computer to run it. Now we make the execution order implicit (it's now implicitly threaded through the functions' caller-callee relationships) and we lose the ability to do state (because state depends on ordering things, which can be represented by the semicolon in curly-brace languages). We also lose the ability to do one thing after the other. But although it sounds like a bad thing, it's actually a really good thing, because apparently most bugs live inside semicolons.

> That's the point of declarative programming and FP is declarative.

Declarative programming is a gradient. You can be exposed to a bunch of tedious details in FP and you can abstract over them in OOP. Saying FP is somehow magically declarative (as if that were a thing) leads to profound disappointment when complexity is inevitably encountered.

> It's the same in Prolog, I'd think.

"Cut" it out :) Also see taming space leaks in Haskell.

> There's no state to speak of. FP builds the blueprint of the program (one level removed from the way OOP and procedural see things) and gives it to the computer to run it.

The primary difference between pure FP and OOP is identity: there is no identity to speak of, things don't have names or addresses, they are just values whose equality is determined purely by structure. The inability to name things in FP is a huge drawback, because things actually have names in the real world.

State doesn't depend on ordering. It depends on time; imperative and even functional languages often convolute the two concepts, but not always (see real FRP, not fake FRP ala Rx). Functional languages eschew state are merely eschewing the notion of time, which makes equational reasoning hard; it takes something like FRP to bring time back into the fold by making it explicit. Still, its quite hard to program interactive apps in pure FP given the intrinsic aversion to time.

>Personally, since I work in storage which is where we have to store all the "state" that the FP folks have defined into Somebody Else's Problem, I have little tolerance for that particular flavor of the month.

You say this as if functional programmers somehow make your life harder... It's not like they're decrying the existence of persistent storage or trying to put you out of business :)

Also, functional programming isn't about offloading state, it's about removing it from programs altogether (in the sense of "state" being something that isn't explicitly passed through function arguments).

In FP, State is transformed into something more formalized, namely parameters passed to other functions. So no one in FP is punting on State, we simply don't need it to accomplish those same goals. It's different from saying "it's somebody else's problem", though it might sound like that because State is one problem FP does not have (so in that sense it is solely a turing-machine-programmers' problem).
I didn't get that impression from the title. There are people who are interested in fp but feel that they are not smart enough to comprehend it. They are not luddites by any stretch.
(comment deleted)
We are already dealing with a weak dynamic language - what else could really scare us? Perhaps introducing shared memory multithreading into core JS... Certainly not a bit of immutability and pure functions.
I think it's appropriate. A sentiment I hear an alarming amount is roughly "I want to program, not do math." when someone gets a suggestion to learn functional programming.

In reality, someone can take advantage of FP with absolutely no understanding of the mathematical underpinnings. I think that's what the author was trying to get across.

I think it's better to solve the "math is scary" attitude problem by first showing how it actually helps and is useful. FP gets a lot easier if you know some basic abstract math.
With more passionate FP advocates, there's no "people who are not interested in FP after having tried and not finding it useful for their intents and purposes", there are people who either :

- Don't understand it - Are afraid of it - Find it too complicated

If you're interested in FP in javascript, you should check out the relatively new Ramda.js [1]. It switches from the underscore style collection first call pattern to a collection last pattern and automatically curries its functions. The combination allows for easier function composition.

[1] https://github.com/CrossEye/ramda

More info on the topic:

http://fr.umio.us/why-ramda/

http://www.youtube.com/watch?v=m3svKOdZijA

https://speakerdeck.com/raganwald/javascript-combinators

To be honest, this looks more like an explanation of the concept of abstraction than of the concept of functional programming.
"Functional programming is the mustachioed hipster of programming paradigms."

Well, that's not helping. But I agree that devs should learn FP, and I now write in a much more functional style than I used to. I resisted for a long time, and that was a mistake.

> The literature relies on somewhat foreboding statements like “functions as first-class objects,” and “eliminating side effects.

I personally find this view a little overbearing.

In "lay programmer's" terms, functions as first-class objects can often come down to being able to pass functions to other functions or function composition. Most of us learned about function composition in Algebra 2... So that's pretty straightforward even at its worst.

Eliminating side effects is equally straightforward in that all it means is that any variables outside the scope of a given function are not changed by the function.

Function composition is very different from passing one function into another and the latter is definitely a concept that most Algebra 2 students would not be able to grok.
I knew I was making a strong assertion when I mentioned function composition. I'm grateful for your feedback, but perhaps you could elaborate? I don't see why they have to be "very different". I think you called me on a certain case, but an example like this demonstrates that they are not so "very different":

    def a(b):
        m = 0
        return b(m)
Or even:

    def a(b):
        m = b(1)
        return m+7
I don't see how these are not a form of function composition. I'm definitely no expert. But I'd really appreciate an elaboration so I can learn from any mistake I'm making.
Function composition is a single, special case of a function accepting functional arguments. You could define the compose operator as below:

    compose : (a -> b) -> (b -> c) -> (a -> c)
    a `compose` b = \x -> b(a(x))
However, there is a whole spectrum of additionally possible functions which can be built to accept functions as arguments. Here's a couple of example:

    partial3 : (Integer -> Integer -> Integer) -> (Integer -> Integer)
    partial3 f = f 3
    map : [a] -> (a -> b) -> [b]
    map [] f = []
    map (x:xs) f = (f x):(map xs f)
The first one takes a function and applies an argument, 3; the next one takes a list and a function and maps the list using the provided function. The idea of functions as arguments is a very powerful one, and from my understanding one with which people sometimes struggle.
One way to look at function composition is that it is a function to which you pass two functions and out comes a function:

  compose :: (b -> c) -> (a -> b) -> a -> c
  compose f g x = f (g x)
I'm not sure about the math curriculum in USA but in linear algebra you have functions that operate on functions.
Eliminating side effects is about a lot more than variable scope. One example would be reading from/writing to a file.
I think it's an accurate description. The OS code that reads a file involves changing variables outside function scope, eg pointer writes to memory mapped hardware. Simply forcing all variables to be final, all the way down, really does eliminate all side effects.
I am not so much scared by functional programming as I am annoyed by functional programmers. I feel that they often come over like mathematics professors that just fill the chalkboard with lightning speed and look at you like "seriously, how dare you ask this dumb question". They go ahead explaining to you how your language is crap for all its sideeffects, smugly explaining how their language doesn't have those and then explain to you how Simon Peyton Jones shoehorned them into haskell with a monad. Also you always appear to fail to understand monads and they always fail to explain in an understandable way (although they're not that hard to get).

Functional programming is great and feels right, I am amazed how knowledge of fp affects my js, C# and Python up to this day. But from a social perspective: Loose the smugness, and then we'll talk fp.

So you're unwilling to learn something potentially better because you're were offended by a (surely) tiny portion of its userbase? Lose the childish attitude. You owe it to your craft and users to keep learning and using better tools.
I think you just somewhat proved my point.
For what it's worth, I've asked plenty of dumb questions, and have found the Haskell community happy to answer them.
The reason you might be perceiving this is because once you're past the learning curve hump, the concepts become extremely obvious and the definitions stick to your head.

It takes me a few seconds to rewrite foldl from rote understanding: foldl f z (x:xs) = foldl f (f x) xs, because it takes a function, a 'zero', a list of head and tail, and calls itself again with the same function, a zero 'going up' (so accumulating) and a list 'going down' (being exhausted). So as you see this is an almost a visual way to understand it, which is natural to those that already are familiar with these abstractions, but is meaningless to those who don't.

If someone then says "how dare you ask that" then they have the wrong approach to things. Socrates asked every question and thought they were all valid. By asking the basics you learn the abstraction as if you're proving it to yourself, and that's more powerful.

So it boils down to familiarity, is what I'm trying to say. I usually come across as more passionate than arrogant about it. If you stick to it, it will make sense. It took me 4 years of Haskell exploration for it all to click really hard and now I'm trying to shorten other people's paths. In my opinion the gap is around mathematical abstractions, how they're created, what do they mean, how they actually relate to reality, and what work do they accomplish for us that we then don't need to do anymore.

> It takes me a few seconds to rewrite foldl from rote understanding: foldl f z (x:xs) = foldl f (f x) xs, because it takes a function, a 'zero', a list of head and tail, and calls itself again with the same function, a zero 'going up' (so accumulating) and a list 'going down' (being exhausted). So as you see this is an almost a visual way to understand it, which is natural to those that already are familiar with these abstractions, but is meaningless to those who don't.

I think this type of explanation falls into what the parent post is talking about. That is a technical accurate but very dry way to describe a construct that simply calls the function it's given for every element in a collection and returns the accumulation of the results. Granted you can dive into all sorts of discussions about what that _really_ means and what power it gives you, but save that for much later, it isn't introductory material.

An analogy to how functional programming often is taught would be if I tried to teach a new programming language by going over the BNF grammar first without context instead of starting with how the language is actually used in practice. It may be more 'technically correct' but it's a terribly ineffective way to learn for most people, even those who know and understand BNF. People are often more interested in what they can do or create with this new thing, not the nitty gritty details of how it works mathematically even if they intend to get into the details later.

You're absolutely right, but I wasn't trying to explain it, just showing how easy and natural it gets once you get past the hump. I was assuming the author already understands the benefits of FP but finds it difficult, and I was saying it definitely does get a lot easier.

Having said that, for an example of how I would actually explain some FP to someone, search this page for "DomainList" and I have an explanation there.

I agree with you 100%; my belief is that mathematics and its notation often gets introduced too early, before students have had a chance to build intuition.

That should be "foldl f z (x:xs) = foldl f (f z x) xs" because the function f takes two arguments (the whole thing boils down to "(((z `f` x_1) `f` x_2) ... `f` x_n)").
Thank you for the correction!
Isn't this the blub paradox all over again? I hear the exact same thing about every other programmer in the world from some PHP/Node programmers who only know that language and insist is the best choice for every possible project.

I'm not saying that actual smugness doesn't exist, but almost all the time it's actually just projection from someone who's used to being in the know now struggling to understand something new.

Remember when you explained to someone using a language lower on the power spectrum (VB or PHP maybe) why they should use all the cool stuff that you can do in Ruby or Python, how much more productive they would be, how much more fun it was to work in? You thought you were helping them and showing your excitement, you wish someone had told you this a long time ago!

Unless you are a world class educator they were confused by some parts, threatened that their investment in their current language was losing value and worried they might not be able to function in this new world and therefore protected their ego by seeing you as smug.

First: No, it's not just the blub paradox. The Haskell (and also Lisp) posters, even on HN, seem to have a higher proportion of smug self-congratulatory explanations-that-don't-really-explains-but-let-the-explainer-bask-in-his-or-her-sense-of-superiority posts than other languages do.

Second: The blub paradox (at least as posed by Paul Graham) is wrong. To see why, just look at Lisp and Haskell. Users of both languages are sure that they're at the top of the power curve. They're sure that when they look at the other language, they're looking down, and they can tell you precisely why ("it doesn't even have macros" and "it doesn't even have a decent type system", respectively). But they can't both be looking down at each other - unless languages cannot actually all be ordered along a single axis called "power".

In fact, you have to ask "power for what"? Once you do, you might start to see language power more as a tree than as a single line. You can really only compare power between languages on the same branch. Pick the language that gets you the furthest in the direction that your problem lies.

I think you'll find that language 'power' is actually better modeled by a bounded semilattice which happens to be an idempotent commutative monoid. It's all very simple, you see...
But Lisp and Haskell are arguably the best in their categories, namely programming in lists and programming in functions.

Those two abstractions are several times more powerful than the overly constrained abstractions imposed by OOP. The power of lisp is that it is homoiconic, and it asks what it means to write programs that write programs. The power of Haskell is that it is algebraic, and it asks what it means to execute a mathematical proof.

Both therefore have the power to really abstract away from things in any way you want. They do not constrain the programmer with an abstraction that is too high and not composable, as are (mathematical) functions and macros (which are composable with hygiene). The prevailing paradigm offers poor substitutes for composability and can simulate very poorly abstractions that are lower than Objects (thereby needing design patterns to make up for starting so high in the abstraction tower).

But see, now you're just replacing a one-axis view with a two-axis view. I could equally well have said that a Fortran programmer looks down on both Haskell and Lisp because they don't even have a decent matrix package. Or that a C++ programmer looks down on them because they don't let you really control the details. (Despite the power of abstraction, there are plenty of situations where real control is at least as important. What do I mean by real control? Size of a variable. The ability to manipulate individual bits. Control of individual memory allocations. Et cetera.)

Program power is not one dimensional, and not two dimensional. There are more "best" languages than Lisp and Haskell. There are more categories that matter than just programming in lists and programming in functions.

Yes of course, I did not mean to imply there are only those two (that would be weird for the two to be "lists" and "functions" out of all things).

What I mean is the current mainstream languages are not the best of any category, like Haskell is up there for functional programming and lisp is up there for s-expressions. C++ is not the best for controlling details (see Rust, D, etc), in fact it's quite a broken language. I doubt Fortran is better at matrices than APL (in fact APL is arguably the best in the matrix category) and so on.

That's the problem. These languages that are best in their categories are many many times better than the mainstream languages of today, which aren't the best in any category they represent.

> seem to have a higher proportion of smug self-congratulatory explanations-that-don't-really-explains-but-let-the-explainer-bask-in-his-or-her-sense-of-superiority posts than other languages do

That's my point. It absolutely doesn't seem that way to me, because I know Haskell and Lisp.

I'm also personal friends with PHP developers who I used to work with and they have exactly the same attitude towards anyone talking about Ruby or Python or literally anything else.

They are angry at the bullshit that these smug language jerks keep throwing their way about how "bad" their perfectly good language is.

Some Haskell proponents tend to be a bit annoying, Yes. But they are only a (not so big) subset on Haskell users.

Then, there are other FP languages, which you can easily learn in a few days/weeks, very nice and natural, e.g. OCaml, or SML, or maybe Scheme(Clojure?).

Program in Java for a while and you get immune to smugness. "Java is so ..." Yeah, yeah, bring it on. While you do that I'll solve the problem and can go home.

Programming in PHP must have a similar effect.

I'm not scared. It just does not make sense to me.

Just because I speak French and English fluently does not mean I can speak Japanese. My wife is Japanese, and speaks it fluently. We've been together 19 years. I've tried to learn. it just does not make sense. It would be nice, because I have relatives there, and I could go live there, legally, but it just doesn't make sense in my brain.

What can I do?

Functional programming is the same. Every couple of years I take a look, and I recoil in confusion.

It also doesn't help that people make fun of me for seeming dumb.

What worked for me was taking functional programming concepts, and applying them in languages where I was already comfortable. That way, I wasn't learning a new syntax and a new programming paradigm at the same time.
Which concepts did you find the most applicable? For me it is applying functions to collections in different ways, eg. map, fold, etc.
Can you say a few things about the parts that don't make sense to you? That would help me identify what beginners struggle with, as I'm developing tutorials for that reason.
It's simple: the very concept of the program.

I want to write a program that reads one text file, and outputs another.

Let's say the first file contains a list of emails, one email per line.

The second file should contain the list of email domains in the first file, one line per email domain.

Procedurally, I would simply declare a list, read the file of emails, line by line, split each one on the @ symbol, and if the part on the right was not already on the list, I would add it to the list.

At the end, I would write the list to a new file, one line per item in the list.

How would one do that using functional programming?

By the way, for those who think programs like that are too simplistic, and not a good showcase for all the FP goodness, maybe that's part of the problem. I work in the corporation, and I we do simple things.
Ok so here's one way to solve that problem in FP:

We define what we have first (after all this is mathematics):

EmailList = a list of Email

Email = a string that goes "something" then "@" then "domain"

Now what we want:

DomainList, no duplicates.

Ok so no we're set up. We want a function that takes us from an EmailList to DomainList, as defined above. There are no objects so we simply say the relationships between things. I will introduce the tools we need:

map = takes a function and a list and applies that function to every element of the list, returning a new list.

We use map over EmailList with the function extractDomain which we will write later (it's trivial to do without mutation so I won't show here).

So now we have a list of domains, but there might be duplicates.

So now I have DomainList(with duplicates) and I want DomainList(without duplicates), so I want a function from DomainList to DomainList.

Well, what's the difference between what I have and what I want? What I have has too much, and I want less (ie. the list I have has too many domains (duplicates) and I want fewer (unique)).

filter = takes a list and a function. That function it takes, that you will supply to filter, we'll call f. Function f takes one list element (so filter will call f for every element) and it must return a boolean to mean whether that element should go into the new list, or if it should not go into the list.

So we could use filter to go from a DomainList with too much, to a DomainList with fewer things. However each time our function f is called, we don't know if this function has already been called before with that same domain; and because we don't have state to change a variable to keep a tally, it seems impossible.

So let's approach the problem in a different way. One thing we could do is sort the list, then group adjacent similar items, then map over the list taking the first element of each item. But we won't do that.

We need something more generic than filter. Let's look at fold.

fold = takes a list, a function f, and a starting point z (that we call 'zero'). For each element of the list, it calls your supplied function f with the current element and the zero; but then, it would be useless because it would be the same zero all the time. So what fold does, to be useful, is that whatever the result of calling your function f with a list item and the zero is, THAT value (so the value the call to yourFunctionF(list_item, zero) returns) will be the next 'zero'. So now we have a little machine called fold that can thread something we have through its inner mechanism and play along with us.

Using that we can say the zero is the list of elements we've seen so far.

So now our DomainList -> DomainList function will be a fold. That fold will take a the DomainList we have, and a zero that will be the empty list (to start with!) and a function f. That function f will simply check if the item is inside zero. If it is, then simply return zero (because that will be the zero for the next call to f!); if it's not, then return a new zero with that item added to it.

Now we've achieved the goal without any mutation (ie. without us talking/concerning ourselves with any mutation. of course there's lots of mutation going on in RAM but the point is not to eliminate that!)

Does that help?

Sadly, no.

It's like I asked for a tuna sandwich and you explained how to build the engine of the fishing boat to go catch the tuna off the coast of Baja.

I feel like I need a masters in computer science to understand what you wrote.

let me see:

# Python:

domains = []

emails = open("file_with_emails.txt","r").readlines()

for email in emails:

____domain = email.split("@")[1]

____if domain not in domains:

________domains.append(domain)

out = open("file_with_domains.txt","w")

for domain in domains:

____out.write(domain)

out.close()

input:

joe@msn.com

mike@aol.com

tim@aol.com

sam@hotmail.com

output:

msn.com

aol.com

hotmail.com

Simple no?

That's actually not simple at all because it assumes an implementation, that is, a machine with RAM that changes.

For instance, what does = mean between domain and email.split ? Who is equal to who? That question is a question that students sometimes have. You have to explain that Time is implicit in the code, and that an equal sign works by making the thing on the left equal to what the thing on the right was just before this line. So now there's a concept of Time and that's how things start to get very complicated, because it is implicit in your code.

Then, what does ".append" mean? I mean, you call append, but now you don't assign the result to any variable? And what would it mean if you said domains = domains.append(domain), then we really need to have a talk about the hidden Time.

So it only seems simple because it's familiar to you; so in fact I believe it's not simple at all.

But this I believe it's simple, and my fishing boat engine above would've benefited from this:

https://www.fpcomplete.com/tutorial-preview/4217/4k7kq6jGUE

If you look at that code (you can also click play to run it) and then look at the explanation again, could you please tell me what doesn't seem simple?

Edit: let's not forget your Python example uses a map, disguised as a for loop. That for is a keyword, but backstage it's just map where email is the element, emails is the list, and everything you write inside that scope is the function that gets passed the element each time (except in your case the function (i.e. the loop body) can also see the whole list, which is prone to bugs).

Thanks. I looked at the code you provided. it makes still very little sense to me.

You're right though that my using python language features like lists and file open and loops is like function, but I understand those, so I can use them.

Also, a machine with RAM that changes. Well, yes, and files that change too. I'm completely ok with that.
"Edit: let's not forget your Python example uses a map, disguised as a for loop"

Mmmm, really? A map? Like Google maps? Like a paper map? unfamiliar terminology.

Seriously, I learned how to use for loops in 1984. Been using them ever since. Maybe that's the problem. Too old to learn new tricks.

It's not unfamiliar, you got it right, the map I'm talking about is exactly like that maps you described, in that they both contain directions (all the possible origin-destination relationships between all the places) and you can draw arrows from one place to another (thereby defining a "route" that represents the act of starting to walk from point A and arriving at point B). If you cared to learn you would benefit a lot from it.

Oh, but the moral of the story is that because the computer understands those maps, then I can simply tell it what I have (point A, origin) and what I want (point B, destination) without telling it how to do it. Then the computer goes and writes that code for me! The same code you're now still writing manually. Because you don't want to learn a new "trick". :)

> Mmmm, really? A map? Like Google maps? Like a paper map? unfamiliar terminology.

No, a map like the Python built-in function "map". Though, really, the for loop in your Python sample isn't a map done as a for loop, its a reduce done as a for loop. (Again, like the Python built-in "reduce".)

You can learn these tricks, but it's ok if you don't want to, as long as you don't mind that many of us like doing things this way, and recognize that you may well run into these concepts in other peoples' code at some point.
For completeness, I should add that I don't know what "reduce" means in this context.
https://docs.python.org/2/library/functions.html#reduce

Reduce (also frequently called "fold") turns a list of items into a single item (of maybe the same type or maybe a different type). It does this based on a function that tells you how to update that single item as it looks at each list item in turn.

For instance:

    def fn(a,b):
        return str(b) + a + str(b)

    reduce(fn, [1,2,3], "0")
That says:

    Start with "0" (we call this the accumulator value)
    Read the first element of the list (1)
    Pass "0" and 1 to fn
        Turn 1 into "1"
        Stick it on the front and back of "0"
        Return "101".
    Use "101" as our new accumulator value.

    Read the next element of the list (2)
    Pass "101" and 2 to fn
        Turn 2 into "2"
        Stick it on the front and back of "101"
        Return "21012".
    Use "21012" as our new accumulator value.

    Read the next element of the list (3)
    Pass "21012" and 3 to fn
        Turn 3 into "3"
        Stick it on the front and back of "21012"
        Return "3210123".
    Use "3210123" as our new accumulator value.

    Notice that we've reached the end of the list, return our latest accumulator value.
I think the main difficulty in converting your example to a functional style is how to express the idea of a for loop which builds up a result as it goes along, without using a mutable variable to store this result. Here's an attempt to explain how that the ideas in the parent are helpful in solving that problem.

You can think of the body of a for loop in python as the body of a function. In that case, a for loop is like the built-in map function, which applies that function to every element of the list. So convert your for loop into:

  def body(email):
    domain = email.split("@")[1]
    if domain not in domains:
        domains.append(domain)
And instead of a for loop we can use the standard function, 'map'.

  domains = []
  map(body, emails)
Now, obviously the data here isn't immutable. But what if, instead of changing 'domains' every time the function is called, we passed in the current value of 'domains', and returned a changed value, like so:

  def body(domains, email):
    domain = email.split("@")[1]
    if domain not in domains:
        return domains + [domain]
    else:
      return domains
Then, we could apply this function to each element of the list in turn, passing the current list of domains at each stage to the body. This is the standard function 'reduce' in Python (which the parent calls by its other common name, 'fold'):

  domains = reduce(body, emails, [])
   
You could think of 'reduce' in python being implemented like:

  def reduce(func, l, currentResult):
    for item in list:
      currentResult = func(currentResult, item)
    return currentResult
    
Or, it could also be implemented recursively, which means it wouldn't need to use mutation internally:

  def reduce(func, l, currentValue):
    if len(l) == 0:
      return currentValue
    else:
      return reduce(func, l[1:], func(currentValue, l[0]))
      
Many for loops that build up a result can be expressed instead using 'reduce', so reduce is one of the most important building blocks of functional programming.
Except you'd never write it like that in idiomatic Python. Your reduce's repeated list copying/appending/searching kills performance. Instead you would do something like this:

domains = set( email.split('@')[-1] for email in open(path) )

He's pretending python compiles to something sane. FP spoils you with great compilers.
This is a great explanation and I hope it will help your parent commenter realize that, while different and not trivial, this stuff really isn't dark magic and really doesn't require a master's degree level of study to grok.
I think my comment is going to be that, from reading the parent, I understand that functional programming moves the code to the data and not the data through the code.

I can't do the mental gymnastics to think about the code that way.

When I think about the purpose of the code, the result of the code, before the code has been written, I do this by "compiling and running" in my head. Of course, there's no actual compiling. There's just thinking.

When I reason about the code, either code I'm going to write, code I wrote, or code other people wrote, I execute the code in my head.

I'm currently building a web-scraping system, to look information on partner web site. I'm using python 3, selenium, pyodbc to sql server. I'm also developing the same in C# (3.5) using the webbrowser component in systems.forms. It's got a lot of moving parts, lots of languages involved. I read the documentation for the tools, then I assemble in my head, essentially "running the code" in my head, and then, once all the edge cases are dealt with (are there iframes, popups, ajax async calls, flash and pdfs) then I design on paper, with boxes, and then, later, later, if it all works in my head, then I code it.

Can't do that if I can't reason in my head about what the program is going to do, exactly.

Functional Programming, for me, does not fit in my mental model, so I can't use it, regardless of actual implementation.

This is partially because you're shoe horning functional ideas into an imperative language. When you have no choice but to use maps and folds you quickly build up mental models of how to do what you normally do with for loops: accumulate (fold), transform (map), or generate (zipWith).
Here is some scala code does the same thing

FileIterator("input.txt").map{_.split('@').last}.distinct.toFile("output.txt")

Lets compare it to the Python version. It looks much shorter, but many constructs are similar to what you wrote and it can be argued that the Scala code is merely a rearrangement of the tokens in the Python code. So let us explicitly call out the tokens/items I did not use in the Scala code, but you did in the Python code.

domains = [] domains.append(domain) out.close() infile.close() // I think you missed closing the input file, a handle leak bug

The map method encapsulates the construction of the output list and the FileIterator and toFile methods encapsulate the opening and closing of files. In particular, the Scala code hjas no handle leak bug. What functional languages are really good at is abstraction. map has abstracted away iteration and construction of an output list, while the file handling methods abstract away resource usage. As programs get bigger and more complex, this power of abstraction becomes increasingly powerful.

(Note that FileIterator and toFile are constructs that I wrote for myself a long time ago, differing constructs are available in the Scala std lib)

In functional style, but still in Python (given that python method calls are equivalent to function calls with an additional first argument of the receiver object, and indexing is equivalent to a method call) -- this uses mutable objects, but doesn't actually rely on mutating them:

  addEmail = lambda |doms,email| doms.union([email.split("@")[1]])
  domains = reduce(set(),addEmail,open("file_with_emails.txt","r")))
  open("file_with_domains.txt","w").writelines(domains)
(Loses order because of use of set for deduplication; you could do it with a list and keep order, but then addEmail becomes more complex, and I don't think its strictly necessary to illustrate the general process. Also, its not clear to me that order-preservation is really part of the intended function here or just an artifact of the implementation. Note that this could be a one-liner, its just split up into three lines with two as assignments for readability.)

And, more how I'd really write it in Python (which is also functional, though perhaps less obviously so, and leverages Python set comprehensions):

  domains = { email.split("@")[1] for email in open("file_with_emails.txt","r") }
  open("file_with_domains.txt","w").writelines(domains)
> this uses mutable objects, but doesn't actually rely on mutating them

That is actually a really good compromise and a style I tend to stick to when coding Ruby/Rails.

That right there, that's beautiful. I love list comprehensions.
Using clojure:

  (ns my.namespace
    (:require [clojure.string :as str]))

  (->> (str/split (slurp "/home/deadghost/fake-emails") #"\n")
       (map #(second (str/split % #"@")))
       set
       (str/join "\n")
       (spit "/home/deadghost/new-file"))
Read entire file, split by newlines into a vector, split each element by @ and take the second part of each split, convert to set to remove duplicates, join set elements with newline and spit to new file.

Clear as mud?

Wow, that looks like what was trying to come out of Perl. Very cool snippet!
One way in P6:

    say ~ m/ '@' (.*) / for lines
Ignoring the regex for now (the `/.../` bit), this reads as say (`say`) the string (`~`) that matches (`m`) for each of (`for`) the lines (`lines`).

Note that lists (`lines` returns a list) are lazy by default in P6. So the above code will start producing results immediately and continue without exhausting RAM even if the input is infinite.

The `/ ... /` bit above is a "regex". It means match the symbol '@' and return all the following characters of that line. P6 regexes are far more powerful than P5 regexes. P6 regexes can work together to comprise arbitrarily complex parsing grammars. The main P6 compiler, Rakudo, parses input source code using a P6 grammar.

There are of course many more ways:

     say lines.map: { m/ '@' /; $/.postmatch ~ "\n" }
In this instance `lines` is treated as an "object" (a `List` in this case) on which the `map` "method" is called. `map` calls the block of code (the `{...}` bit) against each element in its invocant, ie against each line. The block of code matches a '@' and returns whatever follows it ("postmatch") with a newline appended. (`$/` refers to "the match object"; it's one of three symbolically named variables in P6 as against the dozens in Perl 5.)
Functional programming is a hard compromise. Almost no mutation or side effects, every part of the system is encoded/encapsulated into a function entity. That's it.

The fun part is that many languages end up aiming for it. Good multithreaded/reentrant code is almost functional. I just saw a ruby rant asking to avoid mutation as much as possible. Same goes for PHP PSR-x ...

Functional programming suffers from acute mathematitis, the people behind it are fond of iterating too fast around their newfound concepts and it end up as cryptic as possible.

I have to say, it makes a big difference who you learn from (or perhaps how you learn). In college I had several courses where we programmed in Scheme and I hated every minute of them because I felt like Scheme made things harder and uglier with no tangible benefit.

Then I had a class where we learned Haskell with one of the best professors in the department. He basically taught me all of the great things about functional programming and now I try to use concepts from that class in whatever language I'm programming in.

Learning functional programming is paradigm-shifting and gives you a much broader set of tools to work with, but if it's not taught well it won't help you.

If you're interested in learning FP but struggling and would like to beta test didactic material for beginners, I urge you to get in touch with me. I'm making a series of videos explaining the concepts and abstractions of functional programming in a socratic way and from scratch (so not assuming even that numbers exist). I'm 'uploaded' at google's email service.
Maybe its me being thick, but I can't honestly wrap my head around what the fundamental good thing in functional programming is. When ever I work in a functional language I just find myself missing objects.

OOP just feel more eloquent to look at personally.

        Struct.function(arg1, arg2, arg3); 
feels more atheistically pleasing to my mental model of programming then

        function(struct, arg1, arg2, arg3); 
This means more complex function definitions. While I can define multiple .close(); functions, that only operate in relation to their connection type I.E.: tcpConnection, serialConnection can each call their own close(). In FP I have to actually state either:

     fn close<T>(&:<T>){
           if(<T>::isType(tcpConnection)){
                tcpConnectionClose(<T>);
           }else if(<T>::isType(serialConnection)){
                serialConnectionClose(<T>);
           }else{
               //throw exception
           }
    } 
Or I just make primitive calls to each connection type

    tcpConnectionClose(*);
This just doesn't stroke me the right way. It feels like bulky, over necessary coding when you take into account objects exist. I'm interested in FP, I feel it has to offer something for people to jump on it, but I just don't get it.
fwiw, clojure supports polymorphism.

http://clojure.org/multimethods

I encountered this recently but I'll try to give a (bad) explanation.

You'll call `function(thing, arg1, arg2, arg3);`. Another function will run on the arguments and return a dispatch value. For example, it will check what `thing` is and return `struct`. The `struct` version of that function is then run on the args and gives you your value.

In this way you can define several `close` functions based on dispatch value instead of one monolith nested if/else `close`.

I'm on the opposite side of the fence, OOP has never ever appealed to me. "Why would anyone want to use this crazy mess?" kind of thing. I'm sure I can learn to appreciate it with time but it's not a native paradigm to my mind.

I only have experience with lisps when it comes to functional programming but the reason I enjoy it is that it's simple. You have functions and you have data. Functions transform data to more data and the two aren't tightly bound. If you pass a function a value it will always return the same result if you pass it the same value.

THE clojure video: http://www.infoq.com/presentations/Simple-Made-Easy (I don't think he even says the word clojure in the entire hour presentation.)

Not sure what language that is supposed to be, but in all the functional languages I've used, you would specify that T implements close and write an implementation for a TcpConnection and a SerialConnection, letting the compiler figure out which one to call when and obviating the runtime exception entirely.
Let me stress your point to the parent:

> letting the compiler figure out which one to call when and obviating the runtime exception entirely.

That is how FP eliminates a whole category of bugs. You have to have "experience", which some love to boast, to know that it's better to have an else clause there and throw an exception.

If you think that's basic knowledge you'd be surprised with how little do "experienced" programmers that get paid very handsomely actually know; I've met 30-year of experience programmers that do not believe that the final-else-then-exception pattern is a good idea, because they think "crashing the program is never good". So what ends up happening is of course the program ends up crashing anyway (supposing you have passed an invalid connection in that case) but now the stack trace is potentially much deeper than it needs to be, and therefore misleading. All stack traces lose their value collectively because now we can't tell the good ones from the bad. That can make debugging harder than it needs to be.

But instead in FP, because it's a fundamentally different way to program, that situation doesn't even arise in the first place, obviating a solution. In FP things are modeled differently from OO, it's not just "procedures without an object jacket". The advantage here is the compiler verifies the "object" selection for you (which "class"/type of connection) and can provide some mathematical guarantees. Those guarantees are what metonymically "eliminate bugs".

So we can rely less on the programmer and more on the compiler. You know, like it's meant to be. After all we are sitting in front of a computer, so we should just let it do its thing without telling it how. Turns out autonomy in coming up with a solution is good both for the programmer and for the computer (and then again for the programmer).

Thanks for the interesting expansion. This technique isn't actually unique to functional programming languages, but rather languages with good type systems and compilers. It just so happens that a lot of functional programming languages have that.
That's great if every time you said "FP" you replace it with "procedural programming" because that's what you just illustrated.

FP has nothing to do with procedural programming at all, it is fundamentally different and diametrically opposed to both procedural and object oriented programming, which are then both in the same group away from FP.

You should look up some tutorials on FP if you want to understand what it means in practice.