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

change order of by arguments to support do syntax #580

Closed
simonbyrne opened this issue Apr 20, 2014 · 13 comments
Closed

change order of by arguments to support do syntax #580

simonbyrne opened this issue Apr 20, 2014 · 13 comments

Comments

@simonbyrne
Copy link
Contributor

If the function was the first argument to by, it would be possible to use do syntax for anonymous functions:

by(iris, :Species) do d
    mean(d[:SepalWidth])
end

Also, what about a convenience macro for creating dataframes from vectors? For example

macro dataframe(vars...)
    Expr(:call,:DataFrame,[Expr(:kw,var,var) for var in vars]...)
end

by(iris, :Species) do d
    AvgSepalWidth = mean(d[:SepalWidth])
    AvgSepalLength = mean(d[:SepalLength])
    @dataframe AvgSepalWidth AvgSepalLength
end
@simonbyrne
Copy link
Contributor Author

One other advantage: this would mean that it would be possible to use a varargs list of grouping variables:

by(dataframe, :Var1, :Var2, :Var3) do d
...
end

@nalimilan
Copy link
Member

Sounds like a good idea. Could probably be a more general rule: functions should always be the first argument, because of this syntax feature.

Regarding the macro, the only difference with the DataFrame constructor is that object names are used as column names? Would it make sense to call it @DataFrame rather, since it's so similar to the constructor?

@simonbyrne
Copy link
Contributor Author

@nalimilan Yes, that's all it does:

julia> macroexpand(:(@dataframe foo bar))
:(DataFrame(foo=foo,bar=bar))

Keeping the capitalization consistent also seems like a good idea.

@tshort
Copy link
Contributor

tshort commented Apr 20, 2014

I've pondered this too as part of thinking about use of macros and LINQ-like syntax:

JuliaData/DataFramesMeta.jl#5

To me, it's a close call. Having the DataFrame object first seems more consistent and follows what other LINQ libraries do.

I'm starting to think we should define methods with both argument orderings. Then, we get do syntax plus more natural usage for normal usage.

Either way, whatever we do for by, we should follow suit for other LINQ-like functions. If we move the function to the first argument position, we can still leave the macro versions as is.

@kmsquire
Copy link
Contributor

Defining both method is actually pretty common in base. +1 for that.

Kevin

On Sunday, April 20, 2014, Tom Short notifications@github.com wrote:

I've pondered this too as part of thinking about use of macros and
LINQ-like syntax:

JuliaData/DataFramesMeta.jl#5JuliaData/DataFramesMeta.jl#5

To me, it's a close call. Having the DataFrame object first seems more
consistent and follows what other LINQ libraries do.

I'm starting to think we should define methods with both argument
orderings. Then, we get do syntax plus more natural usage for normal
usage.

Either way, whatever we do for by, we should follow suit for other
LINQ-like functions. If we move the function to the first argument
position, we can still leave the macro versions as is.


Reply to this email directly or view it on GitHubhttps://github.com//issues/580#issuecomment-40907151
.

@nalimilan
Copy link
Member

Yeah, if defining both doesn't create any issues, it's a possible solution. But then for consistency the version with the function as first argument should be reserved to the do syntax when advertising.

Or would it make sense for Julia to automatically detect what's the position of the argument the anonymous function corresponds to? It's pretty obvious: the first argument accepting a function should be chosen. Doesn't sound like a very complex rule for users, and it would avoid creating many duplicate functions with swapped arguments, which is going to create a mess if it happens to often.

@StefanKarpinski
Copy link
Member

That would mean that the meaning of an expression depends on what methods are defined for a function, which is no good. It also wouldn't interact very well with ::Any arguments – it's not uncommon to leave the type of an argument unconstrained but that doesn't mean that you're expecting a function.

@nalimilan
Copy link
Member

Ah, right. To me Any arguments could be considered as OK for functions (too bad for such functions, but still works for others). But indeed having the behavior of a block change when a new method is defined is pretty bad. One could rely on argument names (e.g. fun), but names do not matter for positional arguments anywhere else. Ah...

@StefanKarpinski
Copy link
Member

I've considered making do-block syntax sugar for passing a keyword argument. However, since we don't dispatch on keyword arguments, that causes some conflicts – namely in the cases where foo(f::Callable, a::A, b::B) and foo(a::A, b::B) both mean something.

@nalimilan
Copy link
Member

In that case you'd need to always pass f as a keyword argument. Does this create any problems, except for the obvious one of breaking compatibility?

@StefanKarpinski
Copy link
Member

No, the idea would be that

foo(a, b) do x
  # stuff
end

would be shorthand for this:

foo(a, b, fun = x -> begin
  # stuff
end)

or something like that.

@nalimilan
Copy link
Member

Yeah, that's what I meant.

@garborg
Copy link
Contributor

garborg commented Jan 12, 2015

Implemented in #643.

@garborg garborg closed this as completed Jan 12, 2015
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

6 participants