-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Currying? #554
Comments
This would conflict with multiple dispatch:
You might be able to do this sort of partial evaluation with a macro. That macro may already exist. |
I think the macro may look something like this:
But I don't think that reverse($args) works... |
My suggestion was to return this function just if otherwise there would be a wrong number of arguments error.
Then:
and so on. arrgh... This $%@! markdown ... |
This definitely isn't going to happen. Even if this could somehow be made to work with multiple dispatch (and I really don't see how it could), it would be incredibly confusing. In a language like Julia, when I call a function with the wrong number of arguments, I want to know that I called a function with the wrong number of arguments. I don't want a function object where I expected a plain old result. This would also immensely complicate method dispatch, which is not acceptable from the implementation end. If you want to curry |
For the markdown, you need to leave a blank line before and after your code blocks even when they're in triple backticks. |
Yes, no disrespect to currying, but this isn't julia's style. |
On the other hand, Julia could adopt an explicit currying mechanism as in Pyret, Scala?, etc. This would circumvent issues with multiple dispatch and still provide an elegant currying experience. For example, if f(x, y) = ...
f(x) = ... then Currying is extremely helpful when done properly. I think Julia could benefit significantly from it. |
The issue with using |
@StefanKarpinski, isn't that just a matter of choosing a convention? Also, just curious: what would be the benefit of having |
From all the other usages I've seen using this type of currying, I'd say definitely I think this is a nice and handy feature to have. It doesn't really scale to complex expressions but I think the point of the underscore is to keep simple things simple and avoid boiler-plate looking anonymous functions where the argument+arrow takes 50%+ of the definition. The do-blocks already solve the multiline anonymous function problem, so it would be a nice complement. Edit: Also, it would make Edit2: Just realized my previous pipelining example wasn't very good because you don't need to wrap one argument functions with anonymous ones. Way to make a fool of yourself! haha *The most confusing example I can think of would be something like |
Right, I think the standard convention is to simply capture the most immediate parent expression. So, As @fcard points out, using multiple |
I think it should be
We could lead the ASCII-art renaissance! |
Both Scala's and Pyret's way of dealing with multiple I like it for some situations (like for reduce), but yes, we could live without it. @timholy What we need is a digital-circuits-style mini-language: data
|
--------------v
map------->(_,_,_)--------v
f-------------^ (_,_)-----------v
sum---------------------^ (_,_)-------------->EXIT
println------------------------------^ But yeah. Are you saying that because you find that strange looking? I mean, I do too, I just like it better than the alternative :P We could always combine it with the 'pipelining' style proposed elsewhere, so that the
That could be implemented by simply adding the |
That's pretty awe-inspiring. Maybe we should allow one to write julia code using SVG! More seriously, yes, I personally find even basic pipelining somewhat dubious, but that's probably just because I've never written code in a language which supports it (other than bash, which is not exactly a great model for convincing me that it adds elegance 😄). I guess I see why
is slightly easier to read than
but one can write the latter as
in which case I think the advantage disappears. The number of keystrokes is not dramatically different either way. Anyway, while there's a part of me that's genuinely reluctant to embrace having too many ways of doing things, I don't care deeply about this issue, I was just having fun with it. |
I am not super-fond of pipelining in Julia either, my argument was more like, "hey, given that we already decided to have pipelining, why not have this feature that makes it better?". I find pipelining more useful in other languages that already have a lot of nesting so that you can use it to differentiate 'statement-like' series of transformations. I think that this is a discussion for #5571, though. Also, its good to have fun, and you did bring up a good point, so its all good in my book. :) And related, |
@oxinabox Yes, Pipe.jl solves the case for pipelines, but it's not a complete implementation of It seems I touched a sensitive subject with the pipeline thing. It's one of the lesser uses I was thinking of for Ah, and for anyone interested in the style of currying proposed originally, due to the *Updated with much faster version. I will probably reorganize it into a package or somethin', with maybe some other curry-based functionalities. |
As far as I can tell, Pipe.jl is basically just a simple form of currying hacked together. Pipe.jl definitely doesn't fill this hole. If anything, I think its existence is a sign that incorporating currying into Julia should be seriously considered, since people are going through all the trouble of hacking some likeness of it together, even in such a restricted form. |
No currying is different from what Pipe.jl is trying to achieve. Currying is the replacement of a function that takes multiple arguments, with a function (functor?) that takes 1 argument, and returns a function which takes the next argument etc. If one wanted to hack in currying, they would be hacking in partial application macros. They wouldn't be thinking about pipe redirection at all. What you are calling "Explict Currying" is kind of similar, but it creates functions. I've not used a language with this "explicit currying", though I used F# a lot, which has normal currying -- that is to say all multiple argument functions were curried and so could be partially applied. I think this normal implicit currying is antithetical (on a philosphical basis at least) to multiple dispatch. I'm fairly certain what you want right now can be implemented in macros already. (I was stuck awake last night thinking how fcard's joke could actually be implemented in macros. |
@oxinabox Like, you could implement infix operations with macros, but who would want to write Some functionalities are indeed just as useful when implemented as macros, others not so much. I would certainly not use
Last year I actually implemented something similar, but with trees instead of circuits. Experiments like these are always fun! @samuela Hopefully I am not sounding pushy about this. I really like this type of *Got bothered by a functional programmer :P |
I'm aware that currying and piping are two very different issues. More precisely, what I meant to convey is that Pipe.jl exists primarily because currying is not provided by Julia. If it were, the usual piping functionality would suffice. In other words, the essence of Pipe.jl is to introduce a currying-like mechanism for the sole use in piping expressions. (Feel free to correct me here if my understanding is still not correct!) I agree with @fcard here that macros won't really do. Writing |
I think the real question for this thread should be: is this a conversation worth having now? Aren't the function type system changes required to make higher-order functions fast vastly more important? Aren't the array changes for 0.5 vastly more important? The question shouldn't be: is this feature worth having? The question should be: is this the specific change that someone should be working on implementing today to achieve the greatest marginal change in Julia's quality? If not, can't we have this conversation in the future, when we actually know more about how Julia's functions will interact with the type system? |
@johnmyleswhite That's a good point. I never really thought of this as a priority issue, though, I just wanted to discuss the merits of the feature for later reference, seeing that some interest was sparked. It was certainly not my intention to diverge attention to this, specially so close to the feature freeze. This being a closed issue, I didn't think I was doing that, but if this is the case, I apologize. @samuela How about we continue this conversation somewhere else? :P |
@samuela After thinking about it for a bit (or after someone yelling PARTIAL APPLICATION in my ear), this is probably an issue separated from currying anyway. The best thing to do I think would be to come up with a bunch of use cases, a list of merits vs cons, etc, and maybe some time later post an issue here/start a thread in julia-dev about a possible syntax construct for partial application (PARTIA- ahem alright). If you want to discuss, feel free to email me. (we could probably do a few test implementations as well) |
@fcard Sounds like a plan! If I get some free time I may take a stab at implementing partial application. I'm not familiar with Julia's internals so it may be quite an adventure. |
This isn't as elegant as the original proposal, but you can do partials with something like this, right?
(So kind of like python's functools.partial.) This should allow for, e.g:
|
* bump version to ~0.33.0~ 0.32.1 * build docs on 1.3; test on (pre-)release (1.3 and 1.4), drop 1.2 * fix doctests * release is non-breaking, using 0.24 documenter exactly
Stdlib: SparseArrays URL: https://github.com/JuliaSparse/SparseArrays.jl.git Stdlib branch: main Julia branch: master Old commit: e61663a New commit: 55976a6 Julia version: 1.12.0-DEV SparseArrays version: 1.12.0 Bump invoked by: @ViralBShah Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: JuliaSparse/SparseArrays.jl@e61663a...55976a6 ``` $ git log --oneline e61663a..55976a6 55976a6 Keep sparse solvers docs as before (#552) 95fd7ff Missing space in error message (#554) b8a13ef implement in-place `ldiv!` for Cholesky factorization (#547) 1527014 Do not use nested dissection by default. (#550) ``` Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
…aLang#55469) Stdlib: SparseArrays URL: https://github.com/JuliaSparse/SparseArrays.jl.git Stdlib branch: main Julia branch: master Old commit: e61663a New commit: 55976a6 Julia version: 1.12.0-DEV SparseArrays version: 1.12.0 Bump invoked by: @ViralBShah Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: JuliaSparse/SparseArrays.jl@e61663a...55976a6 ``` $ git log --oneline e61663a..55976a6 55976a6 Keep sparse solvers docs as before (JuliaLang#552) 95fd7ff Missing space in error message (JuliaLang#554) b8a13ef implement in-place `ldiv!` for Cholesky factorization (JuliaLang#547) 1527014 Do not use nested dissection by default. (JuliaLang#550) ``` Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
I idea is relative simple, most languages lack it, but it's awesome and quite easy to be integrated I guess...
This example is obvious:
((x,y) -> x + y)(1,2)
When calling with only one parameter as
((x,y) -> x + y)(1)
it raises a wrong number of arguments error. When a function is called with too few arguments it should instead return a anonymous function expecting the a missing one and executing it. So((x,y) -> x +y)(1)
should return(a -> ((x,y) -> x +y)(1,a)) )
(x,y) -> x +y is simply +(x,y). So +(1) would become (a -> +(1,a)). This function increments. Incrementing all fields of an array would bemap(+(1),[1,2,3,4]).
Of course it would be possible to simply create an anonymous function that increments, but that it would be cool if that happed automagical. Sometimes this aids a strange but useful programming style.That a way more beautiful than
>>=(checkData(data),(message -> reportError("serve", message)))
as a consequence (if f is a regular function expecting 3 parameters)
f(1,2,3)
would be as valid asf(1,2)(3)
orf(1)(2)(3)
The text was updated successfully, but these errors were encountered: