Live data from Hacker News

Pissed off about functional programming (2005)

perlmonks.org

51–60 of 65 posts

Re: Pissed off about functional programming (2005)

#51
Maybe I'm missing something, but points 3 and 4 don't make much sense to me. Could someone tell me why I'm wrong here?

In myth 3 he seems to mix words of description and the words describing logical equivalence. "even though equals('three',3) is true, length('three') does not equal length(3)" - while this is correct, it only seems to say anything about the myth because of the function name. Try this instead "even though foobar('three',3) is true, length('three') does not equal length(3)" - does this really prove anything? You could never substitute "three" with 3 at any point in the first place.

Specifically he hid a "$lut{ $str }" in equals(). It's not that 3 can be substituted with "three" - it can be substituted with "$lut{"three"}".

In myth 4 he seems to play a similar trick of talking about the variable names. Sure, perl allows you to create a new block with a new variable of the same name. I'm not sure what does that have to do with functional programming as a whole. It's just the language implementation that allowed you to play this trick - referring to a new thing by a name you used before.

Re: Pissed off about functional programming (2005)

#52

"Even a side-effecting function call in C has a well-defined "value" as a state transformer that maps states to pairs of states and values (the so-called "monad" in functional programmers' terminology). The reluctance of functional programmers to call such languages "referentially transparent" merely implies that they are reluctant to admit such complex mathematical/conceptual objects as "values". On the other hand,…

The value in a monad is that it allows you to work with pure data transformations (functions), which can then be reused, composed, tested, used in isolation and then deal with the side-effect later when you really need it. If you think about the monadic type as a container, it allows you to apply transformations while not pulling the values out of that container (think data-structures). If you think about a monad as a context, it allows you to apply transformations while keeping the context (think Future/Promise).

The call of a side-effecting function of course it has a well-defined value and I don't think there's anybody knowledgeable enough in FP to deny that. Problem is - there's data missing in the representation of side-effecting operations. For example, take a statement like this:

    x = x + y
As a matter of fact, this makes no sense mathematically, because there's data missing from the above representation, as it really means this (where i, j and k are moments in time):

   x(i) = x(i - j) + y(i - k)
So time is actually an implicit parameter here that you've got no control over. The above representation makes even more sense when studying the architecture of CPUs. And as a side-effect, the old value of "x" gets lost and so if anybody else holds a reference to "x", then that reference will point to an entirely different value, an event that can happen at arbitrary points in the future, so you could say that the whole world changes after an operation like that. Since concurrency is often brought into the picture, I think it's fairly easy to see how this can create problems in terms of our capability to reason about the logic we read or write.

Also, if we think about side-effecting components (say, mutable objects), the functional behavior of such a component ends up depending on its history, history that's in no way explicit. Variables are buckets or places. Immutable values are facts that can be compared. E.g. 2 references to mutable things cannot be considered equal, unless they point to exactly the same memory location, otherwise equality (as defined in the programming languages we are using) is broken and a constant source of gotchas.

That link you posted argues that the way we are using the term "referential transparency" today for programming languages is different from its original meaning, but I'm arguing that the original meaning is not relevant for programming languages. There's no point in arguing that a side-effecting function call in C does have a well-defined "value". Yes, the output of a side-effecting function can be considered a value, but you can't treat it as a value.

Of course, we can always argue semantics.

Re: Pissed off about functional programming (2005)

#53
post #18
post #5

Earlier quoted context omitted.

"I kind of feel like he was missing out on what pure FP can and does achieve." So what exactly was he missing? When I was programming in FP languages, the process didn't seem all that different from other languages, except generally more cumbersome.

He simply made a lot of assertions that certain styles of programming are impossible. That assertion feels great if you (a) don't actually know the techniques of achieving some of the benefits outlined and (b) try to program in a language that doesn't support them as well in a style which doesn't work well with them. In some sense, despite the author's assertion that FP is mind-expanding and a great learning experien…

Well, at least he gave some concrete examples. I was hoping for some concrete examples of what he was missing from you, because I am curious.

My own exposure didn't show me that much that was new/different in ways that were practically useful (and no, it wasn't writing FORTRAN or BASIC or Pascal in a different language).

Mind you, I have, for example used (mostly) immutable data structures and generally write in what many would probably consider a somewhat functional style. I also like HO mechanisms, but all of these are not at all exclusive to FP languages.

So as I wrote, I am looking for concrete examples.

Re: Pissed off about functional programming (2005)

#54
post #46
post #40

Earlier quoted context omitted.

I think this is somewhat misleading. It's easy to give a semantics for a toy C-like imperative language. As far as I know, a semantics that is mostly faithful to the C standard becomes extremely complicated and requires modeling e.g. code layout in memory. The equational reasoning you get from that kind of semantics is terribly weak: essentially, only terms that have the same byte-for-byte effect on memory are really…

I think we're in agreement on content if not tone... and, honestly, I think your tone is probably closer to reasonable while mine was a bit facetious. I said it that way to emphasize the argument that existence of a referentially transparent semantics isn't enough, though. You need extant and useful .

Very true. In fact, that's true of every language feature. Way too often, people waste time bikeshedding about how language X can do Y. But if it can't usefully do it, who cares?

Re: Pissed off about functional programming (2005)

#55
post #13
post #5

Earlier quoted context omitted.

"I kind of feel like he was missing out on what pure FP can and does achieve." So what exactly was he missing? When I was programming in FP languages, the process didn't seem all that different from other languages, except generally more cumbersome.

> except generally more cumbersome you're doing it wrong.

No true Scotsman?

FP is not actually the One Right Answer for every problem.

Re: Pissed off about functional programming (2005)

#56

To be fair, I've done a fair bit of Clojure and Ruby in my short career, and while it's technically possible to write Ruby code in a Clojure-esque style, it's very ugly and Ruby does not make it very easy. When you write in Ruby, use classes or you will have a headache on your hands!

After years of trying to force things in various languages and frameworks (like MVC ala Struts on top of WebForms), I've learned this simple thought: don't kick against the bricks; they don't care and you'll get hurt. If you're in Ruby, program idiomatic Ruby. If you're in Python, program idiomatic Python. If you're in PHP, get a new language.

And if you're in Ruby or Python or whatever, and the language is a bad fit for the problem, use a different language for that problem.

Re: Pissed off about functional programming (2005)

#57
post #18

Earlier quoted context omitted.

He simply made a lot of assertions that certain styles of programming are impossible. That assertion feels great if you (a) don't actually know the techniques of achieving some of the benefits outlined and (b) try to program in a language that doesn't support them as well in a style which doesn't work well with them. In some sense, despite the author's assertion that FP is mind-expanding and a great learning experien…

Well, at least he gave some concrete examples. I was hoping for some concrete examples of what he was missing from you, because I am curious. My own exposure didn't show me that much that was new/different in ways that were practically useful (and no, it wasn't writing FORTRAN or BASIC or Pascal in a different language). Mind you, I have, for example used (mostly) immutable data structures and generally write in what…

Myth 1: if you restrict variables to be references alone and no longer mutable slots then this argument vanishes. Additionally, these kinds of computations do not need to take forever. Case in point:

    fix f = f (fix f)  -- ought to take forever, right?
    fact' rec n = if n == 0 then 1 else n * rec (n-1)
Here `fix` appears to set up an infinite computation, but applying it as `fix fact'` produces a terminating function, the factorial.

Finally, simultaneity in LC doesn't much hold you back from using concurrency because you have two avenues: (1) model your concurrency abstractly within LC and then execute it specially and (2) have concurrency applied implicitly with optional hinting.

Both of these are a little non-obvious, but extremely workable.

Myth 2: Summarized as: Turing Completeness holds, perhaps? It seems to be attacking a certain strawman, but in doing so depends a lot upon some definition of functional as being a totally different form of computation. It's hard to even concretely respond to this abstract notion, but I'll try with two points

1. LC and TMs are not equivalent on higher order computation. While any function Int -> Int can be equally represented in each, functions like (In -> Int) -> Int are different. In LC you cannot examine the input function (with tradeoffs and benefits) and in TM you can (with tradeoffs and benefits). That's totally theoretical, though since you can model higher order computation as a function Int -> Int for most practical purposes.

2. Turing Completeness is not an end goal. Some languages today even challenge whether you want TC to hold all of the time (or only sometimes) and push right up the border of TC from below. I think they provide example that taking TC as your ultimate arbiter of language comparison is shortsighted.

Myth 3: Referentially transparent is not a property of a language or of a (poorly defined) style of language. It's a property of an analysis of a language, the language's definition/semantics/statics. I've discussed this on this page in another comment concerning C.

The short of it is, however, that RT depends upon what you're willing to analyze as a frame of reference and for any language you can pick choices of frames which provide RT or those which break it.

The author's choice of mechanism to demonstrate RT breakage is pretending like variables in Perl are references and then showing that mutability and dynamic scoping breaks them. Then he even tries to pretend like universal use of dictionaries models reference and breaks that.

These are strawman arguments, deliberately pushing models outside of their theoretical value and then showing that they no longer have theoretical value.

Ultimately, this culminates in an unjustified argument that "referential transparency isn't all that desirable" due to "incredibly tight constraints" being untenable.

As a concrete counterexample, I give you pretty much the semantics of Haskell which has a ton of practical code written in it and referentially transparent variable semantics by default. It goes much further than his ideas do and achieves it in such a way where the programmer does not have the ability to break the model.

(For instance, if Haskell let you modify its own running memory and fiddle around with arguments on the stack then you'd certainly be able to use those facilities to break RT... but you can't.)

Myth 4: This is total strawman—he asserts that FP does not allow "assignment", defines assignment as he chooses, and then shows that his own model of assignment can be modeled without assignment. He does this by embedding an imperative language in a "functional" style in Perl and then observing the effects of running that embedded language.

Then he shows... something utterly unrelated to referential transparency but claims that it's the same. Since you asked for concrete, here's his example in Haskell

    data Op = Inc | Dec | Zero

    counter :: Int -> [Op] -> [Int]
    counter n []       = []
    counter n (op:ops) = case op of
      Inc  -> n + 1 : counter (n+1) ops
      Dec  -> n - 1 : counter (n-1) ops
      Zero -> 0     : counter 0     ops

    -- modified to return only the last value
    counter' n ops = last (counter n ops)

    x = [Inc, Inc, Inc]
    y = [Inc, Inc, Inc]

    test1 = counter' x == counter' x
    test2 = counter' x == counter' y
    test3 = counter' x == counter' (x ++ [Inc]) -- what?
---

Ultimately, while he does spend a little time talking about how reference works—and he's not completely wrong in his definitions—his application of this idea to "FP" (whatever he defines it as) is full of logical fallacies, unbacked assertions of impossibility, and... ultimately just totally flawed arguments.

It turns out you can have much of what he's asking for—even in Perl if you like. But to do so you must intentionally restrict yourself from using certain parts of your language. If you move the goal posts on your restriction over and over then, yes, you can discover a lot of weird counterexamples to your own broken models.

Re: Pissed off about functional programming (2005)

#58

Here's another myth: "Functional programming solves the concurrency problem". http://www.infoq.com/presentations/java-performance Scroll to 41:05

Has anyone ever seriously believed this? I guess it's just easier to coordinate resource sharing when resource sharing isn't allowed. My favorite is "Functional programming is becoming more relevant because...multi-core" so your 100x slower functional code would get a 4x speed up and only be 25x slower?

> Has anyone ever seriously believed this?

Hickey, Datomic?

Re: Pissed off about functional programming (2005)

#59
post #14

Earlier quoted context omitted.

That it's possible to write nontrivial programs using immutable data (see Clojure in particular), which makes debugging highly concurrent applications much much easier. That it's possible to achieve precise control over effects with only a moderate amount of overhead. Look at the perennial debate in Ruby-land over whether you should test at the unit or the integration level. In Haskell that's not a choice, it's clear…

Concurrent access of immutable data is super easy since....no real concurrency is going on. But what about when I need concurrency? One can manage the time with which updates are seen (so it is deterministic), but there are many ways to accomplish this beyond one mutable reference to an immutable world style that clojure seems to promote.

I'm sympathetic to that viewpoint; I prefer the Haskell style where some things are mutating and some things are not and you can see which and control when they happen. But I've seen the Clojure style used effectively, building real-world webapps, and I'd take either over the imperative-language style where your tools give you no help and it's up to you to keep track of when mutations can and can't happen.

Re: Pissed off about functional programming (2005)

#60
post #59

Earlier quoted context omitted.

Concurrent access of immutable data is super easy since....no real concurrency is going on. But what about when I need concurrency? One can manage the time with which updates are seen (so it is deterministic), but there are many ways to accomplish this beyond one mutable reference to an immutable world style that clojure seems to promote.

I'm sympathetic to that viewpoint; I prefer the Haskell style where some things are mutating and some things are not and you can see which and control when they happen. But I've seen the Clojure style used effectively, building real-world webapps, and I'd take either over the imperative-language style where your tools give you no help and it's up to you to keep track of when mutations can and can't happen.

There are other ways:

http://research.microsoft.com/apps/pubs/default.aspx?id=2112...

Post reply on HN