Hacker News new | past | comments | ask | show | jobs | submit login

Here's the link where he discusses functional programming style:

https://web.archive.org/web/20170116040923/http://gamasutra....

He does not say that that his email is completely outdated - he just says that calling pure functions is exempt from the inlining rule.

He's not off writing pure FP now. His approach is still deeply pragmatic. In the link above he discusses degrees of function purity. "Pure FP" has a whole different connotation - where whole programs are written in that constrained style.






The original article literally starts with this:

> In the years since I wrote this, I have gotten much more bullish about pure functional programming, even in C/C++ where reasonable: (link) > >The real enemy addressed by inlining is unexpected dependency and mutation of state, which functional programming solves more directly and completely. However, if you are going to make a lot of state changes, having them all happen inline does have advantages; you should be made constantly aware of the full horror of what you are doing.

He explicitly says that functional programming solves the same issue as inlining but more directly and completely.


Maybe this is just a subtle semantic thing, but what I took from this is that he is factoring out pure functions where ever possible, within a procedural context. In my mind, that’s not the same thing as “functional programming”.

Thank you for this. I appreciate that this (classic) article lays bare the essence of FP without the usual pomp and "use Lisp/Scheme/Haskell already" rhetoric. My takeaway is that FP is mostly about using functions w/o side effects (pure), which can be achieved in any programming language provided you're diligent about it.

This is a bit naive though. It depends on what you want to do and whether the language you are using offers the required primitives and other things like persistent functional data structures. Without those, you will find yourself hard-pressed to make FP happen. It is of course usually possible with most languages (except those where primitives are already mutating and therefore infectiously prevent you from writing pure functions), but it might not be idiomatic at all, or might not be feasible to roll all things your own, to replace any mutating basics. For example imagine having to copy a data structure all over the place again and again, because its methods are mutating its internal state. That would be inefficient, much more inefficient than a well written corresponding functional data structure, and it would be ugly code.

Are you going to write that extra data structure, when your task is actually something else? Some management breathing down your neck, asking when something will be done? Or not so well versed coworkers complaining about you adding a lot of code that might need to be maintained by them, while they don't understand FP? Do you even have the knowledge to implement that data structure in the first place, or will you need to study a couple of papers and carefully translate their code, if any, i to your language and then verify expected performance in meaningful benchmarks?

Lots of problems can arise there in practice.


No functional programming is about programming as if your code is a math equation.

In math people never use procedures. They write definitions in math in terms of formulas and expressions.

If you can get everything to fit on one line in your programming. Then you are doing functional programming.

The lack of side effects, lack of mutation and high modularity are the beneficial outcome of fp, it is not the core of what you're doing. The core of what you're doing is your defining your program as a formula/equation/expression rather then a list of procedures or steps. Of course, why you would write your program this way is because of the beneficial outcomes.

By coincidence if you write your code in a way where you just account for the side effects like deliberately avoiding mutation, IO and side effects... then your program will become isomorphic to a mathematical function. So it goes both ways.

Another thing you will note and most people don't get this is that for loops don't exist in FP. The fundamental unit of "looping" in fp is always done with recursion, just like how they would do it in mathematical expressions.


As someone who likes math (math major, applied math grad) and who picked up functional programming relatively early in my career, I don't find this model (fp is just math) to improve my understanding or make it easier to understand why I would want to program like this

Talking about state and error handling is helpful because it helps explain why to use the tool, not how the tool was forged (or originally conceived)


This doesn’t help you understand why you should do functional programming.

It just helps you understand the nature of what functional programming actually is. Too many people think it’s just immutability, anonymous functions, map, reduce and filter.

Understanding why you should do functional programming is orthogonal to understanding what it is.

Even if I tell you functional programming is more modular and referentially transparent and lacks state. None of these things truly register until you have done both imperative programming and Haskell programming for a non trivial amount of time.

Also error handling is orthogonal to functional programming. Yes I know it’s clever how Haskell does it but it’s independent to functional programming… and even so.. explaining maybe monads or any other error monad just makes things less understandable.


My usual statement on monads is "like any abstraction, it makes sense when you need it"

If you write a lot of go code and think "this error management (!= nil anybody?) is a drag, there has to be a better way! The truth is: go largely looks like how cpp is written at Google, EXCEPT in Google cpp you get to use macros like RETURN_IF_ERROR which handles the ubiquitous StatusOr class.

Is this StatusOr a monad? I'm failing to recall what the monad functions look like internally, but I suspect it's trivial to make it one (I mean, it probably is! And if it's not it would be trivial to make it one)

Do you need to understand monads to see why they're useful here? I don't think so! And so even if you don't know how to build the microwave, you know how to use it.


For-loops do exist, they just need to not have side effects, which in practice means the likes of map/filter/reduce (ideally promoted to a first class language feature like sequence comprehensions).

You could argue that those are still desugared to recursion, but I think at that point it's kinda moot - the construct is still readily recognizable as a loop, and it's most likely also implemented under the hood as an imperative loop with encapsulated local state; not that it matters so long as semantics stay the same.

In general, so long as mutation can be encapsulated in modules that only expose pure functional interfaces, I think it should still count as FP for practical purposes.


>For-loops do exist, they just need to not have side effects,

No they don't. For loops and loops in general are procedural actions. You are jumping from directive to directive, command to command. Loops are NOT functional at all. loops are an artifact of the computational machine, jumping to different instructions.

Functional programs like functions in mathematics DO not contain for loops.

> which in practice means the likes of map/filter/reduce (ideally promoted to a first class language feature like sequence comprehensions). >You could argue that those are still desugared to recursion, but I think at that point it's kinda moot - the construct is still readily recognizable as a loop,

All programs are desugared into assembly instructions. Assembly instructions are procedural by nature... they are not functional so your point is moot as everything is desugared into loop based jumps.

map/filter/reduce Are not loops. They are fundamentally different. It doesn't matter if it's "recognizeable" as a loop, it is NOT a loop. There is an isomorphism between imperative and functional programming, So the definition of Loop vs. no loops refers to the superficial differences between the two EVEN when the underlying things are the same.

>In general, so long as mutation can be encapsulated in modules that only expose pure functional interfaces, I think it should still count as FP for practical purposes.

It actually can't... for loops rely on mutation to work.

a for loop looks like this:

     <OUTER SCOPE>
     for i in range(10):
          <INNER SCOPE>
By nature the for loop needs to influence outer scope otherwise your for loop is utterly useless. So how would you influence outer scope from inner scope?

     <VARIABLE FROM OUTER SCOPE>
     for i in range(10):
          <MUTATE VARIABLE FROM OUTER SCOPE WITHIN INNER SCOPE>
That's the only way man.

This is the fundamental nature of for loops. They are imperative constructs. Sure it can look very similar to map or reduce or even filter, but THEY are not the same.


Python:

   def range_mul(n, mul):
      for i in range(n):
         yield i*mul

   x = list(range_mul(10, 20))
range_mul is a pure function, yet it is implemented with a for loop. Once you have first class continuations or the equivalent, the differences between imperative and pure blur (cf. Haskell do-notation, is it imperative?).

In any case I think you are missing int_19h point, the it doesn't matter if a function is implemented using imperative constructs, if you can't tell from the outside it is still pure. And an FP compiler will convert pure code to imperative anyway.


range_mul is not pure. Don’t believe me? Ask ChatGPT:

“””

No, a Python function that contains the yield keyword is not a pure function.

A pure function is defined as a function where the output is solely determined by its input values, with no observable side effects. This means it should:

• Always return the same result when given the same input. • Have no side effects (like modifying global state, reading from or writing to files, etc.).

Functions containing yield are generators, which maintain internal state between successive calls, making their behavior dependent on the sequence of calls (because they return values incrementally and remember the point where they left off). This internal state makes them impure because they don’t always return the same result when called with the same input—they return successive values upon subsequent calls.

In summary, a function with yield is not pure due to its stateful behavior and non-deterministic output across multiple calls.

“””

>In any case I think you are missing int_19h point, the it doesn't matter if a function is implemented using imperative constructs, if you can't tell from the outside it is still pure. And an FP compiler will convert pure code to imperative anyway.

You’re making a side point here that’s irrelevant to the main point. Sure you can do this, these are techniques you can do to segregate your impure code away from your pure code. But is this the topic of conversation? No.

Additionally your code actually is like a virus. Not only is the example wrong but The impurity can infect other functions that use it such that those functions lose determinism and purity as well.

Which brings me to my main point. Circling back to my parent comment. Most people don’t understand fp and don’t get carmacks insight… and you are an example of such a person for now.


Well, technically a call to range_mul() is still pure and it matches the definition: it has no side effects, and it returns a new instance of the same object (the generator); the generator itself is inpure of course, so I concede the point. But that's a limitation of python generators being one-shot; with proper delimited continuations you can snapshot the state at any point and replay it at leisure, not differently than a lazy stream.

Regarding the rest, I was referring to this comment from int_19h re encapsulation:

"In general, so long as mutation can be encapsulated in modules that only expose pure functional interfaces, I think it should still count as FP for practical purposes."


> Haskell do-notation, is it imperative?

No it’s not. It only works in context of a monad and it achieves what looks like imperative code by utilizing closures.

I think it’s quite obvious imperative code with mutations but a deterministic end result can be hidden behind a function and treated as pure. I don’t think anyone misses this point so it’s likely redundant to bring that up. Either way it’s not the point of the conversation.


I dunno, I imagine that there's no appreciable difference between:

  (loop :for i :below 10 :collect i)
and:

  (let loop ((i 0))
    (if (= i 10)
        nil
        (cons i
              (loop (1+ i)))))

First off I can’t read that. Please use more common pseudo code for your example if you want me to understand.

Second it looks like there’s a function called for here that does something similar to reduce? That’s a functional thing.

The topic at hand is for loops as in loops that are procedural statements which is what most people recognize as loops. For, while, do while and those things you see in popular languages. Not some obscure construct in lisp.

If your talking about the isomorphism between functional and imperative programming, I already covered that.


Largely orthogonal to your comment:

One interesting thing I learned/realized when reading about the msr dafny project is that for loops mean you need to provide guarantees about invariants.

How do I know there's no index out of bounds? How do I know how large the resulting array is?

When you have to write post conditions for each loop, it makes higher order functions (map, reduce, filter) much more appealing. The proof was already done in the function that will invoke yours!


He literally said he’s bullish on pure fp. Which means he is off writing pure fp. His own article about it never explicitly or implicitly implies a “pragmatic approach”.

I never said he said his email was completely outdated. He for sure implies it’s outdated and updates us on his views of inlining which I also mentioned.


> I never said he said his email was completely outdated.

From your prior message:

> Carmack called his email completely outdated


Ok my bad but I did mention what he’s doing with inlining. So I contradicted myself in the original message which you didn’t identify.

He still does inlining.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: