Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This reminds me of Brian Will's "Object-orientation is Bad" where he makes the case that most decoupling tends to be more confusing than long-form code that's got sufficient comments.

https://youtu.be/QM1iUe6IofM?t=2235



I think that

    fn x() {
        doStuff();
        moreStuff();
        forgotSomething();
    }
is pretty bad code, but that's probably because I consider procedures with no arguments and no return value a sign that something is poorly factored. However,

    fn x(y) {
       foo = doStuff(y);
       bar = moreStuff(foo);
       if (isSomething(bar)) {
           return theRest(bar)
       } else {
           return theBest(bar);
       }
    }
can be a good way to separate the why from the how and clearly communicate what's going on, especially with conditionals in the mix.

It's important, however, that these helper functions are not haphazardly strewn around the code base and accessible to things that don't need them. Depending on the language/context, I'd reach for nested function definitions or public/private keywords (or a combination), because definitely, it can be very hard to approach a big file with a bunch of (often poorly-named) functions that are defined at the same hierarchical level but not meant to be used at the same level.


Haven't you just described functional vs procedural programming?


No he described implicit arguments and mutation vs. explicit arguments and mutation.

It's independent of OOP vs. functional, through it's easier to have implicit arguments and mutation in OOP so you see it more often there, especially if people somehow ended up believing that programming OOP means you need to create a class for everything and make all states a object member instead of e.g. a variable in the stack.


Yes! I often see code that where a method sets an instance variable, then calls another method that does some work based on that instance variable, which is then not used until the next time that first caller is used. The instance variable is redundant, and the control flow is obfuscated. I think the principle is just to only expose something when you really have to (harhar), and that the 70's suspicion of unstructured use of global scope should be equally directed to class scope.


That's called temporal coupling, where some code only works if some previous code set some variable earlier in time. And it's a sign that you're building an implicit state machine.


It's not explicitly expressed (the functions could still execute sideeffects and e.g. mutate things), but yes, that is pretty much the style that pure functional programming enforces!.


The former example was much easier for me to understand than the latter, even if it wasn't factored well. But I'm assuming that the functions aren't mucking about with global state or something equally distasteful.


Well, if we assume that both are doing the same work, then the explicitly named variables y, foo, and bar of my second example would, in the first example, have to be global variables, or at least things that are defined in the scope of the definition/call of x.

Given that, I have to say that I, err, and I'm sorry if this sounds a little arrogant, but I don't really believe you when you say that it's easier to understand. It might be easier on the eyes, sure, but to really understand how the first one works, you'd have to look into all the defined helper functions, and see which outer-scope variables they touch (global/module/class scope), and also, probably, have some knowledge of where x is called. In a style closer to the second example, there are less outside dependencies.

Of course, this is all very abstract. I'm not saying there aren't cases where a few variables in global (or class) scope that get mutated by bare functions can't work at all, but as a rule I don't consider it a good style.


I don't really fully understand how the kernel scheduler works, but I know in general how it works, and so I can write software using it. In this sense, a simple implicit abstraction is easier for me to understand than one where I'm peeking under the covers, so to speak.

Even just seeing a few variables or objects passed around, I still don't know exactly what it's doing, or hiding. I can more quickly understand the high level idea with the former example without the details potentially confusing me.


I see what you mean.

I think it depends on the type of code and how you interact with it. A library that requires its consumer to keep track of a bunch of data and pass it in and out at the right times when it could just as well handle internally is not a very good library. But if we say that all the code the above example is from an application, where a normal change might involve touching all of the mentioned functions, making it explicit where some variable is used.


> But I'm assuming that the functions aren't mucking about with global state or something equally distasteful.

There's not much else for them to do, since they don't take arguments, but to muck about with global state and/or have side effects.


> But I'm assuming that the functions aren't mucking about with global state or something equally distasteful.

It seems fairly obvious to me that either the former example is waaaay simpler or it is doing something really distasteful.


Some designs don't require your own code to pass around state between functions, because each function can handle state in its own way without dependence on other functions. I've written a lot of such simple scripts where I just need to execute a series of tasks that aren't necessarily related to one another, but do follow each other.


I hate unnecessary functions with a passion and it's refreshing to hear his opinion.

Sometimes a big, fat block of code is just easier to understand.


> Sometimes a big, fat block of code is just easier to understand.

John Carmack made once the same comment: http://number-none.com/blow/blog/programming/2014/09/26/carm...


> I don’t think that purely functional programming writ large is a pragmatic development plan, because it makes for very obscure code

As a functional programming enthusiast, I agree with this.

Recently I worked on rewriting a relatively large backbone web application in React/Redux (For those that don't know, Redux is a framework for writing JS in a more functional style).

While moving all app logic to functional style makes it safer and easily testable, it is definitely not friendly to most programmers that maintain it. We've had lots of bugs caused by new people coming in, not understanding the functional style and making bad changes such:

- Doing side effects from functions that are assumed to be pure.

- Writing a lot of logic into functions that are directly doing side effects, instead of refactoring the decision making into reducers.

While I still love mostly pure functional code, I would only recommend it for smaller projects, where one or few developers with strong grasp of the style would strictly review every new PR to ensure new people don't mess up.


You're not wrong, but I don't really think this is the fault of FP.

I had the exact same experience with people not understanding new paradigms with Procedural (programmers experienced with Assembly using GOTO for everything), OOP (programmers used to procedural using only static methods), MVC (by putting everything in the controllers and ignoring views/modules/helpers), MVVM (by modifying state by themselves instead of using the MVVM mechanism).

All those things were always "obscure" for newcomers since it was different from what they learned in college, but after a while they became second nature.

I think the answer is not to avoid those paradigms because they're hard, but rather to teach people how to work with them. It's expensive but it's only way forward IMO.


> Doing side effects from functions that are assumed to be pure.

That completely goes away as soon as your language has purity declared at the type system.

It's just not available for Javascript development, like most nice FP features.


That's because JS is not a functional language. You are enforcing a functional style by discipline. Additionally a huge portion of benefits of the functional style are lost on untyped languages. If you're not using typescript you're dealing with a lot of unnecessary bugs.

If you guys moved to a fully functional paradigm where the language enforces the functional style you will see greater benefits.

Unfortunately for the front end the ecosystem for functional languages outside of TS/JS is not that great. But an easy one to try out to actually see the benefits I recommend writing a little app with ELM. With something like ELM, the functional style is enforced by the compiler. You will see that 90% of the bugs you typically deal with in JS/TS will disappear. One type of bug that will disappear is runtime errors. Runtime errors are not possible in ELM.

I find a lot of JS programmers haven't fully grokked the functional style. Example: You'll find JS programmers who talk about how much they like functional programming but don't understand why for loops don't exist in functional programming.


I haven't read that in awhile. But the argument was around optimisation rather how easy it is to understand.


The way I read the article, I believe his main point was to make reasoning about state easier, even at a small cost to performance, making code easier to test and debug.


Not really, Carmack explicitly states that this is not a performance optimisation in the article:

> In no way, shape, or form am I making a case that avoiding function calls alone directly helps performance.

Rather, he argues that (among other things) this is a way to make current bugs more visible and to avoid future bugs by disallowing calling functions that should be inlined.


Very insightful. Can I read more John Carmack's letters somewhere?


Took me a while to find something useful, but here you are:

https://fabiensanglard.net/fd_proxy/doom3/pdfs/


Wow that's a lot of interviews and letters. Cheers


If you have a well documented system, OWNED by a software architect, then breaking it down is better.

If you don't have that, at least big fat blocks of code are self documenting...


Thank you for this.

It has been a strongly held opinion of mine for a very long time, but I haven't been able to state it as eloquently as that.


I love his series. Eye opener for beginner coders.


Agreed. IMO it's an eye opener for experienced coders too.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: