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

Disallow @macroname x, y? #12021

Closed
simonster opened this issue Jul 6, 2015 · 25 comments
Closed

Disallow @macroname x, y? #12021

simonster opened this issue Jul 6, 2015 · 25 comments
Labels
needs decision A decision on this change is needed parser Language parsing and surface syntax speculative Whether the change will be implemented is speculative
Milestone

Comments

@simonster
Copy link
Member

It's not immediately obvious to many people that @macroname x, y will be parsed as @macroname((x, y)) and not @macroname(x), y or @macroname(x, y). This has been a source of confusion for use of @compat (JuliaLang/Compat.jl#71, JuliaLang/Compat.jl#111, JuliaLang/Compat.jl#116, JuliaLang/Compat.jl#136, probably other places as well), but I'm not sure I've ever seen it used intentionally. In the interest of usability, we might just want to throw an error.

cc @sbromberger

@simonster simonster added speculative Whether the change will be implemented is speculative needs decision A decision on this change is needed labels Jul 6, 2015
@IainNZ
Copy link
Member

IainNZ commented Jul 6, 2015

Hit this today, funnily enough. We do have this construct in JuMP though:

@addConstraints mod begin
  stuff
end

Is that the same thing? We'd be happy to use the parentheses for this.

@joehuchette
Copy link
Member

Though

@addConstraint(mod, begin
  stuff
end)

is better IMO. +1 to this.

@ScottPJones
Copy link
Contributor

👍 This caused me trouble also

@simonster
Copy link
Member Author

@IainNZ That's not quite the same thing. @macroname x y parses as @macroname(x, y). That seems relatively unambiguous, since x y, f(x y), etc. would not be valid expressions. What bothers me is @macroname x, y (with a comma between x and y), which passes the macro :((x, y)) as its only argument.

But since you mention it, I wonder what the rationale is/was for allowing macro calls without parens. There is certainly a lot of code that calls macros without them, but I tend to agree that things are clearer with them.

@nalimilan
Copy link
Member

But since you mention it, I wonder what the rationale is/was for allowing macro calls without parens. There is certainly a lot of code that calls macros without them, but I tend to agree that things are clearer with them.

I think it allows for clearer code when using macros like @inbounds. It makes it closer to a built-in language keyword. When the macro applies to the whole expression (only one argument), parentheses don't add any value.

But +1 for your proposal.

@ivarne
Copy link
Member

ivarne commented Jul 6, 2015

@simonster This is definetly two different issues.

  1. Parsing of macros and tuples (require parentheses to dissambiguate).
  2. Confusion caused by parentheses less macro calls for 1 and N arg macro innovocations.

I like tuples without parentheses when the meaning is clear, but this issue seems to indicate that we should require parentheses when combined with macros.

I like the possibility to have 1 argument macros without parentheses (eg @inbounds begin .... end), but I haven't yet managed to internalize the parsing rules for multiple arguments separated by spaces, so I need to try and see what works each time. If we remove n>1 arg macrocalls without parentheses, that would make Julia a simpler language to learn and fully master.

@carlobaldassi
Copy link
Member

If we remove n>1 arg macrocalls without parentheses

-10¹⁰ for this, please don't. It's useful feature when using macros to create DSLs (like done by JuMP, ArgParse, etc.); differentiating between one and more args is inconsistent and annoying.

(I have no problems in removing the parenthesis-less tuples in macro arguments even though I occasionally use them, the distinction is in fact subtle.)

@rened
Copy link
Member

rened commented Jul 6, 2015

same as @carlobaldassi - DSLs like FunctionalData et al require the @somemacro a b c syntax - removing this would kill off a huge appeal of Julia

@yuyichao
Copy link
Contributor

yuyichao commented Jul 6, 2015

I agree @macro x, y might be confusing. However, the alternative syntax would be @macro (x, y), which would be even more confusing... (since it's different from @macro(x, y))

@yuyichao
Copy link
Contributor

yuyichao commented Jul 6, 2015

In another word, I find both @macro x, y and @macro (x, y) are confusing at the same level and it would be too much to disallow both. (Although the first one may couse more confusing in the context of Compat)

@tkelman
Copy link
Contributor

tkelman commented Jul 6, 2015

Personally I have a really hard time reading @rened's code that uses FunctionalData, when it's otherwise doing things that I would find useful. With the exception of the above begin end block syntax, most of the macro calls in code that uses JuMP use parens anyway. Much easier to visually parse when macros don't use the space-sensitive mode.

This issue's primarily about the comma-vs-tuple confusion though.

@rened
Copy link
Member

rened commented Jul 6, 2015

@tkelman yeah, I know, it looks very different. have to expand the documentation and add a tutorial. DSLs are always a tradeoff - another thing to learn, but they can make certain problems drastically easier to formulate / solve. I would not dare to heavily promote a pet DSL, but it would an absolute shame to loose the ability to implement/use them.

back on topic, I believe disallowing the @macro a, b seems ok on first sight, but having macro parsing work differently for a single case is also confusing. i'd rather not have special parsing rule for this case, but i'd be ok with both.

@ScottPJones
Copy link
Contributor

I don't have a problem with @foo a b c, which becomes a call to the macro foo(a,b,c), and passing a tuple to a macro, while a bit confusing because of the difference between @foo(a,b,c) and @foo (a,b,c), the real problem is the comma-less tuples, and not just in macro calls.

function foo(a,b,c)
    ....code....
    expr1,
   # comments
   # blah, blah, blah
   # blah, blah, blah
   expr2
end

The little comma above really changes everything.
If comma-less tuples weren't allowed, then the language would be a lot clearer with a lot of these edge cases.
Another example: [a, b, c] vs. [(a,b,c)] vs a,b,c and (a,b,c).
Parsing gets even more inconsistent when you consider how the syntax within [ ].
[[1,2,3],[4,5,6],[7,8,9]] gives a deprecation warning and a 9x1 array, instead of the 3x3 array
JavaScript, Python (and other languages) would return, however
[(1,2,3),(4,5,6),(7,8,9)] gives a vector with 3 length 3 tuples.

@johnmyleswhite
Copy link
Member

I've been pretty unhappy with using macros without parentheses for a long time since they require users to understand something subtle about how expression boundaries are defined in Julia.

I really think we should discourage people building complex DSL's on top of Julia's more obscure pieces of syntax and encourage them to use string-parsing instead. As far as I can see, there's almost no cost to making most DSL macros invocations look like, @dsl("1 + 2 + 3"), since macro authors can always make the first line of the macro call parse(str) to opt in to building a DSL on top of Julia's existing syntax. But using special string literals gives you the freedom to define custom syntax and the ability to make editors use custom syntax highlighting to elucidate your DSL. The other big win is that we can support much weirder DSL's (using custom parsers) without having to lock Julia into a certain parsing strategy for less common operators like |>.

@rened
Copy link
Member

rened commented Jul 7, 2015

@johnmyleswhite I agree, very good suggestion! But I would like to avoid the explicit string notation. That would feel as clumsy as "metaprogramming" in matlab or other interpreted languages and would be a huge step back. To have to put this around every line is tedious, multiline would be odd, you would loose syntax highlighting (numbers are still numbers, e.g.), etc.

FWIW, I am not involved in the |> discussion for the very reason that I think this should live in packages, using DSLs. I don't need / necessarily want |> in the core language.

Building on @johnmyleswhite 's suggestion, what about the following:

stringmacro mymacro(a::String)
    ...
end
$mymacro problem <-> specific -> notation

This allows the parser to do whatever it wants in standard macros (like this issue proposes), and DSLs can be expressed with less hacks using stringmacro. Invokation with @mymacro or $mymacro or similar. But forcing people to use @dsl("1+2") is just unnecessarily punishing people that want to provide a problem specific notation for their packages.

Please let's not take away the wonderful, unique ability of Julia to write clean, custom DSLs for scientific computing.

@johnmyleswhite
Copy link
Member

Glad you're sympathetic, @rened. Isn't what you're describing essentially equivalent to http://julia.readthedocs.org/en/latest/manual/metaprogramming/#non-standard-string-literals

@rened
Copy link
Member

rened commented Jul 7, 2015

@johnmyleswhite Thanks for the link! Yes, except for the fact that strings are exactly the thing I want to avoid ;-)

The semantic intent of a line should be obvious IMO ("is this a string/data? are these instructions?"). And syntax highlighting is really important for legibility. Try it out on a non-DSL piece of Julia code, and put 2/3rds of the lines in between mystr" and " - that's not much fun... clumsy to edit, harder to read, more bugs, worse user experience, semantically questionable. Huge step back.

So yes, what I am proposing is functionally the same as non-standard string literals, except for the string part. I know this feels like a detail if you don't need the functionality, but if suddenly most of your code is string literals... total nightmare.

@tkelman
Copy link
Contributor

tkelman commented Jul 7, 2015

The semantic intent of a line should be obvious IMO

Some macro DSL's make that inherently non-local since you have to understand what the macro is doing and how it works. If the macro arguments are just a list of space-separated expressions, then syntax highlighting only makes a difference within each of the expressions. And any expressions that have any infix operators in them might be easily confused with the macro-argument-separator spaces.

@rened
Copy link
Member

rened commented Jul 7, 2015

@tkelman regarding the topic discussed in this issue - yes, you are right, of course. My comment was more meant in response to @johnmyleswhite suggestion for generally handling DSLs using the @dsl("1+2") approach. We should probably move that discussion to its own issue instead of highjacking this one (sorry for that).

@carlobaldassi
Copy link
Member

Well, my opinion is pretty much diametrically opposed to what @johnmyleswhite said, so it boils down to personal preference I think. I could get into more detail but I don't expect it would be very profitable and it's not the main focus of this issue anyway.
But to summarize the way I see it, removing parentheses-less macros for multi-arg calls is:

  • A breaking change (obviously, but let's just stress that)
  • Unnecessary, since if you want to use parentheses there's nothing stopping you at the moment — in other words, it's a net removal of features, with benefits which are at least debatable
  • Unnecessary since it doesn't seem to have ever come up until now, and never in a concrete context
  • Inconsistent and annoying in that it creates a difference between calls with one argument and calls with multiple arguments
  • Visually clumsy ;)

@elcritch
Copy link

I second the points @carlobaldassi made.

@mauro3
Copy link
Contributor

mauro3 commented Sep 18, 2015

I think this would need #7669 fixed.

@Tetralux
Copy link
Contributor

I think that the following rules should hold.

  • @foo x, y -> @foo(x, y)
    Good for seperating difficult-to-read expressions,
    such as @foo n > 0, iseven(n) @foo n > 0, x % 2 == 0
  • @foo x y -> @foo(x, y)
    Good for @inline, @inbounds; any kind of "annotation".
  • @foo(x, y) -> @foo(x, y)
    Expected, function-like behavior.
  • @foo (x, y) -> @foo( (x, y) )
    Should be considered a use of the syntax @foo x y, but with only one argument: (x, y).
    Alternatively, you could have this produce a warning which suggests you use @foo((x,y)).
    Personally, I would rather that it didn't raise a warning; when I'm using macros, I consider them "expression manipulators" first and foremost, and therefore, it makes more sense to me to be passing them expressions---as I would otherwise write those expressions; @foo x = 5.

This would also fix #7669; @foo((1,2), (x = 5)) and @foo (1,2) (x = 5) should both pass (1, 2) and :(x = 5) to the macro.

@StefanKarpinski
Copy link
Member

I vote no.

@StefanKarpinski
Copy link
Member

Let's not change this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs decision A decision on this change is needed parser Language parsing and surface syntax speculative Whether the change will be implemented is speculative
Projects
None yet
Development

No branches or pull requests