Skip to content
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

[RFC] Support Uniform Function Call Syntax #31779

Closed
richardanaya opened this issue Apr 20, 2019 · 18 comments
Closed

[RFC] Support Uniform Function Call Syntax #31779

richardanaya opened this issue Apr 20, 2019 · 18 comments

Comments

@richardanaya
Copy link

richardanaya commented Apr 20, 2019

Since Julia is struct oriented, this could be a nice way make the language more ergonomic for methods
https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax

Assume

struct Blah
    ...
end

foo(b::Blah,i::Int32)
      ...
end

b = Blah(..)

In addition to this syntax

foo(b,123)

this would also be acceptable if a function signature existed with Blah as first parameter type

b.foo(123)

Edited: fixed function name to be idiomatic

@jpsamaroo
Copy link
Member

This goes against Julia's functional philosophy and threatens to make Julia into an OO language, so I don't think you're going to get much agreement from experienced Julia developers.

@jpsamaroo
Copy link
Member

jpsamaroo commented Apr 20, 2019

Also, note that it'd be more idiomatic to just define:

function foo(b::Blah, i::Int)
...
end

foo(b, i)

No need to name it Blah_foo, since Julia uses multiple dispatch. Now foo can be extended to be called with instances of Bar or Flub or whatever as the first argument by just adding another method.

To your point, I consider this to be quite ergonomic.

@richardanaya
Copy link
Author

I don't see how a new way to call a function threatens a whole language to become OOP. That's a slippery slope fallacy. People are writing functions associated with structs right now and that's not a threat to the language.

@richardanaya
Copy link
Author

Nim is a procedural language that takes this idea pretty far and it is very imperative oriented: https://narimiran.github.io/nim-basics/#_calling_the_procedures

@richardanaya richardanaya changed the title Support Uniform Function Call Syntax [RFC] Support Uniform Function Call Syntax Apr 21, 2019
@richardanaya
Copy link
Author

richardanaya commented Apr 21, 2019

Another benefit I see to uniform function call syntax is the support of autocomplete in IDEs/REPL for Julia. One would simply suggest a list of functions with the variable's type as a first parameter.

@jpsamaroo
Copy link
Member

Let's put autocomplete aside for a moment, since it's mostly orthogonal to your RFC. Let's also not discuss just ergonomics since that is mostly a matter of personal choice, and UFCS doesn't actually shorten the number of keys you have to press to achieve effectively the same behavior with idiomatic Julia code (as I demonstrated above).

What concrete benefits do you see from adding UFCS to Julia? What things are currently difficult/infeasible to do with idiomatic Julia syntax that UFCS would improve on? If you can, try to refrain from pointing to other languages as the core of your example, and instead focus on what people use Julia for today, and how UFCS can help them. Specific examples would be very helpful as well!

If you can illustrate how UFCS really is a game-changer or significant improvement over what's currently possible, without any significant downsides to implementing it, maintaining it, and seeing its usage become commonplace in the package ecosystem, then I think you'll be able to garner support and valuable discussion for your RFC.

@stevengj
Copy link
Member

stevengj commented Apr 21, 2019

The basic problem with allowing foo(b,123) to be called as b.foo(123) is that it implies that the first argument is special. This is true in OO languages because the first argument is used for dispatch, but is mostly not true in Julia because multiple dispatch treats all arguments equally.

@tknopp
Copy link
Contributor

tknopp commented Apr 21, 2019

isn't this a duplicate issue of #25052?

@al6x
Copy link

al6x commented Jul 31, 2020

It solves problem of function chains natively

[-1, 2, 3].filter(x -> x > 0).map(x -> x^2)

instead of

map(x -> x^2, filter(x -> x > 0, [-1, 2, 3])).

And it has absolutely nothing with object oriented stuff.

And yes - it's much better to use in IDE with autocompletion.

@cloudhan
Copy link

Why is this issue closed?

@timholy
Copy link
Sponsor Member

timholy commented Aug 23, 2020

Because aside from tab completion there are nothing but negatives to it? In general we don't make changes that would make stuff worse.

@al6x
Copy link

al6x commented Aug 24, 2020

there are nothing but negatives to it

@timholy so you consider this

map(x -> x^2, map(x -> x^2, map(x -> x^2, [-1, 2, 3])))

as being superior to

[-1, 2, 3].map(x -> x^2).map(x -> x^2).map(x -> x^2)

Right? With multi-line it will be even more interesting.

@stevengj
Copy link
Member

stevengj commented Aug 24, 2020

@alexeyPetrushin, you can already use

[-1,2,3] .|> x->x^2 .|> x->x^2 .|> x->x^2

(and maybe [-1,2,3] .|> _^2 .|> _^2 .|> _^2 in the future #24990) for map-like function chaining.

We also have

((([-1,2,3].^2).^2).^2)

These have the additional advantage over chaining map in that they both fuse into a single loop with no temporary arrays.

And you can also chain map directly:

[-1,2,3] |> y->map(x -> x^2,y) |> y->map(x -> x^2,y) |> y->map(x -> x^2,y)

@al6x
Copy link

al6x commented Aug 24, 2020

@stevengj try use pipes for functions with many arguments, don't know how things are now, but previously julia pipe had limitations.

This last line [-1,2,3] |> y->map(x -> x^2,y) |> y->map(x -> x^2,y) |> y->map(x -> x^2,y) - you basically doing exactly what uniform function call does, but in an verbose and strange way.

@StefanKarpinski
Copy link
Sponsor Member

Being able to write data processing pipelines from left to right is a happy accident that x.f(y) method call syntax happens to be good for. That syntax already has a meaning in Julia so there's no way we're changing the meaning of it in any 1.x release (and pretty unlikely in 2.0, to be honest). The most constructive direction this conversation can take is thinking of alternative ways to get the benefits of the left-to-right processing flow. Insisting that one wants the [-1, 2, 3].map(x -> x^2).map(x -> x^2).map(x -> x^2) syntax to work as is in Julia is not going be productive.

@stevengj
Copy link
Member

stevengj commented Aug 24, 2020

Yes, you can write [-1,2,3] |> y->map(x -> x^2,y) |> y->map(x -> x^2,y) |> y->map(x -> x^2,y) in an ugly way, but you could also just write [-1,2,3] .|> x->x^2 .|> x->x^2 .|> x->x^2 (or [-1,2,3].^8) — we have plenty of other ways to construct this in Julia.

I think the proposal with the most support (#24990) has been to implement nicer multi-argument function chaining in the future with something more flexible like the Scala-like syntax [-1,2,3] |> map(_^2,_) |> map(_^2,_) |> map(_^2,_).

Furthermore, the proposed "uniform function call syntax" wouldn't even solve this, because it would transform [-1, 2, 3].map(x -> x^2) to map([-1, 2, 3], x -> x^2), which is the wrong argument order for Julia.

Julia isn't an OO language. The first argument isn't special for dispatch, so OO-like syntax here is a non-starter.

@cloudhan
Copy link

Why is this issue closed?

is answered by

That syntax already has a meaning in Julia so there's no way we're changing the meaning of it in any 1.x release (and pretty unlikely in 2.0, to be honest).

And it is a valid answer.

The first argument isn't special for dispatch

the first elements in a list is neither special so head or first or car should be deleted from any language. I am fine with it.

Asides from this, I am start recognizing the community being toxic and fundamentalism.

@StefanKarpinski
Copy link
Sponsor Member

Cool reaction, dude. Devs take the time to explain why something isn't a good fit for this language. You call them toxic for explaining it to you. There's nothing "fundamentalist" about this. If you're going to engage here, at least engage with what someone has actually written. The first argument is totally non-sequitur: it's common to want to take the first element from a collection, so there is a first function. It's also common to want to take the last element, so there is a last function. What in the world does that have to do with method/function call syntax?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants