I enjoy reading and writing "Functional" code in a language where the ergonomics lend themselves to functional semantics.
That swift code is painful to read.
I'll give some examples (from worst to best.)
Java: (awful, to read and write)
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;
Function<Integer, Integer> addFive = add.apply(5);
int result = addFive.apply(3); // Output will be 8
}
Ruby a. (still not great, but there's many ways to do it!)
add = proc { |x, y| x + y }
curried_add = add.curry
add_five = curried_add.call(5)
result = add_five.call(3)
}
Ruby b. (slightly more concise)
add = ->(x) { ->(y) { x + y } }
add_five = add.(5)
result = add_five.(3)
Elixir (functional programing lang but doesn't lean on currying too much)
add = fn x -> (fn y -> x + y end) end
add_five = add.(5)
result = add_five.(3)
ES6 (say what you want about javascript but they at least did arrow functions right)
const add = x => y => x + y;
const addFive = add(5);
const result = addFive(3);
Standard ML (clearly a lang where currying is a first class concern in its language design)
fun add x y = x + y;
val addFive = add 5;
val result = addFive 3;
and finally Haskell (which, in this case is a joy to read and write.)
add x y = x + y
addFive = add 5
result = addFive 3
Java has (limited) type inference now I think, right? Does this work for the example you gave?
public class Main {
public static void main(String[] args) {
var add = x -> y -> x + y;
var addFive = add.apply(5);
int result = addFive.apply(3); // Output will be 8
}
}
Still not great, but a lot better than the original IMO
public class Main {
private static <T,R> Function<T,R> fun(Function<T,R> function) {
return function;
}
public static void main(String[] args) {
var add = fun(x -> fun(y -> x + y));
var addFive = add.apply(5);
int result = addFive.apply(3); // Output will be 8
}
}
No, I don’t think that works. Last time I checked, type inference doesn‘t work for lambda expressions.
Part of the reason - I think - is that lambda expressions need to be boiled down to some concrete @FunctionaInterface interface. You can have arbitrarily many of these, all with the same signature and method name, so language doesn‘t know which to choose.
When we talk about Currying I can grok these, but if I saw that ES6 example in the wild and FP isn’t on my mind I’d be so confused when reasoning about it, especially if it was more complex than adding.
I loved Ruby: an OO lang with some FP on top where it makes sense. I found Kotlin to be very similar to Ruby: primary on OO lang with FP on top where it makes sense, but now with compile-time types (slightly but fundamentally better than Java's) built in.
The main thing I miss in Kotlin is proper, first-class sum types. There is a trick with using inheritance to achieve it, but not good enough for me. They've become a fundamental tool for me (same level as record (data class) and collections).
I get how that would make Java-interop much harder, and that that's likely why they've been left out.
While I find these topics esoteric and very interesting in normal day to day code during a code review I’d probably say make it less complex, because majority of the time spent is reading code this makes code harder to read
Currying is pretty much just a convenient way to write a one-constructor one-method class. It's a parameterized version of the command pattern.
That said, if you're using a language with classes you may as well stick with those and omit the syntactic sugar from your diet.
I find functional programming useful though because it can shorten code in a way where you can get a better view of the big picture and easily make sweeping changes. But it's also easy to write in a way that's too "perfect" (i.e. resistant to change) and incomprehensible. Though I'm not sure the total incomprehensibility is generally any worse than an enterprise Java codebase -- local readability might only give you the illusion that the whole system is understandable. Still, if you don't enjoy reading code in a certain style it makes for hard reading!
This "looser" way of doing things tends to be more popular in the F# community, which isn't terribly fond of those rigid crystalline structures that you get in other corners of the FP community. Though I'd also point out that, even in Haskell, ways of programming that lead to incomprehensible and resistant-to-change code are often viewed as a sign of inexperience by more seasoned developers. There's a lot of truth in this joke: http://pages.cpsc.ucalgary.ca/~robin/class/449/Evolution.htm
> it’s a way of breaking down a complex function into smaller, simpler functions that can be composed together to achieve the same result.
And you could almost see it as analogous to the "fluent" pattern like:
GetObject()
.add(3)
.applyGradient()
.giveFish()
Etc.
On the side: I really hate the "make it less complex" PR comments because it's not complex, it's just hard to read, and that's usually because the reader doesn't know about them.
I have one guy in our company who wants to unravel any list comprehension or map/generators functions (python) because he doesn't know anything except "for X in Y"
I always think of Rich Hickey's "Simple Made Easy" [1]
At the time I had a coworker who was militant about "simple code" and anti "complexity". I used to argue that the code they preferred was more of a pain in the butt due to avoiding anything beyond the most basic of constructs. They saw that this was a talk, suggested our team watch it, and then became upset because it turned out they hadn't actually watched it and assumed based on the title that it'd back up their viewpoints.
Lowest common denominator coding isn't the same thing as avoiding complexity.
[0] abort()
[1] call-site 1
[2] call-site 2 <actual place of issue>
[3] call-site 3
Dealt with this enough at a previous company I’m glad on my team at FAANG people are reasonable and don’t just shoehorn functional paradigms unnecessarily
Complexity and understandability are very difficult to separate. If a coding style is unknown / bizarre to most coders on a team, but beautifully simple to a few who know the technique, which is it?
There should be a balance between teaching new ideas, and acknowledging the truth of what people are comfortable with.
I guess every language needs a variant of JavaScript: The Good Parts. Believe Go, despite its many shortcomings, gets readability better than most languages: https://go.dev/talks/2012/splash.article#TOC_4.
On the topic of Kotlin, it is really one is the better (perhaps the best?) JVM languages out there.
> I have one guy in our company who wants to unravel any list comprehension or map/generators functions (python) because he doesn't know anything except "for X in Y"
I'm that guy at my current place of work. Incidentally, I'm also always the guy called in to work out why something doesn't work when the guy who made it (and loved streams and CompletionStage) has moved on to a different job.
I can read and write generators and maps just fine (I do it all the time when writing little one off scripts), but debugging in that mess is a hell. When I write something that will outlive my tenure at an organization, I reach for what everyone knows. I write in a dialect that even somebody right out of programming 101 can understand, because god knows what sort of clown they're going to hire after me.
These are great when the methods are simple configuration, but it indeed sux whenever you are debugging something. Or when it gets just a little bit more complicated.
I don’t think currying is esoteric at all, and if someone told me in a code review that it’s “too complex” I’d start looking for a new job immediately, since it’s a career dead end if the reviewer doesn’t even understand simple functional programming.
That said sadly these days I’m working on smart contracts in Solidity which is object oriented, and the primary functional alternative Vyper has had major problems while Rust has proven to be slower to develop in, at least for cosmwasm chains (I still hold out hope for Rust). So you could have a point lol.
I think this illustrates well an issue I have with the whole clean code thing. Utilizing slightly obscure language features isn't unclean, some times they're the cleanest solution.
Some people may not know what currying is and be confused by the code, that's okay. They can ask or Google it and then they understand.
The important thing is that the system itself is clean, that code is organized in a way that makes sense so that everything has a natural place to be etc. Loose coupling is a big one.
People talk too much about shot that doesn't really matter like names or whatever. Sure they're important but that's like saying conditional statements are important in programming. Yes they're important but it's week 1 stuff. Learning to do good work takes years, we're way past naming and stuff.
> Improved type safety: Currying can help improve type safety by ensuring that each curried function takes exactly one argument of the correct type. This can help catch errors at compile time and make your code more robust.
Why would non-curried functions have worse type safety?
Passing around functions that have already been partially applied means that downstream users can't incorrectly call or accidentally modify the arguments which have already been applied. Whether this actually results in more safety is unclear, as there are other language features or API design that accomplishes the same thing.
Yeah I would think that downstream users would just expect a function that only takes in the params that they specify - leave it up to the API user to just make a closure on the spot.
If we're using the more formal/traditional definition of "type safe" of "won't crash or go into an undefined state due to runtime type errors", then I think that it doesn't actually change the type safety situation at all.
Assuming we're actually using the more common colloquial meaning of "helps prevent logical errors due to a failure of the program's types to properly convey semantic distinctions" rather than the more I actually think it's kind of the opposite. When you curry a function that takes multiple arguments of the same type but treats them differently (so, not something like +), it gets easier to lose track of which is which in code that makes heavy use of partial application. Without partial application, someone reading the code is more likely to see that it's being called with two arguments of the same type and think to double-check the documentation if they're unsure which is which.
I _think_ what they're saying is a specialized/curried function that takes a single argument is better than the method it's replacing that takes three strings or whatever. That is, `foo.a("a").b("b").c("c")` is better than `foo.a("a", "b", "c")`. Which...sure, but 1. if you're really passing 2-3 parameters in with identical types, maybe use inline value classes instead? Or at least type aliases?, and 2. if you can't or don't want to do that, named arguments help.
Type safe builders/factories work and should be used where appropriate (e.g. to catch invalid state at compile time) but I don't see how this has anything to do with the currying shown in the post. The compiler checks the param types whether the function has a single param or not.
I tend to agree. I'm using this in a personal project and when I go back to debug issues with the curried functions I find I am not as smart as I thought I was.
I'm always wondered why some people prefer to use a lambda in a variable instead of just a function.
I'm referring to using
var f = (a) -> code;
Instead of
function f(a){ code }
Which are exactly the same.
JavaScript (and typescript) prefers them. Is it because of a convention or it really has advantages?
Haskell (and other functional languages) also use them a lot, but I guess in these cases the language is designed around it.
Kotlin, Java and other languages with lambdas doesn't really benefit from them in my opinion. If it's a global variable, a function is just enough and 99% of the times preferred. If it is something local, either inline it or also use a function (Kotlin allows for inlined functions, Java doesn't; and maybe, if you need to capture variables, a local Function variable is the only instance where this is useful, but otherwise please just use a function. * )
Python and other languages with limited or not available lambdas do use functions, and there are rare cases where a lambda would help.
In general, if you are using language X try to use language X's features and best practices. Don't try to program as if you were using language Y, it will only make it more confusing.
You can easily see when someone is used to a language and tries to use another without understanding the differences. Going from java to python is a very clear example (classes everywhere! Loops just because!)
* In java, an initialized private Function<> field is 99% of the time useless and worse than just a function. And 99.99% if it is final.
Ah! Thanks. I always forget the special "this" differences. I still prefer to use function when "this" is not involved though (I find strange to have a function as a variable, if you are not using it as a variable that is.)
I think In JavaScript, it often comes down to the fact that you want to use arrow functions if you need to reference this, for instance when creating callbacks for events in a component class context. Since there are some use cases where it’s necessary, some people just use it all the time so as not to have to think about it. The only thing you lose is function hoisting.
Are you sure? Kotlin allows for one-line functions. This is valid Kotlin:
fun sum(a:Int, b:Int) = a+b
In fact, it is even shorter than the lambda equivalent:
val sum = {a:Int, b:Int -> a+b }
Even so, if you still need a block body but dislike the mandatory return, you can use a run body (where the last value will automatically be returned).
fun sum(a:Int, b:Int) = run {
doThings()
a+b
}
And even in that case you can use let, apply, and all the other syntactic sugar to allow beautiful flows and one-liners
I've used currying in the past but these articles don't really make you tick at all as they introduce the concept first, which doesn't mean anything until you realize you actually need it to solve a problem you may encounter but examples also doesn't really feel like it's usable in day to day code.
I've used it to keep specific variable assigned on a function for later use such as for asynchronous functions which you'll lose the current state without saving it somehow such as by currying.
Java: (awful, to read and write)
Ruby a. (still not great, but there's many ways to do it!) Ruby b. (slightly more concise) Elixir (functional programing lang but doesn't lean on currying too much) ES6 (say what you want about javascript but they at least did arrow functions right) Standard ML (clearly a lang where currying is a first class concern in its language design) and finally Haskell (which, in this case is a joy to read and write.)Part of the reason - I think - is that lambda expressions need to be boiled down to some concrete @FunctionaInterface interface. You can have arbitrarily many of these, all with the same signature and method name, so language doesn‘t know which to choose.
Haskell better be good at Currying since it’s named after https://en.m.wikipedia.org/wiki/Haskell_Curry
I you mean the Swift code in the article, that almost looks like Kotlin :-)
Swift requires type constraints on the original add function, but infers them for addFive.
Edit: in Swift 3, one could write and call that as or, in two steps, as That functionality was removed, though. For reasoning behind that, see https://github.com/apple/swift-evolution/blob/main/proposals....(Swift for currying, based on your example, isn't terrible actually!)
The main thing I miss in Kotlin is proper, first-class sum types. There is a trick with using inheritance to achieve it, but not good enough for me. They've become a fundamental tool for me (same level as record (data class) and collections).
I get how that would make Java-interop much harder, and that that's likely why they've been left out.
add = x: y: x + y addFive = add 5 result = addFive 3
(let [add-five (partial + 5)] (add-five 3))
The add-five function can take any number of arguments, including 0.
That said, if you're using a language with classes you may as well stick with those and omit the syntactic sugar from your diet.
I find functional programming useful though because it can shorten code in a way where you can get a better view of the big picture and easily make sweeping changes. But it's also easy to write in a way that's too "perfect" (i.e. resistant to change) and incomprehensible. Though I'm not sure the total incomprehensibility is generally any worse than an enterprise Java codebase -- local readability might only give you the illusion that the whole system is understandable. Still, if you don't enjoy reading code in a certain style it makes for hard reading!
Here's a nice talk on the subject: https://www.youtube.com/watch?v=sfYA0HCWgqQ
and slides: https://fsharpforfunandprofit.com/pipeline/
This "looser" way of doing things tends to be more popular in the F# community, which isn't terribly fond of those rigid crystalline structures that you get in other corners of the FP community. Though I'd also point out that, even in Haskell, ways of programming that lead to incomprehensible and resistant-to-change code are often viewed as a sign of inexperience by more seasoned developers. There's a lot of truth in this joke: http://pages.cpsc.ucalgary.ca/~robin/class/449/Evolution.htm
Deleted Comment
> it’s a way of breaking down a complex function into smaller, simpler functions that can be composed together to achieve the same result.
And you could almost see it as analogous to the "fluent" pattern like:
Etc. On the side: I really hate the "make it less complex" PR comments because it's not complex, it's just hard to read, and that's usually because the reader doesn't know about them.I have one guy in our company who wants to unravel any list comprehension or map/generators functions (python) because he doesn't know anything except "for X in Y"
At the time I had a coworker who was militant about "simple code" and anti "complexity". I used to argue that the code they preferred was more of a pain in the butt due to avoiding anything beyond the most basic of constructs. They saw that this was a talk, suggested our team watch it, and then became upset because it turned out they hadn't actually watched it and assumed based on the title that it'd back up their viewpoints.
Lowest common denominator coding isn't the same thing as avoiding complexity.
[0] abort() [1] curry call site [2] partial closure 1 [3] partial closure 2 [4] partial closure 3 [5] partial closure 4 [6] <actual place of issue> [7] partial closure 5 [8] partial closure 6
Compare this to something isn’t using currying:
[0] abort() [1] call-site 1 [2] call-site 2 <actual place of issue> [3] call-site 3
Dealt with this enough at a previous company I’m glad on my team at FAANG people are reasonable and don’t just shoehorn functional paradigms unnecessarily
There should be a balance between teaching new ideas, and acknowledging the truth of what people are comfortable with.
On the topic of Kotlin, it is really one is the better (perhaps the best?) JVM languages out there.
I'm that guy at my current place of work. Incidentally, I'm also always the guy called in to work out why something doesn't work when the guy who made it (and loved streams and CompletionStage) has moved on to a different job.
I can read and write generators and maps just fine (I do it all the time when writing little one off scripts), but debugging in that mess is a hell. When I write something that will outlive my tenure at an organization, I reach for what everyone knows. I write in a dialect that even somebody right out of programming 101 can understand, because god knows what sort of clown they're going to hire after me.
That said sadly these days I’m working on smart contracts in Solidity which is object oriented, and the primary functional alternative Vyper has had major problems while Rust has proven to be slower to develop in, at least for cosmwasm chains (I still hold out hope for Rust). So you could have a point lol.
Some people may not know what currying is and be confused by the code, that's okay. They can ask or Google it and then they understand.
The important thing is that the system itself is clean, that code is organized in a way that makes sense so that everything has a natural place to be etc. Loose coupling is a big one.
People talk too much about shot that doesn't really matter like names or whatever. Sure they're important but that's like saying conditional statements are important in programming. Yes they're important but it's week 1 stuff. Learning to do good work takes years, we're way past naming and stuff.
> Improved type safety: Currying can help improve type safety by ensuring that each curried function takes exactly one argument of the correct type. This can help catch errors at compile time and make your code more robust.
Why would non-curried functions have worse type safety?
Assuming we're actually using the more common colloquial meaning of "helps prevent logical errors due to a failure of the program's types to properly convey semantic distinctions" rather than the more I actually think it's kind of the opposite. When you curry a function that takes multiple arguments of the same type but treats them differently (so, not something like +), it gets easier to lose track of which is which in code that makes heavy use of partial application. Without partial application, someone reading the code is more likely to see that it's being called with two arguments of the same type and think to double-check the documentation if they're unsure which is which.
I'm referring to using
Instead of Which are exactly the same.JavaScript (and typescript) prefers them. Is it because of a convention or it really has advantages?
Haskell (and other functional languages) also use them a lot, but I guess in these cases the language is designed around it.
Kotlin, Java and other languages with lambdas doesn't really benefit from them in my opinion. If it's a global variable, a function is just enough and 99% of the times preferred. If it is something local, either inline it or also use a function (Kotlin allows for inlined functions, Java doesn't; and maybe, if you need to capture variables, a local Function variable is the only instance where this is useful, but otherwise please just use a function. * )
Python and other languages with limited or not available lambdas do use functions, and there are rare cases where a lambda would help.
In general, if you are using language X try to use language X's features and best practices. Don't try to program as if you were using language Y, it will only make it more confusing. You can easily see when someone is used to a language and tries to use another without understanding the differences. Going from java to python is a very clear example (classes everywhere! Loops just because!)
* In java, an initialized private Function<> field is 99% of the time useless and worse than just a function. And 99.99% if it is final.
There are some slight differences: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I believe the reason arrow functions are prefered in his is because of the way `this` is handled: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
(I don't know why they made return mandatory in Kotlin, as a language can function just fine without explicitly writing return everywhere.)
I've used it to keep specific variable assigned on a function for later use such as for asynchronous functions which you'll lose the current state without saving it somehow such as by currying.
Deleted Comment
http://cromwellian.blogspot.com/2006/07/fun-with-generics-an...