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

Even in maths, I find a solution in terms of the problem easier to understand than one in terms of the previous step.

Even when the recursive form is a more natural representation, like arithmetic sequences: start at s, increase by d with each step:

  a(0) = s, a(n) = a(n-1)+d

  a(n) = s + n*d
The analytical form seems simpler, neater, more "right" and more efficient to me - even though, if you want the whole sequence, the recursive form is more efficient (given tail-call optimisation).

I suspect I'm just not smart enough.

fp can be much shorter, and the execution model isn't actually hidden, just unfamiliar (and unintuitive and unnatural - for me). Consider: all suffixes of a list. In jq:

  while( length>0; .[1:] )



>I suspect I'm just not smart enough

Nah, I have a PhD in math and I agree with you completely. Imperative is way better. And most mathematicians agree with me. You can see this by cracking open any actual math or logic journal and looking how they write pseudocode (yes, pseudocode: where things like performance don't matter one tiny little bit). You'll see they're almost entirely imperative. Sometimes they even use GOTO!


Agreed. I arrived at programming through math (B.S. in Mathematics) and have no love for FP. At the end of the day all software (except hobby projects) is mostly about maintaining it. FP adds unnecessary complexity, abstraction and obfuscations. None of those qualities help code maintenance.


Is this view of FP based on actual experience maintaining a non-trivial program written in an FP language? In my experience, FP doesn’t necessarily add a lot of unnecessary complexity. Sure, languages like Haskell are perhaps initially a bit more abstract when learning them, but once you know the basics, you can write pretty straightforward code in it. You can also do crazier things, but there is no need for that in most software.

Keeping a functional style, regardless of the language (although FP languages lend themselves better to this) can help in keeping code more decoupled, since you have to be explicit about side effects.

I think that both FP and imperative languages have places where they shine, and I freely switch between them depending on the project. Given how much some imperative languages have recently borrowed from FP languages, I think that this shows that functional programming has some significant merits.


>You can also do crazier things, but there is no need for that in most software.

For dev teams of sufficiently large size, a general principle is: whatever crazy things the language allows, someone is going to do and commit into the codebase.


Lately I’ve been thinking that a lot of code style debates center around an explicit versus implicit axis. Imperative is more explicit, and, in one sense, easier to see what’s going on since it lays everything out step by step. On the other hand, those same steps are a mix of essential steps (that deal with the problem being solved) and accidental steps (that deal with computer and code in order to get the job done.)

It seems to me that OOP, Functional, and Relational programming models try to abstract away the accidental steps, but like all abstractions there are limitations.

I suspect that once familiar with one of these models, imperative seems awfully tedious, however now the code is more obscure to those not well versed in the paradigm, thus we have a trade off between ease of use for many and optimal for some.


Implicit also means a tradeoff in estimating performance, an instance of an abstraction leaking.

I've been trying to think of a totally clean functional abstraction, i.e. that's functional under the hood, but there's no way to tell. Perhaps in a compiler?


Absolutely, explicit vs implicit is part of imperative vs. functional. And doing more with less information is elegant - and has a deeper significance in terms of Occam's Razor, that simplicity tends to closer to the truth, and therefore generalizes better. And, like pg's take, shorter code means less code to write, to read, to modify.

There can be leakage, when the given model is not perfectly accurate, and you need the true implementation details (this also happens for imperative code - it can be very helpful to have source of libraries) - in debugging, in performance, in working out how to do things.

But I feel a general issue is that it might not be a good fit for the human code processing system... Our interactions in the real world are more like imperative programming - not just familiarity, but how we evolved. This issue is similar to how quantum physics and relativity aren't a good match to the human physics system, which seems to be the mechanical/contact theory. To convert things to recursion is like working out an inductive proof - you can do it, but it is harder and more work than just getting it done in the first place.

A specific issue about this is that functional recursion is based on the previous step, whereas imperative code is usually based on the starting step. Like, build a new list at each recursion vs. indices into the input list. The latter is easier because it's always the same thing being indexed, instead of changing with each recursion.


This doesn't look to me like the difference between functional and imperative so much as the difference between recursion / iteration and map / list comprehension.


You may need to exercise some charity here.

I've been trying to see why fp isn't intuitive for me.

I suspect it's like a second (human) language acquired as an adult: only those with a talent for language (maybe 5%?) can become fluent with practice.

Regarding my first example, I see recursion (or induction) as the essence of fp; and the recurrence form of arithmetic sequences is the simplest recursion I've seen used in mathematics.

The explicit form in that example is harder to justify as "imperative". But a commonality of imperative style is referring to the original input, rather than a previous step (see the first line of my above comment). This isn't the literal meaning of "imperative", but may be a key distinction between fp and ip style - the one that causes the intuitive/fluency issue for me.

To illustrate using my third (jq) example of suffixes, here's an "imperative" version, in py-like psuedocode:

  for i = 1 to length
    # a suffix
    for j = i to length
      print a[j]
    print \n
This is so much longer than jq (though shorter if used .[j:]), but it is how I understand the problem, at first and most easily.

It always refers to the starting input of the problem, not the previous step, and this might be why it's easier for me.

I'm interested in your comment - could you elaborate please? There's a few ways to relate your comment to different parts of mine, and I'm not sure which one was intended.


Well I agree with you that that kind of recurrence (which mathematicians love to use so much, as do some functional programmers who're overly influenced by math) is not very intuitive and frankly is a programming anti-pattern in my view.

But I disagree with you that recursion is the essence of fp. For your concrete example, a more functional version of doing that (in Python) would be something like:

  print("\n".join(a[i:] for i in range(len(a)))
No need to reuse f(i-1) when you can express f(i) directly.

Reusing the previous step (whether it is using recursion, rising intermediate computations in the form of local variables in a loop, or through a fold) should only be done when absolutely necessary.


> [recurrence] is not very intuitive and frankly is a programming anti-pattern in my view. [...] Reusing the previous step ... should only be done when absolutely necessary

Thanks, that's my main concern (fp was just an example). Would you agree the reason it is bad is becase there is more to track mentally in the execution model? (i.e. the intermediate results).

I think a complex execution model is problematic in general (it sounds obvious when I say it that way).

> which mathematicians love to use so much,

hmm... I was thinking "induction", and believed that fp is the same. .. > But I disagree with you that recursion is the essence of fp

This is BTW now, but that statement surprises me. Can you elaborate? What is the essencd of fp (has it one)?

Is your py version "more functional"? I'm so wedded to the idea that fp=recursion that that's the reason it doesn't seem functional to me. What makes it functional? Just that it's a nested expression (i.e. fn calls)?


Well I guess you could say the essence of FP is working recursively without (mostly) thinking of it, and not having to deal with the sort of control flow necessary for either loops or the kind of self-administered recursion you seem to think of.

The .join() taking the iterator in their example is, if you look closer, very much a fold/reduce repeatedly invoking a join of the thus far assembled string, the next part, and \n. Recursion!

Also rather than mutable i/j variables being incremented (albeit implicitly so in your example), generating a list of all numbers on which to run.




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

Search: