6 ms·
It's a shame that the post has been taken down, as there were some good criticisms there, and I suspect this comes from dealing with a larger codebase. I work
by phillipcarter 9y ago
It's a shame that the post has been taken down, as there were some good criticisms there, and I suspect this comes from dealing with a larger codebase. I work on F# (similar to OCAML) at Microsoft. I learned a few things that made my life easier:
* Annotating your parameter types is a life saver with large, complicated codebases. No type annotations is cute and convenient in the short-term, but horrible when you need to understand some code that sorta doesn't work 6 months later.
* Write stupid code. I recently had to muck about in some old code that called into a function that called into a function created from another function that produced a 5-tuple of partially evaluated functions. This function called into another partially evaluated function, and then another, until it crossed an assembly boundary. Clever code like that is impossible to understand if you didn't write it yourself. Luckily, languages like F# and OCAML make it really easy to write stupid code.
* Write code that makes tooling "light up". This is usually also stupid code.
There are ways you can write F# code that make any kind of IDE tooling useless. It's not fun to deal with.
* Purity is overrated. Immutability isn't.
- hammerandtongs 9y agoI've never been attracted to the idea of partial application. Without it a function is either in state 0 or 1. It's not called or it has been called. With partial application in the mix, functions are in an almost indeterminate state (to the programmer typing at the keyboard even if some compilers know). In functional styles, programmers are encouraged to "just pass a function". The f(x,y,z) gets passed around in an unknown state of f(maybe needs x, maybe needs y, maybe needs z). This would be complicated even if it was the same function with a declining arity as it was passed around. Several languages allow named arguments which just adds to the signature complexity. It's hard to see the benefits of the complexification. Most pedagogical examples of the use occur in tight loops where the complexity isn't obvious. Once a function is passed outside of that tight loop I wonder at it's actually utility vs the negative effects.
- danield9tqh 9y agoYea I totally agree, partial function application certainly complicates things. Instead of `called` vs `not called` like you said, a function can have any number of states depending on its number of arguments. I will give you my two cents on its benefits though and let me know if you agree or disagree. I would compare it to taking a step up on the ladder of abstraction. Computers are powerful because they are programmable. If we take the example of a value like an integer, in a computer we can make it anything we want (0, 1, 2, 3, 4 etc). Functions are how we program a value. We say for example speed(distance, time) { return distance / time; } The value of the speed depends on these two variables (distance and time) which are programmable. Partial function application is just a continuation of this idea. It's obvious that `values` are programmable, but why can't functions be programmable as well? After all, we gained a lot of power from letting values be programmable maybe the same will be true for functions? Using the previous example lets say we want a function to calculate the speed of runners in the 100m dash. We could write a new function for this speedOf100YardDash(time) { return 100 / time; } but we already have logic for calculating the speed and we don't want to reuse it. So the idea of having a `programmable` speed function starts to look a little better. speedOf100YardDash = speed(where `distance` = 100) Obviously, this plays into your criticism of 'pedagogical examples' being simple, but I think the idea is even more valuable with more complicated code. Why? Because with simple examples it's easy it duplicate code without much consequence. The value of speed and division are not changing anytime soon. If we have more complex functions, however, this is where we absolutely want to avoid code duplication. Because if we write a complex function multiple times it's more likely that one of those implementations will be written differently or eventually diverge from the other one. What do you think?
- phillipcarter 9y agoI'm really into partial application when you wish to mock some data for testing, and the caller chain of that test is shallow. That is: * function under test takes in a data as its last parameter * partially applied version of this function is used in a test * testing function constructs fake data * testing function calls the partially applied version It's also nice for code reuse in libraries, but I'm generally less inclined to use it unless I know that the thing I'm working on has dead-simple requirements and won't change much.
- lgas 9y agoCan you elaborate on why you think purity is overrated? I find the destruction of local reasoning by impurity to be one of the biggest drawbacks of non-functional languages.
- phillipcarter 9y agoI think something that is impure when it needs to be is easier for me to maintain than something that really goes out of its way to be pure. Especially when the set of cases something should handle is large. On the other hand, I think that immutable data is rarely difficult to deal with, and saves my stupid self from all kinds of bugs that I'd probably write.
- imtringued 9y agoBecause this is not a black and white matter. Lack of purity doesn't imply lack of local reasoning. A program that is 10% impure and 90% pure is not considered pure yet is still 90% easier to understand than a 100% impure program.
- ModernMech 9y agoAt the same time, when a language is 100% pure you gain some interesting abilities, like being able to rewind and replay the state of your program, or to optimize code by replacing function calls with computed values (memoization).
- setzer22 9y agoWhat stops a 90%-pure language to dectect pure functions and do the exact same optimizations? I'm not saying that is decidable in all cases (can't come up with a meaningful counter-example either), but usually a 90% good enough solution is better than a 100% solution that makes you go out of your way to do very simple things (like print to stdout).
- ernst_klim 9y agoI think the opposite. I feel that OOP had become such a mess exactly because of those ``compromises'' which were made in mainstream OOP languages (i.e. java, c++ etc). I expect the same from mainstream FP implementations. Such ``compromises'' just ruin the whole carefully elaborated framework of the paradigm introducing breaches, which would be filled with workarounds. And voila, meet new java.
- danield9tqh 9y agoAs a strong proponent of functional languages, I would have to agree these are some good criticisms. FP solves many problems it cannot solve is code readability. Readability will always be a problem no matter what paradigm you work in. This is why I both appreciated and disliked this article. It brings up many valid points about how FP can be (and is often) written illegibly. But on the flip side, don't throw the baby out with the bath water. Just make the functional code more readable. > Write stupid code Also really agree with this. As engineers our job is not just to write something that works, it's to write something that can also be read and understood by others. This is why simple solutions to problems are praised over complex solutions.
- yawaramin 9y agoRe: type annotations, it's a best practice in OCaml (and at least possible in F#) to provide interface files for your modules. The interface files capture all the type info and documentation, and the implementations stay succinct. Best of both worlds.