← Back to context

Comment by TrackerFF

12 days ago

What's the benefit of learning a PURE functional programming language, opposed to just using a language which has adapted the best bits and pieces from the functional programming paradigm?

Given that you want write code that sees "real world" use, and is used to handle data and events from the real world. To me, sometimes the line between optimized code and intellectual curiosity blurs.

> What's the benefit of learning a PURE functional programming language

1. It makes it easy to learn how to structure a program in a pure way, which is hard to do in languages that offers you a easy way out.

2. Since "everything" is pure, writing tests is easier.

3. You know for certain that if you discard the result of a function call, all the side-effects that it would normally trigger would be stopped as well.

4. A program where all side-effects are guaranteed to be pushed to the boundaries, is a program that's easy to reason about.

> a language which has adapted the best bits and pieces [...]

Languages that has adapted to best bits and pieces from X, Y, Z tend to be worse than a language specifically for X, Y and Z.

For instance, Java supports functional programming but functional programming languages are much better at it because they were designed for that specific paradigm. In the same vein, sure you can write pure programs in F#, but not as easily as in Haskell that was designed for doing just that.

> and is used to handle data and events from the real world

Pure code really only means (in practice) that side-effects are controlled, which is generally very helpful. It forces you to structure programs in a way which makes it easy to pinpoint where data is coming in, and where data is going out. It also makes for easier testing.

Being able to know, definetly, the answer to "will calling foo perform a network request" without having to read the source for foo is quite nice, especially when dealing with third-party code.

All this said, I probably wouldn't begin with Haskell. A language like Elm is much better suited for learning writing pure programs.

  • Agree with all the reasons, but number 1 is really the most important:

    > 1. It makes it easy to learn how to structure a program in a pure way, which is hard to do in languages that offers you a easy way out.

    When there's an escape hatch, you will reach for it at some point. It helps with getting things done, but you never end up really confronting the hard things when you have that, and the hard things are an important part of the learning/benefit.

  • The problem with Haskell is that it's slow and memory-heavy (and OCaml is the same, but worse). F# and Scala (and Clojure?) are pretty much the only reasonably usable FP languages.

    • Where are you getting your info from?

      Typical OCaml programs, when compared to similar C++ would be slower but use less memory.

      F# and Scala are both OCaml in disuse. I don't know what you mean by "reasonable"... but, if the idea is "easy to reason about", then these two don't particularly stand out much.

      Languages that are easy to reason about would be generally in the category where you need to do fewer translations before you get to the way the program is executed (i.e. bytecode adds an extra step, thus making a language harder to reason about). Also, languages with fewer primitives are easier to reason about, because the program text becomes more predictable.

      In general, "functional" languages are harder to reason about when compared to imperative, because computers inherently don't work in the way the programs are modeled in "functional" languages, so there will be some necessary translation layer that transforms an FP program into a real computer program. There are people who believe that FP programs are easier to reason about due to the lack of side effects. In my experience, the lack of side effects doesn't come close to compensating the advantages of being able to map the program to what computer actually does.

      All kinds of behind-the-scenes mechanisms in the language, s.a. garbage collector, make the reasoning harder too, in a sense. We pretend that GC makes reasoning easier by making a mental shortcut: we pretend that it doesn't matter when memory is freed. But, if you really want a full picture, GC adds a whole new layer of complexity when it comes to understanding a program.

      Yet another aspect of reasoning is the ability of reasoner to act on their reasoning. I.e. the reasoning might be imperfect, but still allow to act (which is kind of the human condition, the way we are prepared to deal with the world). So, often, while imperative programs cannot be formally easily reasoned about, it's easy to informally reason about them to be efficient enough to act on that reasoning. "Functional" programs are usually the reverse: they are easier to reason about formally, but they are very unnatural to the way humans reason about everyday stuff, so, acting on them is harder for humans.

      "Functional" languages tend to be more in the bytecode + GC + multiple translations camp. And, if forced to choose with these constrains, I'd say Erlang would be the easiest and the best designed language of all the "popular" ones. SML would be my pick if you need to get into the world of Haskell, but deciphering Haskell syntax brings you to the boil.

      3 replies →

What's the benefit?

You start to see functions as self-contained things, as lego blocks. All the logic of the function is there in the function. It only works on values it receives as inputs (it can't read global variables). It only outputs its results (it doesn't assign them to some other global variable that you have to track down).

This makes your code modular. You can add a function in a chain of functions, if you want to perform an extra transformation. Or, you can replace a function with a different one, if you want to change something about the logic.

  • Is there a benefit if you're already familiar with writing functions like that? Is it wrong for me to expect that most programmers are already familiar with functions that only use their inputs, but treat that style as significantly more optional?

    I wrote pure functions for a minute there but that's not the same, a function that only uses its inputs can modify an object while a pure function would have to return a new object. But, similarly, I bet that a lot more people know about pure functions than have any working knowledge of Haskell.

    • It seems you only focused on one of the conditions I mentioned.

      You have to follow both rules: the one about inputs and the one about outputs.

      This is like a contract. If you enforce it throughout your program, you gain some guarantees about your program as a whole.

      13 replies →

    • > Is it wrong for me to expect that most programmers are already familiar with functions that only use their inputs

      They'll experience no friction when using Haskell then. Haskell only refuses to compile when you declare "Oh yeah I know functions from other languages this is easy" but then do some mutation in your implementation.

      3 replies →

This is all myth. People don't write Haskell, because they read why other non-Haskellers also don't write Haskell, based on what other non-Haskellers wrote.

> a language which has adapted the best bits and pieces from the functional programming paradigm?

Why write in a statically-typed language when dynamically-typed languages have adapted the best bits and pieces from statically-typed languages?

  • > Why write in a statically-typed language when dynamically-typed languages have adapted the best bits and pieces from statically-typed languages?

    Unfortunately, dynamically-typed languages haven't adapted the best bit from statically-typed languages: that all types are enforced at compile-time!

    • Yep, that's the parallel I was going for.

      Functional languages give you the same output for the same input, and almost-functional languages ... probably give you the same output for the same input?

Don't think of it as being all pure code, think of it as tracking in the type system which parts of your code may launch the missiles and which parts can't. Given the following program,

    main :: IO ()
    main = do
      coordinates <- getCoords
      launch trajectory
      where
        trajectory = calcTrajectory coordinates
    
    getCoords :: IO Coordinates
    getCoords = -- TODO
    
    launch :: Trajectory -> IO  ()
    launch = -- TODO
    
    calcTrajectory :: Coordinates -> Trajectory
    calcTrajectory = -- TODO
    

I can look at the types and be reasonably certain that calcTrajectory does no reads/writes to disk or the network or anything of that sort (the part after the last arrow isn't `IO something`), the only side effect is perhaps to heat up the CPU a bit.

This also nudges you in the direction of an Functional Core, Imperative Shell architecture https://www.destroyallsoftware.com/screencasts/catalog/funct...

  • >as tracking in the type system which parts of your code may launch the missiles

    given that Haskell is lazy by default there's a million ways to shoot yourself in the foot through memory leaks and performance issues (which is not unlike the problems the IO type attempts to make explicit in that domain), so I never really understand this kind of thing. Purity doesn't say much about safety or semantics of your code. By that logic you might as well introduce a recursion type and now you're tagging everything that is recursive because you can easily kill your program with an unexpected input in a recursive function. To me this is just semantics you have to think through anyway, putting this into the type system just ends up creating more convoluted programs.

Haskell interfaces with the real world. ST allows for mutability in pure context

https://github.com/serprex/Fractaler/blob/master/Fractaler.h... fractal renderer I wrote in highschool, has mouse controls for zoom / selecting a variety of fractals

https://github.com/serprex/bfhs/blob/master/bf.hs brainfuck interpreter which mostly executes in pure context, returning stdout with whether program is done or should be reinvoked with character input. Brainfuck tape implemented as zipper

your program may exist in real world, but most of it doesn't care about much of the real world

There's a video game on steam you can buy with real dollars built in Haskell.

I work full-time writing Haskell. Fintech stuff. No shiny research going on here.

I've written some libraries and programs on my stream in Haskell. One is a client library for Postgres' streaming logical replication protocol. I've written a couple of games. Working on learning how to do wave function collapse.

Believe it or not, functional programmers -- even ones writing Haskell -- often think about and deliver software for "real world," use.

> Given that you want write code that sees "real world" use, and is used to handle data and events from the real world.

Real world? As opposed to what?

Is there any benefit to answering such polemical questions as if they are not rhetorical?

  • As opposed to the abstract academic world?

    The only time I had contact with Haskell was in university and I did not see it appealing back then, nor now, nor have I ever seen a program that I use, written in it.

    So learning a bit of pure Haskell might have been beneficial for me to become a better programmer, but I still fail to see it being more than that - a academic language. Useful for didactic purposes. Less to actually ship software.

    • > nor have I ever seen a program that I use, written in it

      The only mass market Haskell software that I know of is Pandoc. Others like Shellcheck and Postgrest are popular in their niche.

      I am not sure that Haskell is faring worse that other programming languages in its level of popularity, like Julia, Clojure or Erlang.

      9 replies →

I can't speak for others, but I never really understood the benefits of functional programming when my language pretty much allowed unbounded mutation anywhere. I would say there's a chance for impure languages to impede you in learning what functional programming is about (or at least my experience with F# and OCaml did not really help as much as it otherwise could have I think).

Your mileage might vary, but I've heard advice from others to learn Haskell and "go off the deep-end" because of people citing similar reasons.

In a way, one benefit is the whole ecosystem / culture / idioms built on top. Haskellers went further in that direction than most languages (except maybe scalaz and some hardcore typescript devs).

Pure functional programming doesn't preclude side-effects like IO; it makes side-effects explicit rather than implicit!

At my previous job, we used pure functional programming to ensure that custom programs only had access to certain side-effects (most importantly, not IO). This meant that it was trivial to run these custom programs in multiple environments, including various testing environments, and production.

In my experience, I only really learned how to write small functions after Haskell. The discipline it forces on you is a good training.