5 ms·
There's a trade-off. Functional programs are easier to read, because the structure makes the state transitions and dependencies obvious - you see your dependen
by ajuc 2y ago
There's a trade-off.
Functional programs are easier to read, because the structure makes the state transitions and dependencies obvious - you see your dependencies in the arguments list. But it forces you to basically rewrite big parts of your program after even very simple changes.
You had the structure of the program so fine-tuned to the dependencies of every part of the code - that when any dependencies change you have to completely change that structure. It's rewrite-only programming style.
Imperative (and OO) programming idiomatically let you do a bigger mess with side effects, and you know less about the data dependencies just from looking at the function specifications - but it also allows you to do exploratory programming much faster (no need to pass a new argument down 20 levels of your call stack when some code deep down suddenly requires a new argument). And it allows you to modify the behaviour locally without refactoring the whole thing constantly.
If you have a for loop that filters out even numbers and suddenly you want to sum the numbers and find maximum and minimum too - most of the code stays the same.
If you have functional code doing the same and want to modify it in similar manner - it's a completely different code. Most people would just rewrite it from scratch.
And that's just a very small scale example. With larger programs the rewrite gets harder.
That's why big programs are almost never functional.
- ninetyninenine 2y ago>Functional programs are easier to read, because the structure makes the state transitions and dependencies obvious - you see your dependencies in the arguments list. But it forces you to basically rewrite big parts of your program after even very simple changes. Disagree. Readability is opinionated so I won't address that but this is an example of functional: gg = (x) => x * x * x y = 1 a = (x) => x + 1 b = (x) => x * 2 c = (x) => x * x d = (x) => x - 4 f = a . b . c . d result = f(y) OOP example: class Domain1 constructor(n: int) this.x = n def gg() this.x *= this.x * this.x def getX() -> int return this.x class Domain2: constructor(n: int) this.x = n def a: this.x += 1 def b: this.x *= 2 def c: this.x *= this.x def d: this.x -= 4 def f: this.a() this.b() this.c() this.d() def getX() -> int return this.x state = Domain(1) state.f() result = state.getX() What if realize d and a fits better in Domain1 and I want to compose d and a with gg in the OOP program? I have to refactor Domain2 and Domain1. Or I create a Domain3 that includes a Domain1 and a Domain2 How do I do it in functional programming? domain3 = gg . d . a #Note I use the fucntion composition operator which means: a . b = (x) => b(a(x)) or a . b . c = (x) => c(b(a(x))) Functions by nature with the right types are composable without modification. A can compose with B without A knowing about B or vice versa. The same cannot be said for objects. Try achieving the same goal with OOP.... It will be a mess of instantiating state and objects within objects and refactoring your classes. OOP is NOT modular. It's pretty clear. One style is more modular than the other. Objects tie methods to state such that the methods are tied to each other and can't be composed without instantiating state or doing complex Object compositions and rewriting the Objects themselves. In programming you want legos. You want legos to compose. You don't want legos that don't fit such that you have to break the legos or glue them together. >Imperative (and OO) programming idiomatically let you do a bigger mess with side effects, and you know less about the data dependencies just from looking at the function specifications - but it also allows you to do exploratory programming much faster (no need to pass a new argument down 20 levels of your call stack when some code deep down suddenly requires a new argument). And it allows you to modify the behaviour locally without refactoring the whole thing constantly. you shouldn't be programming with state ever if you're doing FP. You need to segregate state away from your program as much as possible. State by nature is hard to modularize. State should be very simple generic mutation operations like getValue and setValue, it should rarely ever contain operational logic.
- ajuc 2y agoYour examples are inherently functional, and in practice people would do ABCDService that just returns the result. But ignoring that - how is the code changing when you need to call some external API from the c(x) function and handle the credentials, session, errors etc? Real world has external state and we do need to work with it. > you shouldn't be programming with state ever if you're doing FP. You need to segregate state away from your program as much as possible. State by nature is hard to modularize. State should be very simple generic mutation operations like getValue and setValue, it should rarely ever contain operational logic. This approach to state management is exactly what is causing the need to rewrite almost everything when requirements change in a functional program. I like how clean FP code is when it's done. But I hate writing FP code when I'm not 100% sure what needs to be done and what might change in the future. If I could write imperative code with side effects and once I'm done have it transpiled into efficient, elegant, minimized state functional code - that would be great. Maybe it will happen at some point with AI getting better.
- ninetyninenine 2y ago> Your examples are inherently functional, and in practice people would do ABCDService that just returns the result. But ignoring that - how is the code changing when you need to call some external API from the c(x) function and handle the credentials, session, errors etc? Real world has external state and we do need to work with it. Abcdservice is bad because I want to use a b c and d in different contexts. You’re saying in the real world oop promotes a style where you can’t break down your code into legos. With oop you need to glue a b c and d together. My code is not inherently functional. I literally picked the smallest possible logical operations and interpreted them as either functional or oop. And then I tried to compose the logical operations. I mean look at it. A b and c are just one or two mathematical operators. If you’re saying this is inherently functional then your saying computing at its most primitive state is inherently functional. > This approach to state management is exactly what is causing the need to rewrite almost everything when requirements change in a functional program. Not true. Fp programs segregate state. Look at my code. All the code for fp is stateless. The only state is y=1. > But I hate writing FP code when I'm not 100% sure what needs to be done and what might change in the future. You hate what’s inherently better for the future. Fp is more modular and therefore more adaptable for the future. You hate it because you don’t get it.
- ninetyninenine 2y ago>If you have a for loop that filters out even numbers and suddenly you want to sum the numbers and find maximum and minimum too - most of the code stays the same. x = [1,2,3,4,5,6,7,8] even = [i for i in x if x%2 == 0] Now I want to sum the numbers and add maximum and minimum too. s = max(x) + min(x) res = reduce(lambda acc, y: acc + y, x, 0) I achieved your desired goal without rewriting code? The thing is with functional programming all state is immutable, so you can always access intermediary state without modification of the program at all. It's an improvement on imperative and OO. Because I only needed to add additional code to achieve the additional goal and those additions are modular and moveable. With imperative I would be changing the code and changing the nature of the original logic and none of it is modular and all of it is tightly integrated.
- ajuc 2y agoSure, if you want to have 4 loops where 1 suffices.
- ninetyninenine 2y agoThe big oh is the same man. It just feels like it’s less efficient but it’s not. Think about it. And it’s more modular with more loops. If you’re trying to shove 4 different operations into one loop you’re not programming modularly and you’re trying to take shortcuts.
- ajuc 2y agoReduce FPS of your game from 60 to 15, tell players it's the same cause complexity haven't changed. But it's not even mainly about performance. The structure of the code changes with every requirement change. In a non-artificial code you're doing stuff other than calculating the result, and all the associated state and dependencies now have to be passed to 4 different loops. While in the ugly non-modular imperative code you add 3 local variables and you're done, everything outside that innermost loop stays exactly the same. > you’re trying to take shortcuts Yes, that's the point. I started by admiting FP code is more elegant. But shortcuts are not inherently worse than elegance. They are just the opposite sides of a trade-off.