Live data from Hacker News

Why Functional Programming Matters (1984) [pdf]

cse.chalmers.se

31–40 of 145 posts

Re: Why Functional Programming Matters (1984) [pdf]

#31
post #22
post #11

Earlier quoted context omitted.

Functional programming largely has two schools: Treat "commands" as a separate entity from expressions, and bake "commands" into expressions. The former is largely Haskell, Clean, ... and the latter is exemplified by e.g., Standard ML or OCaml. There are trade-offs between the two, but I definitely belong to the second school: we simply add an imperative subset to our functional language. This means we can exploit an…

> This means we can exploit any efficiency trick an imperative program can do, including low-level stuff. The seasoned FP programmer will then proceed to encapsulate the efficiency trick in a abstract module such that the rest of the program doesn't have to worry about it. You can do that in a pure language as well (i.e. ST monad.) Purity shouldn't be given up lightly.

I think he was talking about something like unsafePerformIO. There are plenty of tricks you can not write on pure monads, and there are many Haskell libs that use the unsafePerformIO trick too.

Re: Why Functional Programming Matters (1984) [pdf]

#32

The top link on /r/haskell right now [1] is someone complaining that their program runs out of space due to a subtle interaction between laziness and IO. I think it's safe to say that we have tried laziness as the default and have learned that it's the wrong default, because it plays havoc with space and with IO. [1] https://www.reddit.com/r/haskell/comments/5h6emf/haskell_run...

Look, I fixed their code in so many ways:

    main =
        runConduitRes                        -- dealing with finite resources
             ( sourceFileBS "input.txt"      -- read input.txt as binary data
            .| decodeC utf8                  -- decode assuming UTF-8
            .| linesC                        -- split into lines
            .| mapC parseList                -- parse each line into list of text
            .| mapC (get 5)                  -- get sixth element of list
            .| catMaybeC                     -- discard lines with no sixth element
            .| encodeC utf8                  -- encode as UTF-8
            .| sinkFileBS "output.txt"       -- dump into output.txt
             )
And it's still very functional, if not more so!

This will run in constant space and linear time, it will buffer reasonably, it will not leak file handles, it will gracefully clean up on exceptions, it will not crash when it fails to parse something correctly, and it makes the encoding assumption explicit (you cannot split into lines unless you know the encoding).

Dealing with I/O is not hard when you use the correct primitives.

Re: Why Functional Programming Matters (1984) [pdf]

#33

A lot of pleas for functional programming give a long list of convincing examples that work well with functional programming. You can do this with almost any language, and this is also quite deceptional, as this is often done to convince beginners to learn another language; "look at how simple it is to program this contrived example in !". In order to be able to use a programming language or paradigm well, it is espe…

I write code for a living (data science), but am not really a programmer. So far, my favorite language is Scala with Spark. Other things I've worked with: Python, Clojure, Haskell, R, SAS, tons and tons of SQL.

The major advantage of functional programming here is that it models the domain really well: the vast majority of my work involves taking a small number of data sets, doing a long sequence of processing, and outputting a small number of data sets. Everything in between benefits substantially from error-preventing techniques such as purity and static types.

The biggest disadvantage is how hard it is to analyze and improve performance. Working with Spark means that a pipeline that works on X gb of data will often fail on 5X with out of memory errors, and it's nontrivial to diagnose and fix. It's not clear to me how much of this is due to Spark itself vs. laziness.

Re: Why Functional Programming Matters (1984) [pdf]

#34
post #26

Earlier quoted context omitted.

The problem is laziness. Lazy IO just makes it happen with files, which is more noticeable. In regular lazy code it happens with memory instead (space leaks).

these are not problems, only small details that you have to be careful of..not a large tradeoff to pay

That's like saying that bounds overflow in C isn't a problem, just something you have to be careful of.

To tell you the true, I can't understand why the code the OP talks about has a problem, and I can't even reproduce the problem (and never saw any of the problems of lazy IO in practice either). But I also can not say the code has no problem, and that is a big issue with the language.

Re: Why Functional Programming Matters (1984) [pdf]

#35
post #11

A lot of pleas for functional programming give a long list of convincing examples that work well with functional programming. You can do this with almost any language, and this is also quite deceptional, as this is often done to convince beginners to learn another language; "look at how simple it is to program this contrived example in !". In order to be able to use a programming language or paradigm well, it is espe…

Functional programming largely has two schools: Treat "commands" as a separate entity from expressions, and bake "commands" into expressions. The former is largely Haskell, Clean, ... and the latter is exemplified by e.g., Standard ML or OCaml. There are trade-offs between the two, but I definitely belong to the second school: we simply add an imperative subset to our functional language. This means we can exploit an…

> As for performance and memory usage: it is always a property of the architecture or system, not of the programming language. Dropping to a low-level language, such as C, usually doesn't buy you too much these days. What is more important is that most C compilers in use have vastly more time invested into optimizing routines than the typical FP compiler. Apart from that, you can easily mange the same kind of data in e.g., OCaml than you can in C.

This is true in in principle, but not in practice. It is not just a question of whether a language is particularly amenable to optimisation and clever compilation (C is not, by the way), but also whether the baseline of naive compilation is efficient in itself. A naive C compiler will generate vastly better code than a naive compiler for most functional languages, if only because naive C compilation will primarly allocate statically and on the stack, while a functional language will perform an enormous amount of heap allocation. Futhermore, natural C programming style tends towards cache-friendly arrays and bulk allocations, while natural functional programming style tends towards lots of pointers pointing everywhere. Certainly, there are functional languages with efficient array libraries and the like, but they are less natural, and their use is often considered an "optimisation". And of course, most functional languages give you some way of accessing raw memory and essentially just writing C-in-Haskell or whatever, but then you're not really doing functional programming anymore.

Of course, if the problem is at its essence about pointer chasing, or composing IO pipelines, then functional programming is a fine choice, because the performance of the language is less important, and the ability to reason about complicated control flow is important. What I find interesting about Haskell is that due to the clear reification of IO, the compiler can actually perform optimisations on IO pipelines, such as fusion. Think about it - IO is usually the prime example of a fusion inhibitor, but GHC can actually do it for libraries such as conduit! It is a little ironic that Haskell is probably the best language I know of for describing complex IO operations.

An interesting twist is of course when you construct a functional language with an eye towards efficient compilation from the start. Then the primary compound data type is no longer the linked list, but the array, and you tend to end up with an array language. Examples include SISAL, Single Assignment C, NESL, Accelerate, Futhark, Lift, etc, which are naturally fairly efficient due to their programming model, and which provide strong functional invariants that the compiler can then exploit. These .anguages are all still pretty experimental and unwieldy in practice, though.

Re: Why Functional Programming Matters (1984) [pdf]

#36
post #19

Earlier quoted context omitted.

No, that's not laziness per se, that's "lazy IO", something somewhat different. EDIT: It's not even a lazy IO problem since no lazy IO functions are used there.

The problem is laziness. Lazy IO just makes it happen with files, which is more noticeable. In regular lazy code it happens with memory instead (space leaks).

EDIT: I was wrong. It seems to actually just be a hideously inefficient in-memory representation of the data (akin to List[Char]) perhaps plus a space leak.

That's what I get for not reading the code carefully...

Re: Why Functional Programming Matters (1984) [pdf]

#37
post #26

Earlier quoted context omitted.

these are not problems, only small details that you have to be careful of..not a large tradeoff to pay

That's like saying that bounds overflow in C isn't a problem, just something you have to be careful of. To tell you the true, I can't understand why the code the OP talks about has a problem, and I can't even reproduce the problem (and never saw any of the problems of lazy IO in practice either). But I also can not say the code has no problem, and that is a big issue with the language.

> That's like saying that bounds overflow in C isn't a problem, just something you have to be careful of.

I think there's a significant difference between undefined behavior (hello security flaws!) and the program crashing in a (reasonably) well-defined way.

Re: Why Functional Programming Matters (1984) [pdf]

#38
post #22

Earlier quoted context omitted.

> This means we can exploit any efficiency trick an imperative program can do, including low-level stuff. The seasoned FP programmer will then proceed to encapsulate the efficiency trick in a abstract module such that the rest of the program doesn't have to worry about it. You can do that in a pure language as well (i.e. ST monad.) Purity shouldn't be given up lightly.

I think he was talking about something like unsafePerformIO. There are plenty of tricks you can not write on pure monads, and there are many Haskell libs that use the unsafePerformIO trick too.

> There are plenty of tricks you can not write on pure monads

This made me curious, could you list some?

Re: Why Functional Programming Matters (1984) [pdf]

#39
To me, this is the most important part of the text (p2, bottom):

"The ways in which one can divide up the original problem dep end directly on the ways in which one can glue solutions together Therefore to increase ones ability to mo dularise a problem conceptually one must provide new kinds of glue in the programming language."

Functional programming is great because it provides two (new) kinds of glue: function composition and lazy evaluation.

Even if you accept that (I certainly do for function composition, less enthusiastic about lazy evaluation), I would say that it only provides two new kinds of glue.

We need lots of kinds of glue, in other words, lots of architectural connectors. And that means linguistic means of defining and varying architectural connectors. http://objective.st

Re: Why Functional Programming Matters (1984) [pdf]

#40
post #35
post #11

Earlier quoted context omitted.

Functional programming largely has two schools: Treat "commands" as a separate entity from expressions, and bake "commands" into expressions. The former is largely Haskell, Clean, ... and the latter is exemplified by e.g., Standard ML or OCaml. There are trade-offs between the two, but I definitely belong to the second school: we simply add an imperative subset to our functional language. This means we can exploit an…

> As for performance and memory usage: it is always a property of the architecture or system, not of the programming language. Dropping to a low-level language, such as C, usually doesn't buy you too much these days. What is more important is that most C compilers in use have vastly more time invested into optimizing routines than the typical FP compiler. Apart from that, you can easily mange the same kind of data in…

And it's not just about linked lists vs arrays, but also about the actual call stack.

FP code tends to be heavy on recursive algorithms, which means you need mutually tail recursive functions. And laziness. Somebody mentioned the State monad and the IO type. Well, these are lazy abstractions with a memory-safe bind/flatMap operation that makes them tick.

Well, the problem with these abstractions is that they'll require building a data structure that cannot be an array. And in the case of languages like Scala or Clojure that don't have real tail recursion because of the JVM, you need to manage your own trampoline as well. We now have the Free monad, which is awesome, except that it is very heap unfriendly.

Actually for FP you need persistent data structures everywhere, not just lists, like maps, hashes, vectors and all known implementations are some sort of trees. And this is an active area of research, but building cache friendly trees is a hard problem.

Post reply on HN