3 ms·
Currying is just another example of poor abstraction. You have a function which returns another function which may be passed around to a different part of the c
by cryptica 7y ago
Currying is just another example of poor abstraction. You have a function which returns another function which may be passed around to a different part of the code and then called and it returns another function... Abstraction doesn't get any leakier than this. It literally encourages spaghetti code. I despise this aspect of FP that the code ends up passed around all over the place and keeping track of what came from where is a nightmare.
I've written plenty of very short OOP programs. They don't have to be huge to be effective. The reason why you sometimes see very large OOP software and rarely see large FP software is not because FP makes code shorter, it's because FP logic would become impossible to follow beyond a certain size.
My point about black box and referential transparency is that a black box hides/encapsulates state changes (mutations) by containing the state. Referential transparency prevents your function from hiding/encapsulating state changes (mutations) and thus it prevents functions from containing the state which is relevant to them; instead, the relevant state needs to be passed in from some (usually) far-flung outside part of the code... A part of the code which has nothing to do with the business domain which that state is about. To make proper black boxes, state needs to be encapsulated by the logic which mutates it.
- jolux 7y agoCan you explain why you see currying as an abstraction leakage? What implementation details does it betray? I haven’t found a single large OOP program in the line of business that was easy to understand. Quite contrary to my experience with large FP code bases, of which many exist, to be clear. They are just a lot smaller than what equivalent OOP code would look like, and I challenge you to refute that with evidence. I completely disagree about black boxes and think they are actually a complete scourge on software engineering. I should know everything that is relevant to me from a function’s type signature. In languages with pervasive side effects, this is not possible.
- cryptica 7y agoI thought my example about being able to call a function to get another function and then passing it to some other part of the code and calling it there was enough to illustrate the kind of confusion and disorganization that currying can cause. For me, the most important principles of software engineering are: 1. Black boxing (in terms of exposing a simple interface for achieving some results and whose implementation is irrelevant). 2. Separation of concerns (in terms of business concerns; these are the ones that can be described in plain language to a non-technical person) You need these two principles to design effective abstractions. You can design abstractions without following these principles, but they will not be useful abstractions. Black boxes are a huge part of our lives. If I want to go on a holiday to a different country, I don't need to know anything about how the internet works, how houses are built or how airplanes work in order to book a unit in a foreign country on AirBnB and fly there. The complexity and amount of detail which is abstracted is unfathomable but absolutely necessary to get the desired results. The complexity is not just abstracted from the users, but even the engineers who built all these different components knew literally nothing about each other's work. As a user, the enormous complexity behind achieving my goal is hidden away behind very simple interfaces such as an intuitive website UI, train tickets, plane tickets, passport control, maps for location, house keys. These interfaces are highly interoperable and can be combined in many ways to achieve an almost limitless number of goals. I couldn't explain to anyone anything about how airplanes work but I could easily explain to them how to use a plane ticket to go to a different country. With programming, it should be the same. The interfaces should be easy to explain to any regular junior developer.
- jolux 7y ago>I thought my example about being able to call a function to get another function and then passing it to some other part of the code and calling it there was enough to illustrate the kind of confusion and disorganization that currying can cause. I generally think of spaghetti code as code that has unclear control flow (e.g. GOTOs everywhere, too many instance variables being used to maintain global state, etc.) Currying, plainly, does not cause this. >1. Black boxing (in terms of exposing a simple interface for achieving some results and whose implementation is irrelevant). Sure, completely possible in ML-family languages and Haskell. Refer to what I said about opaque types earlier. >2. Separation of concerns (in terms of business concerns; these are the ones that can be described in plain language to a non-technical person) Again, nothing in functional languages betrays this. You are talking about code organization at scale, and none of what you have said so far is precluded by using pure functions and modules and such. >If I want to go on a holiday to a different country, I don't need to know anything about how the internet works, how houses are built or how airplanes work in order to book a unit in a foreign country on AirBnB and fly there. The complexity and amount of detail which is abstracted is unfathomable but absolutely necessary to get the desired results. The complexity is not just abstracted from the users, but even the engineers who built all these different components knew literally nothing about each other's work. I do not like analogies in general, though for this one I will suggest that you should at least know what the baseline social expectations are of the place you are traveling to. That is, plainly, what I am arguing that functional programming makes clearer and easier to deal with. >As a user, the enormous complexity behind achieving my goal is hidden away behind very simple interfaces such as an intuitive website UI, train tickets, plane tickets, passport control, maps for location, house keys. These interfaces are highly interoperable and can be combined in many ways to achieve an almost limitless number of goals. Yes, and underneath that program in a functional programming language are lots of small, carefully composed functions that are often just as applicable to many other problems and problem domains. >I couldn't explain to anyone anything about how airplanes work but I could easily explain to them how to use a plane ticket to go to a different country. This is why I don't like analogies. I have no idea what you are talking about here. >With programming, it should be the same. The interfaces should be easy to explain to any regular junior developer. What makes functionally-styled APIs hard to explain to a junior developer?
- crimsonalucard 7y agoIt's not abstraction leakage imo. But it's generally not good to use this feature excessively in FP. First off it's equivalent to instantiation in OOP. An FP program that passes along functions as first class all over the place is similar to an OOP program that is passing along Objects all over the place. Closures contain the concept of state and method just like an object. If you use the pattern of passing closures everywhere you are essentially mimicking the complexity of an OOP program and you will encounter the same problems.
- jolux 7y agoThis is true, but it’s also a lot harder to accumulate that amount of messy state with pure functions. Besides, most of the power is in a few functions, and I’m mostly talking about curried and composed forms of fold and map which are very useful.
- crimsonalucard 7y agoThen don't use it. Not all functional programs force you to use currying and anonymous functions. I'm a functional programmer and I agree that passing around functions as first class can get kind of messy. Don't do it. Data is data and functions are functions, pass data into the pipeline not functions. But if you pass an object into a pipeline (which OOP forces you to do) it's 10x worse. Keep in mind that in OOP is essentially Forced currying. A method that returns an object full of other methods is identical to currying except that method isn't returning a single function... It's returning a group of functions that all rely on shared state... way more complicated.