[HN Gopher] Currying in JavaScript
___________________________________________________________________
Currying in JavaScript
Author : corentin88
Score : 134 points
Date : 2021-09-19 10:02 UTC (12 hours ago)
(HTM) web link (javascript.info)
(TXT) w3m dump (javascript.info)
| anoctopus wrote:
| Automatic, implicit currying is the Haskell feature that most
| surprised me with how much I like having it. It makes taking
| apart and composing functions so much nicer, by making a very
| common case for partial application syntactically and mentally
| cheap.
|
| Trying to use currying in languages where it isn't built in makes
| no sense to me. It doesn't seem to compare well against doing the
| obvious partial application with a lambda. And it doesn't play
| well with variadic functions, default arguments, etc. (I have
| ideas for automatic currying of record types that I hope to
| explore in a toy language at some point, but that only does so
| much.)
| ufo wrote:
| In my experience, currying is problematic in dynamically typed
| languages. If you accidentally pass less arguments than you
| should, then you'll only detect the error much later, when you
| try to use the result and find that it is a partially-applied
| function instead of whatever you were expecting.
|
| A question for the Javascript experts: is there a way to set a
| property in the returned function, saying in what line of code it
| was created? Perhaps something like the __LINE__ macro from C...
| graftak wrote:
| Currying is nice but in practice it's rather esoteric in the
| JavaScript ecosystem. I've only seen currying being used (if at
| all) when transforming data with utility libraries like ramda[1].
| What makes it worse it that partial application is rather
| cumbersome to type.
|
| Lucky for us there is a partial application proposal, albeit
| still in stage 1, which makes partial application--to make
| functions curried--a lot easier[2].
|
| [1] https://ramdajs.com/docs/#curry
|
| [2] https://github.com/tc39/proposal-partial-application
| renke1 wrote:
| The partial application proposal looks reasonable and
| intuitive. Definitely something I would use. Is there any prior
| art (other languages etc.)?
|
| Mh, it's already 4 years old. What a shame.
| weego wrote:
| Scala has partially applied functions that use a very similar
| idiom
| alexfoxgill wrote:
| It is used heavily in Scala
| https://alvinalexander.com/scala/how-to-use-partially-
| applie...
| masklinn wrote:
| Clojure has the "thread-as" macro (`as->`), but I don't
| think it's used much: usually thread-first (`->`) and
| thread-last (`->>`) will more than do the job and have less
| mental overhead due to their regularity.
| graftak wrote:
| There's a lot of bikeschedding on how to use it in
| conjunction with the (also stage 1 proposal and forever
| delayed) pipeline operator[1] unfortunately. Add in never
| ending arguments on which symbol we should use (the
| questionmark has a lot of responsibilities already) and here
| we are.
|
| To make matters worse it appear that generally OOP style
| darlings such as static class blocks and true private fields
| seem to gain more momentum.
|
| [1] https://github.com/tc39/proposal-pipeline-operator/wiki
| runarberg wrote:
| I have strong doubts that partial application will ever advance
| from stage 1. The reason is that partial application was
| supposed to compliment the pipeline operator[1].
| value |> fn(?, "static string")
|
| However earlier this month they advanced a version of the
| pipeline operator with a definite topic marker which makes
| partial application useless in the RHS of the pipeline
| operator. This was of course really controversial, as you can
| see if you read thought the most resent issues (particularly
| the closed ones).
|
| https://github.com/tc39/proposal-pipeline-operator
| eurasiantiger wrote:
| Note that currying, especially with Ramda, has a very large
| negative performance impact.
|
| " To improve the compositionality of our code we had defined a
| ton of curried functions using Ramda. But every abstraction has
| a cost associated with it. On profiling, we found out that the
| `curry` function was taking up a lot of CPU. Benchmarks show
| that removing curry from the code makes it up to 100 times
| faster. Curry function is at the core of Ramda, almost every
| function is curried."
|
| https://blog.dream11engineering.com/lessons-learned-from-run...
| mLuby wrote:
| It also pollutes the call stack, making stepping through
| functions that much more laborious.
| IceDane wrote:
| Wow, that is pretty awful.
|
| I'm a pretty big fan of strong, static type systems and FP in
| general, and I have been writing typescript professionally
| for a few years now.
|
| I decided a long time ago that trying to write Haskell in TS
| just isn't worth the effort.
|
| Adopting FP ideas such as pure functions, restricting side-
| effects and then otherwise just using the type system to
| model the flow of your program goes a very long way. You
| don't have to write Haskell in TS to reap most of the
| benefits.
| toastal wrote:
| You can also just _not_ use TypeScript if you want
| something more appropriate for functional programming
| styles. I know it 's not always practical to switch
| languages, but it's equally impractical to push
| unergonomic, unidiomatic styles on language not built for
| it. There are a lot of good options to compile an FP
| language to JavaScript, and frankly those languages deserve
| more mindshare than pressing so hard on fp-ts.
| khalilravanna wrote:
| What's unergonomic or unidiomatic about TS?
|
| As far as I understand it TS is supposed to follow as
| closely to JS as possible. I always saw it as "let's add
| types to JS" rather than a separate language. I'm not
| sure I've seen a "this is how you write TS vs JS" formal
| declaration from the TS team. Am I missing something?
| toastal wrote:
| Say you want first-class currying and function
| composition. You expect this coming from any functional
| language and now you don't have that ability without
| introducing another compiler or third-party library. Do
| you want algebraic data types and pattern matching? You
| can sort of fake it, but it's certainly uglier. Do you
| want managed IO or immutability? Well, you can code it by
| convention but it's hard to enforce it and many of the
| libraries you consume won't be following those
| conventions.
|
| In this sense, where these features are not the default
| and aren't first-class, will almost likely never be
| idiomatic because to TypeScript because there's too much
| friction compared to something from the ML families or
| the LISPs. In trying to add types to JavaScript, as a
| result you keep a lot of that same friction as
| JavaScript. I'm not saying it doesn't have a place--it's
| clearly super popular--but I do think it does not offer a
| good experience for someone looking for FP-style
| programming. It's really odd to see job postings with an
| FP team, Haskell on the back-end, and then TypeScript on
| the front-end... it just doesn't match, and there were
| other good options, even if it requires writing some
| libraries for these communities.
| brundolf wrote:
| Wow. Was that just from the extra function instances and
| closures being created?
| eurasiantiger wrote:
| The story doesn't tell, but looking at the Ramda source
| [1][2][3], it sure looks like it: each application of a
| curried function creates a new curried function on the fly.
| Didn't check deoptigate, but I'm willing to bet V8 doesn't
| optimize that well.
|
| 1:
| https://github.com/ramda/ramda/blob/master/source/curry.js
| 2: https://github.com/ramda/ramda/blob/master/source/intern
| al/_... 3: https://github.com/ramda/ramda/blob/master/sourc
| e/internal/_...
| thomas_moon wrote:
| In my experience, currying is usually helpful as a hack and is
| tolerable as long as you don't get carried away. One "curry" is
| fine. Two+ is really pushing it and is probably a signal that
| something should be refactored.
|
| A lot of people here asking "why". Only solid example of it being
| the more elegant solution (in my opinion) is on the front end
| with redux.
|
| Maybe you have a large amount of objects in state. Each object
| has many properties and is used by many components. If you are
| using an implementation pattern that predefines selectors, you
| don't want tons of different functions selecting one property
| each. The simpler way is a single selector that "picks" the
| property dynamically by currying the property name to the single,
| more general selector.
| tomxor wrote:
| I've actually grown to dislike currying, it seems like a neat
| idea when you start using it, but whenever I come across it in
| real life code it just results in layers and layers of yuk -
| making me want to scream "show me the fucking code", you can go
| through 100s of lines before seeing a single operator...
|
| I'm not a big fan of OOP either, the older I get the more I end
| up writing a cross between procedural and functional code with
| the least possible abstractions in the way - while also trying to
| find the smallest natural coupling between blocks of code.
|
| Patterns are very subjective, and they usually have a place where
| it's objectively good to use them - but I'm not sure about
| currying, it feels more like a hack for when you can't do what
| should be done to the underlying code.
| layer8 wrote:
| One aspect I don't like about currying (at least with the usual
| syntax) is that it privileges the first parameter of each
| function. As soon as you want to curry on the second parameter,
| you lose the "natural" currying syntax and have to fall back to
| e.g. `half x = \x -> div x 2` or something like `half = flip
| div 2`. That aspect, to me, makes currying nonuniform and
| awkward, as in many cases the first parameter doesn't have a
| particular conceptual distinction to single it out for
| currying. It's a bit similar to how I don't like Smalltalk's
| asymmetric syntax for commutative operations (e.g. `x add: y`).
| mLuby wrote:
| Agreed. I've come to view partial application as the original
| coder saying "I guarantee you'll never need to inspect or
| modify this parameter from now on." Obviously, that's rarely
| true in practice.
|
| What about partial application of _named_ parameters?
| josephcsible wrote:
| You can curry the second parameter without it being too ugly
| like this: half = (`div` 2)
|
| Also, I think the idea with curried languages is that you're
| supposed to write your functions with the parameter that
| people are most likely to want to curry first.
| layer8 wrote:
| Of course that's the mitigation strategy, but my point is
| that the usual currying syntax inherently special-cases the
| first parameter, and in x% of the situations that's not
| what you want. From a language-design point of view it just
| feels awkward.
| josephcsible wrote:
| I guess that's fair. What about Scala's _ syntax, though?
| Does it solve the problem?
| danlugo92 wrote:
| > I get the more I end up writing a cross between procedural
| and functional code with the least possible abstractions in the
| way - while also trying to find the smallest natural coupling
| between blocks of code.
|
| Same here. I think this is a correct approach, sprinkled with
| some reactive code here and there (prefer reactive over other
| abstractions such as 2-way binding et al).
| ItsMonkk wrote:
| Yep. I'm reminded of the advice "Favor Composition over
| Inheritence", which begs the question "When can we not use
| Composition and need to fall back down to Inheritence?" and it
| turns out the answer is that you can always use composition
| with the exception if you are relying on someone else's library
| that does it wrong and you can't change it.
|
| Dependency injection and currying both do the same thing, and
| they are both useless most of the time just like Inheritence.
| You want to avoid nesting as much as possible. We should be
| rejecting dependencies. We should be returning values and
| passing them through functional pipelines.
| drenvuk wrote:
| lmao, is this satire? We should be using whatever abstraction
| is easiest to reason about. Sometimes that's apeasing the
| primate brain with objects and things, sometimes it's shoving
| those things through a pipeline.
|
| Free yourself from the dogma, just do what makes sense.
| nlitened wrote:
| > We should be using whatever abstraction is easiest to
| reason about
|
| I dare mention Rich Hickey and his talk "Simple Made Easy",
| and postulate that we should be using whatever abstraction
| is _the simplest_ (not the easiest one) to reason about, to
| which the grand parent rightfully refers, as far as I
| understand.
|
| Objects are easy to many, but far from being objectively
| simple.
| drenvuk wrote:
| Rather than that I should've written, "we should use
| whatever abstraction makes the problem easiest to reason
| about".
|
| I have no preference for either fyi.
| ItsMonkk wrote:
| I would have linked the Principle of Least Power[0], but
| same idea. Rich certainly fleshes the idea out with more
| examples and I deeply respect his opinions.
|
| I can't find it but there once was a blog that expressed
| this as: Reddit is a site where you can talk about
| anything with the exception of a few banned topics.
| Voat(might have been another reddit clone?) is a much
| less popular site where you can talk about anything. What
| do they talk about on Voat? Only the few topics banned on
| reddit.
|
| While is strictly more powerful than for, for is strictly
| more powerful than foreach, foreach is strictly more
| powerful than map. And yet 95% of the time, the power in
| map is sufficient. Therefore 95% of the time you should
| use map. When you encounter a foreach, you should be
| expecting non-purity. When you encounter a while, you
| know that it's doing some recursive operation that
| requires that power.
|
| This let's you reason about it. This allows you to
| compose these less powerful things.
|
| [0]: https://blog.codinghorror.com/the-principle-of-
| least-power/
| monkeyfacebag wrote:
| I'm not following. How is dependency injection like
| inheritance and also useless?
| sfblah wrote:
| I think GP is saying that dependency injection is like
| currying, not that it's like inheritance.
| ItsMonkk wrote:
| "Favor composition over inheritence" is like "Favor
| pipelining over currying".
| monkeyfacebag wrote:
| I'm still not getting the relationship to dependency
| injection. What is pipelining?
| qsort wrote:
| > I've actually grown to dislike currying
|
| I believe this is a notational issue more than anything.
| Fixpoint style in languages like Haskell is very natural and
| intuitive, if you have to write a `curry` function like the
| article shows, you have already lost.
|
| It's much more convenient to `ad-hoc curry` with things like:
| const f = x => myFunc(someArg, x, someOtherArg); return
| myArr.map(f).whateverElse();
| masklinn wrote:
| Currying makes sense if haskell because it's a curried
| langage, so you may need the odd uncurry, then curry it _back
| to normal_.
|
| In uncurried langages, generic curring has few use cases.
| Partial application does, and some libraries may want to
| provide both curried and uncurried versions of some things,
| but generally "curry" itself is unhelpful.
| kortex wrote:
| What's the difference between currying and partial
| application/closures?
| masklinn wrote:
| Currying is the conversion of a n-ary function to a chain
| of n 1-ary functions. So if you have a function `(a, b,
| c) => d` (a 3-function which returns a result) and you
| curry it it becomes a => b => c => d (a 1-fn which
| returns a 1-fn which return a 1-fn which returns a
| result).
|
| Partial application is the application of an n-ary
| function to k arguments (k <= n, often but not
| necessarily l < n) without actually evaluating (calling)
| the function, so given the same `f = (a, b, c) => d`,
| `partial(f, 1, 2)` returns a function `c => d`.
|
| A closure is a function which "closes over" its lexical
| initialisation context and carries it along.
|
| A curried function is trivially partially applicable,
| just call it with less than `n` parameters and it'll
| yield a (also curried) function taking `n - k`
| parameters. It's useful to build a language on that
| principle, it's not really useful to provide that as a
| generic utility, because in most scenarios you'd rather
| just partially apply a function.
|
| A closure is a completely freeform tool so it can do more
| or less anything you want.
| brundolf wrote:
| The distinction sounds almost completely syntactic. I.e.
| foo(a, b) vs foo(a)(b). Is the former not just sugar for
| the latter?
| masklinn wrote:
| > Is the former not just sugar for the latter?
|
| Theoretically they're equivalent, but practically not
| really especially when you introduce more complicated
| "imperative" features e.g. default parameters or
| overloading.
|
| They also have API impacts e.g. because Haskell is
| curried, its HoFs are generally of the form `fn param`,
| that way you can trivially partially apply the callback
| and use the same partially applied function to different
| parameters.
|
| In method-based languages, there is no way to partially
| apply the callback, and in uncurried languages in general
| since partial application is a secondary concern you'll
| often see the callback last regardless so the non-
| callback parameters don't "get lost" at the tail end of a
| long callback.
| brundolf wrote:
| Ah that makes sense about default parameters and
| overloading. Hmm.
| kortex wrote:
| So partial takes an N-ary function and gives an M-ary
| function, while currying always turns an N-ary function
| into N unary functions, that is the main distinction?
| swman wrote:
| Ugh my team at work is obsessed with DDD for a service with two
| endpoints. Absolutely overkill and the levels of OO abstraction
| are terrible. If this were more complicated I'd understand,
| maybe I'm wrong in general. But writing a small Nodejs service
| in such a way is terrible IMO.
| neurotrace wrote:
| I've felt this same sort of pain but I don't blame currying.
| It's when currying and partial application is used for deep
| dependency injection with no easy route to finding the original
| function. I dabbled in writing an extension for F# that would
| trace back function calls to give you all of the possible
| original sources for a value. I think that would solve that
| particular problem.
|
| Currying and partial application are really nice when you have
| a pipeline operator, which is why the partial application and
| pipeline operator proposals are so intertwined.
| users |> List.map (fn user -> user.Email) |>
| sendEmail |> Result.mapError (fn _ -> "Error sending
| emails")
| okasaki wrote:
| I don't see why that's better than the good old for loop.
| errors = list() for user in users: if not
| sendEmail(user.email) errors.append("Error
| sending emails")
| HugoDaniel wrote:
| nice one, however shouldn't good old for loops be done in
| ALGOL instead?
| p2t2p wrote:
| For me, the functional/pipeline/call chain style changes
| perception from "how" to "what".
|
| Because I say "what" do to do without going into details of
| "how" I feel like I have spent less mental capacity reading
| this portion of code.
|
| However, it does require your transformations to be simple
| and composable, shove a giant multi-line lambda in there it
| yes, the loop will be better.
| neurotrace wrote:
| The example I gave was contrived. I frequently find myself
| modeling much of my logic as a series of data
| transformations. The more I can model logic like this, the
| easier it is to test.
|
| For what it's worth, your example doesn't map 1-to-1 to the
| intention of what I wrote. Doing it in a more imperative
| style, I'd have to write emails = list()
| for user in users: emails.push(user.email)
| res = sendEmail emails if res.IsError:
| Error("Error sending emails") else: res
|
| (excuse my pseudo Python)
| BiteCode_dev wrote:
| Excuses accepted. Imperative Python would really look
| like: try: return
| sendEmail(user.email for user in users) except
| IsError: Error("Error sending emails") # if
| it does something
|
| I agree FP pipelines are nice, but not everything needs
| it.
| p2t2p wrote:
| Oh, the lengths people go to avoid OOP. Java:
| users.stream() .map(User::getEmail)
| .map(Email::send) .map(res -> "Error sending
| email: " + res) .collect(toList())
| creata wrote:
| What's "object-oriented" about that code? I'm suspicious of
| vague labels like "OOP" and "FP", but even some of the Java
| docs call Java's streams "functional-style".
|
| https://docs.oracle.com/javase/8/docs/api/java/util/stream/
| p...
| y4mi wrote:
| the only issue with java in this context is that you'll
| need to create a new class/transfer object between each
| function call that does things.
|
| with elixir (the previous example) you'd likely use simple
| hashmaps or lists like {email: "mail"} or [:error, "reason
| for failure"].
|
| That doesn't sound like its amazing, but actually is in
| practice, because the whole language is written with that
| in mind (pattern matching to effectively do function/method
| overloading depending on the value of each argument for
| example)
|
| its very concise while still being very explicit. The same
| would be possible with java, but you'll need to create a
| lot of classes/interfaces/enums/boilerplate to facilitate
| it.
|
| i'd still prefer java any time at a dayjob though, because
| boring and dumb is generally better if you need to write
| code that's gonna be in use for decades... and likely going
| to be changed by a lot of people with varying levels of
| experience.
| nestorD wrote:
| Side note to say that the previous example is actually
| F#, a static typed language contrary to Elixir (to be
| fair Elixir was heavily inspired by F# and they look
| similar when only looking at snippets).
| qsort wrote:
| > the only issue with java in this context is that you'll
| need to create a new class/transfer object between each
| function call that does things
|
| No, in most cases you don't. The stream interface is
| mostly designed around simple Lists, Sets and Maps. It
| also interacts very well with the Collection framework
| (e.g. myHashmap.entrySet() yielding a set, etc.) which is
| part of the standard library.
|
| You can extend streams with custom collectors, but rarely
| if ever you need to define intermediate data structures.
| You do need to define initial and terminal structures,
| but I'd argue that's good practice regardless.
| y4mi wrote:
| > _No, in most cases you don 't_
|
| that lets you pass the result from each function to the
| next without explicitly stating what form they have, yes.
|
| You'll still be missing the conciseness/explicitness
| because the language isn't meant to be used like that and
| is missing necessary features in order to facilitate it
| such as pattern matching by the passed in values into
| function.
|
| to make a simple example, you could theoretically write
| the following pseudo-code def
| sendEmail({email}) do // send email end
| def sendEmail({id}) do // get email address
| sendEmail({email}) end sendEmail({id: 244})
|
| or to make sure your code stops executing if an error
| occurs (positional return values this time)
| [:ok, msg] = sendEmail(lkajsdf)
| qsort wrote:
| .collect(Collectors.groupingBy(...))
| y4mi wrote:
| that does something entirely different.
|
| maybe actually learn to program sometimes, then you will
| figure out what it does.
| z3t4 wrote:
| You usually want to send the e-mail in order, with a sleep in
| between to not overload the e-mail server, and stop at any
| error, so that you can fix the error and then continue from
| where the error was.
|
| An error could be a malformed e-mail address, or a timeout
| from the e-mail server.
|
| What you do not want is to send 10000 e-mail, then have an
| error like "Error sending emails", then re-run a few time,
| only to have some people receive 5 e-mail, and some people
| receive 0 e-mail.
| neurotrace wrote:
| I didn't think I had to make this clear but a lot of people
| seem to be getting stuck on the specifics of my example
| code. That example is doing nothing but showing a 5,000
| foot view of what pipelines can do. Please don't take my
| dumb example that was written early in the morning as The
| One True Way of processing data, sending emails, or
| handling errors. It's a horrible example of that
| z3t4 wrote:
| Simplified examples always look neat. Any code/syntax
| will look simple if it doesn't have async, state and
| error handling.
| neurotrace wrote:
| I can't be bothered to type all of it out on my phone but
| even adding in those pieces, it doesn't change it much.
| This is F# so you can easily manage the async via an
| async computation expression, the retry logic can be
| encapsulated in the `sendEmails` function, and the error
| handling is reduced to a `Result` which is very easy to
| work with in a pipeline. Not everything should be done in
| a pipeline but pipelines make a lot of things a lot nicer
| masklinn wrote:
| > Currying and partial application are really nice when you
| have a pipeline operator
|
| Currying != partial application.
|
| And while currying is useful in curried languages (to convert
| back from uncurried to curried), and partial application is
| useful period, the question is whether currying is useful, in
| general, in an uncurried language.
|
| I don't think it is:
|
| * You usually want to perform partial application, currying
| is an unnecessary intermediate step.
|
| * Currying conflicts with rich "imperative" parameters e.g.
| overloading, default parameters, keyword parameters.
|
| * Uncurried languages are usually imperative and effectful,
| that you have filled all the parameter spots does not mean
| you want to invoke the function.
|
| Having a curried language is neat, and in curried languages
| converting back from uncurried to curried functions is
| tremendously useful. But it's not so for uncurried languages
| (which is most of them).
|
| > which is why the partial application and pipeline operator
| proposals are so intertwined.
|
| Again, partial application != currying. And pipeline
| operators can perform partial application (implicitly or
| explicitely) on their own. For a flagrant example, just see
| Clojure: (->> users (map :email)
| (sendEmail) (mapError (constantly "Error sending
| emails"))
| creata wrote:
| > Currying conflicts with rich "imperative" parameters e.g.
| overloading, default parameters, keyword parameters.
|
| This doesn't contradict anything you said, but I think
| Idris is a language that curries everything and has default
| parameters.
|
| https://docs.idris-
| lang.org/en/latest/tutorial/miscellany.ht...
| BiteCode_dev wrote:
| Agreed, and I really with partial() was a builtin in
| Python, instead of having to fetch it from functools.
| partial() is useful regularly, and much more
| explicit/flexible than a lambda for this specific use case.
| brundolf wrote:
| I'm not sure I fully appreciate the distinction you're
| trying to make here: "currying != partial application". Can
| you clarify?
|
| In particular I don't know what's meant by "curried" and
| "uncurried" languages
| masklinn wrote:
| > I'm not sure I fully appreciate the distinction you're
| trying to make here: "currying != partial application".
| Can you clarify?
|
| Currying is the conversion of an n-ary function into a
| chain of 1-ary function e.g. `fn(a, b, c) -> d` becomes
| `fn(a) -> fn(b) -> fn(c) -> d`.
|
| Partial application is what it says on the tin, it only
| applies k parameters to the function returning an (n-k)
| parameters function, without actually running the
| function.
|
| Currying is one method allowing partial application, but
| not necessarily the only one, or usually the best fit as
| it tends to not interact well with features like
| overloading, default parameters, keyword parameters, ...
|
| > In particular I don't know what's meant by "curried"
| and "uncurried" languages
|
| Languages like Haskell, OCaml, or Elm are "curried" by
| default: when you define an n-ary function it's really
| just sugar for a chain of 1-ary functions; and the
| application of multiple arguments is similarly the
| implicit application of single arguments multiple times
| e.g. map : (a -> b) -> List a -> List b
| map f l = case l of [] -> []
| (h::t) -> (f h) :: (map f t)
|
| can be called as map fn [1, 2, 3]
|
| or (map fn) [1, 2, 3]
|
| the semantics are identical, which is not the case in an
| _uncurried_ language like Javascript or Python:
| map(fn, [1, 2, 3])
|
| and map(fn)([1, 2, 3])
|
| will behave very differently unless the implementer has
| taken special steps to handle this case e.g. dynamic
| behaviour based on an optional second parameter,
| overloading, etc...
| brundolf wrote:
| > applies k parameters to the function returning an (n-k)
| parameters function, without actually running the
| function
|
| What would be the use of this? Just delaying the
| execution of the function's side-effects?
|
| I'll tip my hand: I'm working on a JS-like (and JS-
| targeted) language that I want to support partial-
| application/currying by default. Under the hood
| everything is represented in a curried way, but
| syntactically you can both define and invoke/partially-
| invoke functions with the more familiar syntax (commas).
| But this is fully superficial right now; it all happens
| at the parser level. Is there a downside to doing things
| this way?
| RexM wrote:
| Bagel?
|
| I read your post yesterday. Good luck with your language!
| brundolf wrote:
| Yes, thanks! :)
|
| To be honest this comments section has me seriously
| reconsidering the "everything is partially-applicable by
| default" feature, haha.
| djur wrote:
| The downside is that curried functions make default,
| optional, and keyword parameters difficult to implement.
|
| For defaults/optionals, given "fn f(x, y, z=10)", if
| "f(1, 2)" evaluates to "f(1)(2)", does that return a
| function or the result of "f(1)(2)(10)"? What if you have
| "fn f(x, y = 10, z)"? All of this is a lot easier if you
| can evaluate the full argument list at the time of
| function invocation.
|
| As for keyword arguments, they just don't play well with
| any kind of enforced ordering.
| brundolf wrote:
| Got it.
|
| I wonder if that ambiguity could be resolved using
| contextual type info? I.e. if one of the two
| interpretations fits the expected type, use that.
| Otherwise, type-error.
|
| Maybe that would be confusing, not sure.
| flyingchipmann wrote:
| If you come from typical oop background like java.
| Imagine there is a function has 5 parameters, and you
| need to to provide the first 3 first. Then later on you
| are using this partially applied function somewhere else.
| You can even reuse it later with different 2 params.
| Compare to class, it's just like how you initialize an
| object with certain values, then do something else with
| this object.
| djur wrote:
| In Standard ML and Haskell, all functions are curried by
| default -- they take a single argument, and the syntax
| for defining multiple-argument functions is just sugar
| for defining single-argument functions that return
| single-argument functions. That's reflected in this
| Haskell type signature for a function adding two
| integers: add :: Int -> Int -> Int
|
| This is a function that takes one integer and returns a
| function which itself takes another integer and then
| finally returns an integer. Syntax sugar allows you to
| define it and call it like a single function.
|
| An uncurried function in these languages is a function
| that takes multiple arguments as a tuple.
| add :: (Int, Int) -> Int
|
| You don't see a lot of functions defined this way in
| Haskell because there isn't really any advantage to doing
| so in a lazy, pure language. It's more common in Standard
| ML from what I've seen, because SML is strict and impure.
| smohare wrote:
| At a deeper level currying is just a specific application
| of the Hom-tensor adjunction to the category of Sets. In
| particular the curried function is equivalent to the
| original. Partial function mappings is a restriction, and
| inherently not a injection. That is, generally the
| partial function is not in an equivalent Hom space to the
| original function.
| nestorD wrote:
| Yes! Like having both pattern matching and tagged union type
| in a language, it is the synergy of the features that makes
| them significantly powerful.
| runarberg wrote:
| We were hoping for similar operators in JavaScript (pipeline
| `|>` and partial application `?`). However TC39 just decided
| earlier this month that we users of the language are not
| worthy of such powers. This was off course meet with a lot of
| resentment from many JavaScript developers, including my
| self.
|
| https://github.com/tc39/proposal-pipeline-operator
| Zababa wrote:
| Considering what happened with the monadic promises, that
| was to be expected. I hope we'll at least have the
| immutable records and tuples so that the JS engine can
| implement them efficiently. This way JS will become an even
| better compilation target.
| somehnacct3757 wrote:
| I mostly agree but every tool has its purpose. Currying is
| indispensable in some composability workflows.
|
| Consider advanced component creation in virtual dom frameworks.
| Components may want to yield some render responsibilities to
| the end-user while not exposing their inner api. They can use
| currying here to hide details from the end-user while providing
| their own public api.
|
| I've also seen it be useful when you have sub components
| editing different parts of a model. You write one generic prop-
| assigner setModelProp(prop, val) alongside the model in the
| scope that owns the model. Then you give each subcomponent a
| curried setter with the prop locked in. Those sub-components
| don't have the ability to edit the model any other way now, and
| you haven't written a ton of boilerplate setters.
| masklinn wrote:
| > every tool has its purpose.
|
| That's really not true. The Y combinator is not generally
| useful for instance, because every modern language supports
| recursion.
|
| > Currying is indispensable in some composability workflows.
|
| It's not.
|
| > Then you give each subcomponent a curried setter with the
| prop locked in.
|
| That's partial application, not currying.
|
| There are convenience use cases for _libraries_ providing
| _curried versions_ of some functions e.g. you might want to
| provide both `setModelProp(prop, val)` and
| `setModelProp(prop)(val)`. But that 's because you expect
| significant use cases fo _partially applied_ versions of the
| utility functions. Currying is not _necessary_ for this, it
| just makes things more convenient for users.
| somehnacct3757 wrote:
| I see, I had these two concepts conflated. I guess the
| difference is currying returns a function and partial
| application invokes the function.
|
| In that case I don't know any indispensible situations for
| currying.
| leodriesch wrote:
| Nice explanation, however I don't really see the use case for
| currying yet. If you want functions with fixed arguments, you
| could also just create a new function that calls the other with a
| fixed first argument and just takes the other arguments. Am I
| missing something?
| goto11 wrote:
| What you describe _is_ currying.
| jhgb wrote:
| Isn't that more like partial application?
| [deleted]
| schpaencoder wrote:
| At that moment, Anton became enlightened.
| Normal_gaussian wrote:
| With the main benefit of being able to also transform the
| argument orser or output value at the same time.
| graftak wrote:
| The most common case I've seen is when you want to map using
| 'point free' style functions, i.e. "x.map(transformItem)"[1].
| Another (more) common scenario--but not yet relevant in
| JavaScript--is piping/composing functions[2].
|
| [1] https://en.wikipedia.org/wiki/Tacit_programming
|
| [2] https://github.com/tc39/proposal-pipeline-operator/wiki
| ttiurani wrote:
| What you're missing is that the curry() function is generic:
| you don't need to manually write that new function, but just
| type a simple oneliner using curry() to generate the function
| you're describing.
| leodriesch wrote:
| Defining one function that just calls another with a fixed
| argument is also a one-liner with arrow functions. And I
| would argue that this would be easier to understand and
| anyone reading the code wouldn't have to understand what
| currying is.
| latexr wrote:
| > If you want functions with fixed arguments, you could also
| just create a new function that calls the other with a fixed
| first argument and just takes the other arguments.
|
| From searching for practical applications a while back, I
| understood currying as a way to create a new function with
| fixed values rather than fixed arguments. Meaning if you had a
| function take arguments FIRST and SECOND, with FIRST being
| computationally expensive, currying would allow you to create a
| new function with the slower part pre-calculated.
|
| But after brief testing I'm unsure that's the case. It seems
| the examples might've been referring to the argument itself
| being a computationally expensive operation, in which case--and
| going back to your example--you could save the computation's
| result to a variable beforehand and use that.
| jhgb wrote:
| Currying should not create a function with anything fixed
| that hadn't been fixed before.
| r1cka wrote:
| I believe you may be mixing the concept of currying with
| partial application.
| fendy3002 wrote:
| Also useful in react. It's better to use `data-props` for
| performance, but if somehow `data-` props cannot contain the
| information (function maybe), you can use currying with
| previously memoize it first.
|
| For example, if you want to pass an object to onClick event:
| const handleOnClick = _.memoize( (myObj) =>
| (evt) => { // you can do something with myObj here
| }); // on render return <button
| onClick={handleOnClick(myObj)} >Try it</button>;
|
| Memoize works on class component but I don't know if it works
| on function component.
| es7 wrote:
| In my 10+ years as a JS dev, I've seen this pattern in numerous
| code bases. Inevitably it leads to bugs and confusion.
|
| Currying can lead to code that looks simpler, but I believe it
| hides real complexity in a non-obvious way. Well-named wrapper
| functions like 'logNow' can be great, but arbitrary curried
| functions are too tricky to keep track of on teams that include
| juniors, backend engineers and experienced senior FE too.
| brundolf wrote:
| I've never seen it abused, maybe just because the team cultures
| didn't lend themselves to that, but I have used it in one or
| two places myself where it was very helpful. The main one that
| comes to mind is pre-binding callback to be passed down to
| react components, so that the exact same function instance can
| be passed on each re-render of the parent component and the
| child component doesn't get triggered to render
|
| What are some ways you've seen it abused in JS projects?
| mjbrusso wrote:
| In the `logNow` example, the date argument will be eager
| evaluated, not lazzy evaluated. Am I right? So what was supposed
| to be a real use case doesn't seem useful to me.
| fendy3002 wrote:
| Nice catch. Rather than date the author should've pass the
| DEBUG constant instead.
|
| Or a function that return new date, but it'll make things
| complicated.
| mjbrusso wrote:
| However , to pass a constant I could use default parameter
| value.
| mnemotronic wrote:
| ... and I still don't understand _why_ I 'd want to use currying,
| except as a mental exercise in "look at the weird things I can
| do".
| jonplackett wrote:
| Seems like more trouble than it's worth
| elboru wrote:
| To me the problem with this kind of explanations is that they
| miss the "why". I understand "what" is currying and "how" to
| implement it. But I'm missing the "why".
|
| The real-world example doesn't tell me why I would prefer
| currying instead of putting the date at the end and making it
| optional, then checking if missing to assign current date. And
| for the other argument I could create separate functions
| logDebug, logWarning and logError, those would call the generic
| function with fixed strings, right?
| ImprobableTruth wrote:
| >And for the other argument I could create separate functions
| logDebug, logWarning and logError, those would call the generic
| function with fixed strings, right?
|
| Sure, but what would you do if e.g. the importance string isn't
| fixed? Well, you might write a function that takes in the
| importance string and returns a function that calls the generic
| function with the dynamic string. That's currying.
| misterpurple45 wrote:
| We use currying at work to inject dependencies. E.g.
|
| `sendEmail(mockEmailService)(to, subject, body) `
|
| I find it helps to differentiate between side-effecting
| dependencies and parameters, making code purer, easier to test,
| more scalable etc.
| SPBS wrote:
| Any discussion on the utility of currying (i.e. closures) vs
| constructing an object with methods always reminds me of this
| parable:
|
| ```
|
| The venerable master Qc Na was walking with his student, Anton.
| Hoping to prompt the master into a discussion, Anton said
| "Master, I have heard that objects are a very good thing - is
| this true?" Qc Na looked pityingly at his student and replied,
| "Foolish pupil - objects are merely a poor man's closures."
|
| Chastised, Anton took his leave from his master and returned to
| his cell, intent on studying closures. He carefully read the
| entire "Lambda: The Ultimate..." series of papers and its
| cousins, and implemented a small Scheme interpreter with a
| closure-based object system. He learned much, and looked forward
| to informing his master of his progress.
|
| On his next walk with Qc Na, Anton attempted to impress his
| master by saying "Master, I have diligently studied the matter,
| and now understand that objects are truly a poor man's closures."
| Qc Na responded by hitting Anton with his stick, saying "When
| will you learn? Closures are a poor man's object." At that
| moment, Anton became enlightened.
|
| ```
|
| Currying (and closures) are great when you don't want to go
| through the overhead of constructing an entire object just to
| hold state. They're lightweight pseudo-objects. But the moment
| the number of variables you are closing over starts getting too
| big, it is better to formalize it into an object instead. That's
| how I interpret this story.
| isaacimagine wrote:
| Closures and objects are both ways to pair code and data -- I
| don't think the lesson is entirely about switching to objects
| when a closure becomes to large; personally, I find it to be an
| invitation to discover how and why code and data are paired
| together in the first place.
|
| Why do closures and objects arise in such a natural fashion?
| I'm not entirely sure, and I don't think there's one right
| answer. All I can think is that I can't imagine the depth of
| Alan Kay's knowledge on the subject, given he pioneered scheme
| and smalltalk, two languages that define the core of the
| closure/object paradigm.
| fendy3002 wrote:
| > Why do closures and objects arise in such a natural fashion
|
| My reasoning is we need a partitioned stateful function which
| is impossible to achieve and the alternative are worse:
|
| - partition global state with your own object id
|
| - passing all state as arguments
| isaacimagine wrote:
| Other ways to manage state as well:
|
| - Managing state through a monad or effect system
|
| - Managing state through coroutines / continuations
|
| - Managing state through dynamic scoping (poor man's effect
| system)
|
| - Associating implicitly mutating functions with types
| without having full objects (Rust)
|
| Some of these are pretty powerful, especially effects and
| continuations. I think the reason we don't see more of them
| is because we haven't figured out a wholly ergonomic way to
| fit them into a language yet.
| srcreigh wrote:
| If you don't mind using mutation for private state in a
| functional language, objects and closures are kinda the exact
| same thing. (define (make-queue)
| (define data empty) (lambda (cmd [arg #f])
| (match cmd ['enqueue (set! data (append data
| (list arg)))] ['dequeue (define
| value (first data) (set! data (rest data))
| value])))
|
| Don't see the connection yet? Let me try again.
| (define (make-queue) (define data empty)
| (define (instance cmd [arg #f]) (match cmd
| ['enqueue (set! data (append data (list arg)))]
| ['dequeue (define value (first data)
| (set! data (rest data)) value]))
| instance)
| discreteevent wrote:
| Slightly off topic, but later on in that Qc Na thread:
|
| ----------
|
| This brought to mind my introduction to OOP by a talented but
| notoriously inarticulate programmer:
|
| NIP: Do you know what OOP is?
|
| Me: No.
|
| NIP: Ah. Well, you have objects and methods. Objects: objects
| aren't anything. And methods are how you talk to objects. Okay?
|
| Me: Sure.
|
| I knew there was no point in asking questions -- this was the
| man at his most expository. I took him to mean, "Forget what
| objects _are_ , just work with them."
|
| -------------
|
| Which I take to mean "Objects are black boxes. How they behave
| is what they are"
| zwarag wrote:
| Who is this Qc Na master? Is that a story from a book or a
| legend from a forum board?
| detaro wrote:
| There's a bit of a tradition of writing such koans for
| software topics. This one is from here:
| http://people.csail.mit.edu/gregs/ll1-discuss-archive-
| html/m...
| goldenkey wrote:
| Indeed but we can get around closures by using first class
| operators like bind.
|
| This implementation I wrote is much more elegant and performant
| than the O(n) stack frame crud in that article:
| const curry = fn => { const curried = (...a) =>
| a.length >= fn.length ? fn(...a) : curried.bind(undefined,
| ...a) return curried }
|
| In fact, I can't think of any implementation in JS that would
| be more performant, using any paradigm, functional, object
| oriented, or imperative.
|
| Try to best it if you can. I'm curious to see what HN can come
| up with ;-)
| masklinn wrote:
| That's not currying, that's partial application (except bad
| because it forces the evaluation of the function if all
| parameters are filled, which is often undesirable as
| javascript is an eagerly evaluated language with side-
| effects).
| goldenkey wrote:
| The implementation in the article is technically partial
| application. I was just reproducing it. But you are
| correct. Currying is a subset of partial application. You
| can do currying through partial application. The function
| is eventually called when the correct number of parameters
| has finally been supplied. Currying lets you call a
| function, splitting it in multiple calls, providing one
| argument per-call. Partial Application lets you call a
| function, splitting it in multiple calls, providing
| multiple arguments per-call.
|
| The only difference is that currying would ignore any
| additional parameters in a flexible language like JS.
|
| It's easy to make this implementation only consider the
| first parameter. See below. const curry =
| fn => { const curried = (a,x) => a.length + 1 >=
| fn.length ? fn(...a, x) : curried.bind(undefined, [...a,
| x]) return curried.bind(undefined, []) }
|
| I still don't understand your point about eager execution.
| The function is supposed to be executed when enough
| parameters have been passed. I suppose you mean that JS
| does not check if the call arguments length equals the
| function signature, so strict currying is safer to avoid
| errors? If so, that makes sense. I tend to consider that
| caveat of JS a feature though, which is why partial
| application is what I'd use to accomplish currying. It
| subsumes it and provides more flexibility by allowing
| multiple arguments to be curried in a single call.
| masklinn wrote:
| > I still don't understand your point about eager
| execution. The function is supposed to be executed when
| enough parameters have been passed.
|
| Which makes perfect sense for languages which are mostly
| side-effect free and in which functions with no
| parameters do not make sense.
|
| That's not the case of Javascript, partially applying all
| the parameters but not wanting to actually run the
| function is a useful thing to desire, and _not_ doing
| that makes partial application incoherent, especially as
| e.g. default parameters get involved.
|
| That is why currying is a bad fit for imperative
| languages, but partial application is a useful tool
| still.
| goldenkey wrote:
| You're not making any sense to me. Neither currying nor
| partial application delay running of the function once
| all parameters have been passed. There is no eager
| execution happening in the implementation I provided. I
| believe you misunderstand when the original function is
| supposed to be executed.
|
| sum = curry((a,b,c) => a+b+c)
|
| sum(1)(2)(3) equals 6 under currying.
|
| sum(1,2)(3) equals 6 under partial application.
|
| Perhaps you are thinking of calling the function with no
| arguments as a signal to execute the final calculation.
| That is NOT how currying normally works.
|
| I believe you think currying works like this:
|
| sum(1)(2)(3)() = 6
|
| This is NOT standard currying. There is never a need to
| give an empty call to signal execution in standard
| currying as CS and Mathematics define it.
|
| The implementation I provided is defacto currying by the
| book. Perhaps you can provide an example if we are
| misunderstanding eachother.
| masklinn wrote:
| > Neither currying nor partial application delay running
| of the function once all parameters have been passed.
|
| Currying does not, partial application can (and should).
|
| > sum = curry((a,b,c) => a+b+c)
|
| > sum(1)(2)(3) equals 6 under currying.
|
| > sum(1,2)(3) equals 6 under partial application.
|
| Sure. Now what happens to `sum(1, 2, 3)`? Or if the
| function is `(a, b, c=1) => a + b + c`?
|
| > Perhaps you are thinking of calling the function with
| no arguments as a signal to execute the final
| calculation. That is NOT how currying normally works.
|
| Which is the point. It's not how currying works. It is,
| however, how a "regular" version of partial application
| works e.g. Javascript's Function#bind or Python's
| `functools.partial`.
|
| > I believe you are confused.
|
| Nope.
|
| > I believe you think currying works like this:
|
| No.
|
| > The implementation I provided is defacto currying by
| the book.
|
| "Currying by the book" does is defined for strict arities
| and can not generate variable-arity functions.
| goldenkey wrote:
| Thank you for helping me understand the issue with
| default parameter values in a variadic language like JS,
| which probably makes a nonstandard currying with an
| execution signal more applicable.
|
| I've created a curry which will only execute when called
| with no arguments, ie. sum(1)(2)(3)() = 6
|
| This is like you mentioned, a much better suited
| technique of currying for a language like JS:
| const curry = fn => { const curried = (a,...x) =>
| x.length === 0 ? fn(...a) : curried.bind(undefined,
| [...a, x[0]]) return curried.bind(undefined, [])
| } // equals 15 because default c=10
| curry((a,b,c=10)=>a+b+c)(2)(3)()
|
| We can also apply the same idea of an empty call as a
| signal to the multi-argument currying variation. I'd love
| to know what you think. And please keep reading. Because
| I apologize and stand corrected. Apparently partial
| application is single use, unlike currying. Essentially a
| fixed point. I have learned something today. I apologize
| for being so hard headed. Thank you for putting up with
| me and correcting me. The wikipedia article on currying
| confused me with regard to partial application but then I
| read the partial application wiki article and what you
| are saying clicked.
|
| It seems like there are 3 techniques here.
|
| Multi argument currying. Single argument currying. And
| partial application (aka binding)
|
| And too many references called the former the latter,
| hence my confusion.
| masklinn wrote:
| It's really no worries. At the end of the day, the issue
| is really that mathematical / functional / "traditional"
| currying is not really suited to imperative languages,
| which is most of them, and notions of "advanced" currying
| are -- I think -- throwing good money after bad.
|
| Partial application is very useful (and you're correct
| that it's "one shot", though you can obviously layer
| partial applications by partially applying the result),
| so can the odd curried version of a library function
| (ideally provided by the library) be, but general-purpose
| currying... not so much I would say: the languages don't
| care for it (unlike curried language), it interacts badly
| with complicated parameterisation (varargs, default,
| keyword, ...), and the almost entirely of the use cases
| is covered by straight partial application. What little
| is left can simply be curried by hand.
| 734129837261 wrote:
| Just because you can doesn't mean you should. Shit, for adding an
| array of objects with numbers just use a simple map/reduce
| instead of confusing future developers with your fancy curry.
|
| Whenever I run into a curry, I can usually solve the same issue
| with a simple for loop that makes it instantly clear what the
| code does, always.
| kesor wrote:
| Cringe at the new Date() example ... will logNow() actually
| create a new Date() each time? no, it will use the one provided
| in the curry initialization.
| goldenkey wrote:
| The implementation in the submission is awfully inelegant, dated,
| and slow. I quickly wrote up a fast elegant ES2021 solution and
| tested it thoroughly. Below is the code, feel free to use it:
| const curry = fn => { return (...args) => {
| const curried = (...a) => { args = [...args]
| return args.push(...a) >= fn.length ? fn(...args) : curried
| } return curried() } }
| thrttnkrnl wrote:
| Your implementation is broken for any function that has more
| than two arguments. const sum3 = curry(( a, b,
| c ) => a + b + c ) const sum3_10 = sum3( 10 ) // ...
| const sum3_10_20 = sum3_10( 20 ) // ... sum3_10_20( 30 )
| // 60 sum3_10_20( 5 ) // 60
|
| One of the points of currying is to be able to fork the partial
| application of the function at any point.
| goldenkey wrote:
| Very good catch, thank you. Here is my take on a better
| version that fixes the issue: const curry =
| fn => { const curried = (...a) => a.length >=
| fn.length ? fn(...a) : curried.bind(undefined, ...a)
| return curried }
| somehnacct3757 wrote:
| I get the dated claim, but how is yours faster? It's nearly the
| same logic written with syntax sugar, no? The differences I see
| are that by using rest params instead of a deferred
| Array.prototype.concat, you've lifted the Array instance
| creation into all branches, where the original saves itself the
| trouble in the invariant case. And yours defines a variable
| whereas the original didn't. Is there some under the hood
| reason why this version would be more performant?
|
| As for the inelegant claim, beauty is in the eye of the
| beholder. If performance is not a concern (as it won't be in
| most currying use-cases) I think the original version is much
| easier to read without years of js experience. It can be hard
| for newer or cross-discipline devs to understand mixed usage of
| rest params and spread operator since they look the same. In a
| scenario like this I think maintainability is a key ingredient
| of elegance.
| goldenkey wrote:
| The article's implementation grows the stack unnecessarily
| through recursively nested closures. A commenter pointed out
| a flaw in my code. This new implementation is even faster and
| more concise by doing away with the closures (other than the
| single inner closure): const curry = fn => {
| const curried = (...a) => a.length >= fn.length ? fn(...a) :
| curried.bind(undefined, ...a) return curried }
|
| It uses bind which is an extremely powerful operator (a
| function that maps functions to functions)
| dotancohen wrote:
| The topic is not the implementation, but rather the concept. To
| make the topic accessible to as wide an audience as possible,
| including old C stogies, the traditional anonymous function is
| much easier to understand.
| zodiakzz wrote:
| That's already built in to the language: let
| logNow = log.bind(null, new Date()); // [sic, "now" is fixed]
| let debugNow = logNow.bind(null, "DEBUG");
|
| Also, uglifiers and transpilers do some horrifying stuff, this
| won't break if fn.length is tempered with. Unlike the article
| implementation.
| masklinn wrote:
| That is not currying, it's partial application.
| q-rews wrote:
| Currying makes it implicit, but it's the same thing.
| const one = curriedSum(1) one(2)
|
| is the same as const one = sum.bind(null,
| 1) one(2)
|
| with the exception that the latter is explicit and not as
| slow as `curriedSum`.
|
| Both `sum` and `curriedSum` can be used the same way:
| curriedSum(1)(2) sum.bind(null, 1)(2)
|
| or just: curriedSum(1, 2) sum(1, 2)
|
| One of the advantages is that non-curried functions make the
| return value explicit. You can't implement `sum(...addends)`
| with currying because curried functions have a fixed number
| of parameters. Once past that, a call will not return a
| function anymore. sum.bind(null,
| 1).bind(null, 2).bind(null, 3).bind(null, 4) // Still not
| called curriedSum(1)(2)(3)(4) // Maybe it works,
| maybe undefined is not a function
| zodiakzz wrote:
| They're technically correct. And if you gotta write on HN,
| you better get the terminology correct (and that derails
| threads into interesting tangents often) so my bad.
|
| Gotta say currying seems to be useless in real world. No
| advantage whatsoever over partial application.
| zodiakzz wrote:
| Ah, TIL.
|
| If we are being pedants. Wikipedia says:
|
| > currying is the technique of converting a function that
| takes multiple arguments into a sequence of functions that
| each takes a *single argument*.
|
| So the article isn't currying either. It's a hybrid between
| currying and partial application.
|
| TBH I can't think of a practical case where you'd prefer
| currying over partial application. Theory/philosophy aside.
| masklinn wrote:
| > So the article isn't currying either. It's a hybrid
| between currying and partial application.
|
| Sure.
|
| > TBH I can't think of a practical case where you'd prefer
| currying over partial application. Theory/philosophy aside.
|
| For eager-evaluation uncurried language (like javascript) I
| would also struggle to think of one.
|
| For curried languages, it can sometimes be useful to
| _uncurry_ functions in order to make types match, after
| which you might have to curry them back into the "normal"
| form to use them.
| g_b wrote:
| Easier to read version: let logNow =
| (importance, message) => log(new Date(), importance, message);
| let debugNow = message => logNow("DEBUG", message);
|
| And with this implementation, current IDEs provide a more
| informative auto-complete.
| asimpletune wrote:
| I remember feeling very excited the first time I ran into a
| legitimate situation, where there was no better way to do things
| than curry the function.
| weatherlight wrote:
| Currying is useful, especially if you are doing point-free style
| functional programming or just functional programming in general.
| its a useful tool with a time and a place, I don't get all the
| "currying hate."
| tenaciousDaniel wrote:
| It's funny, I learned about currying back when I was first
| learning Javascript and programming in general. That was 10 years
| ago, and not _once_ in my 10 years as a professional programmer
| have I ever needed to curry. Maybe I 've been missing out, but I
| don't feel like I have.
| guntars wrote:
| A word of caution - using functional programming principles in a
| language that is not optimized for it can result in awful
| performance. In JavaScript, whenever you create a closure it's a
| heap allocation. If you're doing that in a tight loop, it'll
| completely dominate the time it takes to run your program.
| avinassh wrote:
| How do functional programming languages optimises these?
| oweiler wrote:
| I don't know about other languages, but in Kotlin these are
| normally inlined, which means no allocation.
| eurasiantiger wrote:
| Yes. In JS it is best to avoid FP principles such as
| immutability in hot code paths, the extra allocations and GCs
| are not worth the cleanliness.
| imbnwa wrote:
| Redux is a good example of a functional pattern that makes
| waaay more sense in a language with pattern matching, immutable
| values, and ADTs built in. You have to really think about what
| you're doing to scale Redux up with a large app in a JS runtime
| and to avoid the boilerplate. Last time I used Redux with TS
| the typings were not particularly helpful as well but that
| may've improved.
| brundolf wrote:
| Are you speaking from experience or speculating? It's intuitive
| that there would be some overhead, but the amount of overhead
| could be anywhere from tiny to huge, especially given how
| aggressive V8 is about optimizing certain things
___________________________________________________________________
(page generated 2021-09-19 23:01 UTC)