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

at-dot macro for adding dots to function calls #20321

Merged
merged 11 commits into from
Feb 2, 2017

Conversation

stevengj
Copy link
Member

@stevengj stevengj commented Jan 30, 2017

The PR adds a new macro @. (and parser support to convert this to @__DOTS__ @__DOT__ @__dot__) that adds dots to every function call, operator, and assignment in an expression. For example,

@. x += 3x^2 - sqrt(5x)

is equivalent to x .+= 3 .* x.^2 .- sqrt.(5 .* x).

You can opt-out of the dots for specific function calls by splicing with $, e.g. @. sqrt($sort(abs(x))) is equivalent to sqrt.(sort(abs.(x))). (This also works for function objects spliced directly into expressions by other means, e.g. it allows @. to work in conjunction with @views, and it should similarly allow some interoperability with other macros.)

cc @StefanKarpinski, since @. was (I think?) his suggestion.

@stevengj stevengj added the domain:broadcast Applying a function over a collection label Jan 30, 2017
@nalimilan
Copy link
Member

The feature is really cool, but I wonder whether a more descriptive name wouldn't be in order. @broadcast?


```@docs
Base.broadcast
Base.Broadcast.broadcast!
Base.__DOTS__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want to document the macro, not the helper function, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, whoops.

base/exports.jl Outdated
@@ -1382,6 +1382,7 @@ export
@polly,

@assert,
@__DOTS__,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why export this if only lowering should see it under this name?

@stevengj
Copy link
Member Author

stevengj commented Jan 30, 2017

I'm of two minds about the name.

  • If it were an ordinary macro name, I would prefer @dots (to emphasize that it turns things into fusing dot calls, not just broadcast calls). This has the advantage of not requiring any parser sugar.

  • @. is shorter and thus more lightweight and tempting to insert into code. And the specialness of the macro name is maybe reflective of the specialness of the dot-call sugar?

@StefanKarpinski, you suggested the @. syntax, do you have any further thoughts on why you preferred that over an ordinary macro name?

@stevengj
Copy link
Member Author

(The other thing to keep in mind with @dots vs. @broadcast is that, going forward, I expect that only a minority of Julia users will call broadcast directly. More people will be familiar with the dot syntax — indeed, more people already are for operators, thanks to Matlab.)

@StefanKarpinski
Copy link
Sponsor Member

My thinking was that @. will be the easiest to remember: "I have all these dots all over the place, gee, isn't there a way to avoid those... oh yeah just write @. at the beginning. That was easy!" Otherwise people need to remember what name replaces all the dots. Keep in mind that many users will use f.() syntax and .+ operators without ever knowing or caring that they're implemented as higher order calls to broadcast. To them it will just be mysterious why one uses @broadcast to put dots on everything. There's also the advantage that @. can't possibly collide with any macro name.

@StefanKarpinski
Copy link
Sponsor Member

I see that you just said effectively the same thing, @stevengj :)

@StefanKarpinski
Copy link
Sponsor Member

Wouldn't it be cleaner to just allow the parser to recognize @. as a valid macro and internalize it as calling the $(Symbol("@.")) expander function?

@stevengj
Copy link
Member Author

stevengj commented Jan 31, 2017

@StefanKarpinski, there is a lot of code that assumes a certain form for identifiers. e.g. the documentation system broke when I tried to attach a docstring to @. ...I could get it to work, but I was worried that I'd have to insert special-case code all over the place. The macro seems a lot easier to work with if it is just sugar for a normal Julia identifier.

@tkelman, the reason I export @__DOTS__ and have @. expand to the unqualified @__DOTS__ (rather than to Base.@__DOTS__) is that this way you can override @. inside a module by defining your own __DOTS__ macro, which seemed to add flexibility. (That's also why the __DOTS__ name is documented in the @. docstring.)

@StefanKarpinski
Copy link
Sponsor Member

Makes sense. It's definitely a nit, but should this be called @__dot__ instead? Singular because it corresponds to the syntax @. which has only a single dot. This happens to make it correspond, but default, to a behavior which puts many dots on things, but that might not be what other people define it to do. In generally, I've found that syntactic features with special names, like getindex and setindex! end up being less confusing if the special names correspond to the syntax rather than the default meaning of that syntax. The lowercase part is because we've usually used special names like __init__ have been lowercase. Otoh, we have @__FILE__ and @__DIR__, so that could be argued either way – I just thought we should be cognizant of the choice.

@stevengj
Copy link
Member Author

I'm fine with @__dot__ or @__DOT__. I made it uppercase to correspond to the other "special" macros, but I don't have any strong preference.

@tkelman
Copy link
Contributor

tkelman commented Jan 31, 2017

Lowercase would be preferable to me as it behaves more like a function modifier, and less like something from the C preprocessor which many of the other dunder-caps macros behave like.

Copy link
Sponsor Member

@StefanKarpinski StefanKarpinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bravo. That's a lovely PR.

Expr(x.head, dotargs...)
end
end
end
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should remember in the future that this is a perfect example of why you cannot do serious metaprogramming with strings and why you need data structures for it – a complex, recursive syntax transformation.

All mathematical operations and functions are supported for arrays
See also the [dot syntax for vectorizing functions](@ref man-vectorized);
for example, `f.(args...)` implicitly calls `broadcast(f, args...)`.
Rather than relying on "vectorized" methods of function like `sin`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "of function like sin" be "of functions like sin"?

@@ -244,6 +247,28 @@ let x = [1:4;]
@test sin.(f17300kw.(x, y=1)) == sin.(f17300kw.(x; y=1)) == sin.(x .+ 1)
end

# splice escaping of @.
let x = [4, -9, 1, -16]
@test [2, 3, 4, 5] == @.(1 + sqrt($sort(abs(x))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsequential, but missing a space between the @. and following expression?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, I think in this case the macro is just being called with the parenthesized form, in which case there shouldn't be a space after @..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, of course! Thanks for straightening me out :).

@test y === x == [7,6,5,4]
x[1:2] .= 1
@test y === x == [1,1,5,4]
x[1:2] .+= [2,3]
@. x[1:2] .+= [2,3]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you leave the dots in place here and just below to exercise @. when dots already exist, or did you mean to remove the dots here and just below? (A comment or two on these tests explaining your intention might help those who come after?)

test/subarray.jl Outdated
@@ -479,7 +479,7 @@ Y = 4:-1:1

@test isa(@view(X[1:3]), SubArray)

@test X[1:end] == @view X[1:end]
@test X[1:end] == @. @view X[1:end]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was your intention here to exercise the interaction of @. with @view? If so, perhaps add a comment to that effect?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even better, put it in a testset. 🙂

Copy link
Member

@Sacha0 Sacha0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! (Review regrettably brief.)

@stevengj
Copy link
Member Author

stevengj commented Feb 2, 2017

AppVeyor failure seems like an unrelated network glitch: Cannot clone ColorTypes from https://github.com/JuliaGraphics/ColorTypes.jl.git. Failed to receive response: The server returned an invalid or unrecognized response

@stevengj
Copy link
Member Author

stevengj commented Feb 2, 2017

Should be good to merge?

@StefanKarpinski StefanKarpinski merged commit 2e0290a into JuliaLang:master Feb 2, 2017
@tkelman
Copy link
Contributor

tkelman commented Feb 2, 2017

nevermind, dunno what I was looking at

@GeoffChurch
Copy link

Just out of curiosity, was there a reason for the opt-out $ coming before the function name? I thought it would have inherited the broadcast dot placement, i.e.

@. sqrt(sort$(abs(x)))

instead of

@. sqrt($sort(abs(x)))

@stevengj
Copy link
Member Author

@GeoffChurch, $symbol is the standard way of interpolating things directly into expressions in Julia.

@GeoffChurch
Copy link

Oh that makes sense, thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:broadcast Applying a function over a collection
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants