Partial functions

There has already been a discussion about potential currying, and I agree with the outcome. However, it would be quite convenient to have some form of partial functions implemented.

Prelude> let f x y z = x + y + z
Prelude> map (f 1 2) [1..3]
[4,5,6]
Prelude> map (^2) [1..5]
[1,4,9,16,25]

I came up with the following implementation, which I’ll admit is somewhat strange… but interesting, nonetheless.

julia> import Base: getindex

julia> getindex(f::Function, args...) = (xs...) -> f(args..., xs...)
getindex (generic function with 170 methods)

julia> f(x, y, z) = x + y + z
f (generic function with 1 method)

julia> map(f[1, 2], 1:3)
3-element Array{Int64,1}:
 4
 5
 6

The following is slightly limited by the fact that the parser currently considers the use of the ^ operator invalid in this context (not a unary operator), but this can be temporarily overcome by “aliasing” it.

julia> getindex(::typeof(^), n) = x -> x^n
getindex (generic function with 170 methods)

julia> pow = ^
^ (generic function with 47 methods)

julia> map(pow[2], 1:5)
5-element Array{Int64,1}:
  1
  4
  9
 16
 25

Of course, this would just be syntactic sugar for using inline anonymous functions.

julia> map(x -> x^2, 1:5)
5-element Array{Int64,1}:
  1
  4
  9
 16
 25

I have a hunch that this may never be implemented, potentially simply due to the fact that it could seem “un-Julian”. I’m curious to hear what others think about this concept, though. Is it something you would find useful? Is there a cleaner alternative? What side effects could this feature have - would it break anything? Why should/shouldn’t this (or something similar) be implemented? Any feedback would be much appreciated. :slight_smile:

2 Likes

Currying is pretty wonderful :smiley: because it can be such a joy to use. For that, notation is super-important …

I like f[x,y] a bit more than I thought I would, although it’s limited in that you can only curry on the front arguments? But the notation is problematic. Square brackets are already used loads, and this clashes with e.g. Int8[1], which should according to the above notation return a 0-argument function.

As you saw Stefan commented,

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 f(x,y), just write y->f(x,y) and it’s very clear what’s going on; it also doesn’t privilege the arguments based on their ordering — you can just as easily write x->f(x,y)

I think that’s a tight-looking lid on the problem. Since currying in practice is already very feasible via anonymous functions as above, with an easy, workable, clear syntax, it’s going to be pretty hard to find a spelling which is SO much better that it’s worth introducing that complexity without bringing a new capability.

2 Likes

I don’t get it. What’s wrong with using an anonymous function? They’re fast, the syntax is very simple and non-verbose already, and it’s something everybody already knows and uses.

7 Likes

Plus, in this case you could just use (1:5).^2

1 Like

Thanks for the consideration and comments, everyone. I do miss the succinct nature of Haskell’s syntax for this, but it makes sense that anonymous functions are the clearer solution here.

2 Likes