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

provide a line continuation syntax for nicer macro calls #18612

Open
musm opened this issue Sep 21, 2016 · 23 comments · May be fixed by #35336
Open

provide a line continuation syntax for nicer macro calls #18612

musm opened this issue Sep 21, 2016 · 23 comments · May be fixed by #35336
Labels
speculative Whether the change will be implemented is speculative

Comments

@musm
Copy link
Contributor

musm commented Sep 21, 2016

currently

@inline @inbounds @another @one @for @fun function foo(x)
    x = x + 1
    x += 1
    return x + 1
end

Proposal: introduce a line continuation syntax, e.g. #\ (like how C uses \ or we could borrow from C and straight up use \, however # is nice since it already signifies a comment so it's more familiar) so that the following works

proposal

@inline @inbounds @another @one @for @fun #\
function foo(x)
    x = x + 1
    x += 1
    return x + 1
end

Motivation: it becomes hard and ugly (personal opinion) to identify functions when prefixed with multiple macros. Arguably the fact I'm defining a function should be front and center. In the current syntax one has to follow the chain of macro calls before being able to reach and identify the function signature. This proposal places the function signature front and center and allows the macro calls to placed above the signature.

More examples

currently

function foo(x,y)
    a = exp(1.3) 

    @simd @inbounds for i = 1:10
        x[i] = i + 2 + a*sin(1.0)
        y[i] = i + y[i]
    end
    x *= a
    return x, y
end

proposal

function foo(x,y)
    a = exp(1.3) 

    @simd @inbounds #\
    for i = 1:10
        x[i] = i + 2 + a*sin(1.0)
        y[i] = i + y[i]
    end
    x *= a
    return x, y
end

Again here it's hard to quickly identify the for loop due to the macro calls that precede the for loop definition. In the proposed syntax it's no more difficult than if no macros were used and one can quickly identify the for loop.

The ultimate goal here is to keep the syntax simple and pretty, even if one has to use several macro calls

@ararslan
Copy link
Member

ararslan commented Sep 21, 2016

Currently you can just put the code in a begin/end block, or use the () syntax for macros. For example,

@simd @inbounds begin
    for i = 1:10
        x[i] = i + 2 + a*sin(1.0)
        y[i] = i + y[i]
    end
end

or

@simd @inbounds(
for i = 1:10
    x[i] = i + 2 + a*sin(1.0)
    y[i] = i + y[i]
end)

IMO both of these are quite readable, or at least not less so than #\.

@musm
Copy link
Contributor Author

musm commented Sep 21, 2016

Right I'd argue the first is ugly and verbose the second is passable
....another ugly way as @TotalVerb mentioned is:

@simd @inbounds @othermac #=
=#for i = 1:10
    x[i] = i + 2 + a*sin(1.0)
    y[i] = i + y[i]
end)

Don't get hung up on #\ that was merely a suggestion to gauge feedback on the idea.

E.g. using \

@inline @inbounds @another @one @for @fun \
function foo(x)
    x = x + 1
    x += 1
    return x + 1
end

@simd @inbounds @othermac \
for i = 1:10
    x[i] = i + 2 + a*sin(1.0)
    y[i] = i + y[i]
end

In any case, if this feels unnecessary by any of the higher ups feel free to close. The strongest argument I can find against this is that at most, code blocks shouldn't need more than 3 macros and so readability shouldn't be that impacted that a new syntax is needed.

@DNF2
Copy link

DNF2 commented Sep 21, 2016

Purely out of curiosity: What would happen if there was no line continuation syntax at all, that the macros were simply allowed to discard the leading newline and process the next valid expression? (Asking totally from a position of ignorance here.)

@yurivish
Copy link
Contributor

yurivish commented Sep 21, 2016

@okvs I like that idea a lot -- it would allow for using macros in Python-like decorator style to annotate the block it belongs to -- but I think it introduces an ambiguity right now when a macro is defined with no arguments.

I wonder how often macros without arguments are used. If the rules were changed to require () for calls with no arguments, I think that would make this feature possible without any additional syntax.

@DNF2
Copy link

DNF2 commented Sep 21, 2016

So the problem would be macros that take zero arguments. If macros could somehow tolerate being fed too many inputs, that could work, I guess, but I really don't know what I'm talking about here...

I would just have liked to be able to do something like this:

@simd @inbounds
    for i = 1:10
        x[i] = i + 2 + a*sin(1.0)
        y[i] = i + y[i]
    end

@stevengj stevengj added the speculative Whether the change will be implemented is speculative label Sep 21, 2016
@ararslan
Copy link
Member

@okvs That would mean that macros that take no arguments would need to be called as @something().

@Ismael-VC
Copy link
Contributor

Ismael-VC commented Sep 22, 2016

@ararslan

Currently you can just put the code in a begin/end block, or use the () syntax for macros.

No you can't do that (at least not always):

julia> @simd @inbounds(
       for i = 1:10
           x[i] = i + 2 + a*sin(1.0)
           y[i] = i + y[i]
       end)
Base.SimdLoop.SimdError("for loop expected")

julia> @simd @inbounds begin
           for i = 1:10
               x[i] = i + 2 + a*sin(1.0)
               y[i] = i + y[i]
           end
       end
Base.SimdLoop.SimdError("for loop expected")

Also what happens with multiple arguments? You are only taking into consideration one arg.

@ararslan
Copy link
Member

The @simd @inbounds was a bad example because it doesn't work anyway, no matter how you specify it.

julia> @simd @inbounds for i = 1:10
           println(i)
       end
ERROR: Base.SimdLoop.SimdError("for loop expected")

@TotalVerb
Copy link
Contributor

Indeed, although begin creates a new :block expression, wrapping in parentheses is identical to the parentheses-less variant when it goes to the macro.

@musm
Copy link
Contributor Author

musm commented Oct 12, 2018

reopening, the resolution/decision of #29273 should determine how this issue is closed.

@musm musm reopened this Oct 12, 2018
@c42f
Copy link
Member

c42f commented Feb 6, 2019

I think it's fairly clear from #29273 that the specific implementation proposed there won't be accepted. But I still regularly want this feature and after some experience using the alternative of "commenting out the linebreak" I still find that ugly and distracting. To copy/summarize some thinking from #29273 (comment):

I feel this whole thing might still fly if we could come up with a compelling enough pair of ascii chars for line continuation.

Some desirable properties:

  • It should be short but not steal useful character combinations. A character pair would be ideal here.
  • Any text after a line continuation and on the same line should be considered a comment. So ideally the line continuation pair would end with the existing comment character #.
  • It should be a syntax error in current julia so that non backward compatible code gives a clear syntax error in older versions and doesn't mean something entirely different.

There's not many viable character pairs which fit these criteria. .# is one option:

@inline @inbounds @another @one @for @fun  .#
function foo(x)
    x = x + 1
    x += 1
    return x + 1
end

@jekbradbury
Copy link
Contributor

If a macrocall always consumed a newline if present (even when it turns out that macrocall takes zero arguments), would that introduce an ambiguity or break existing code?

@c42f
Copy link
Member

c42f commented Feb 7, 2019

always consumed a newline if present

I'm confused by this comment. Macro calls currently use the newline to terminate the argument list. That's a pretty critical feature of the syntax!

@jekbradbury
Copy link
Contributor

D'oh, sorry, I see what the problem is now.

@pazner
Copy link
Contributor

pazner commented Apr 1, 2020

I often run into this issue (and the related issue #27533).

I think the proposals suggested in #29273 (comment) are quite good.

I took the liberty of implementing this in Julia's parser using the character combinations \# and .#. If people are interested, I can open a PR (or the existing PR #29273 can be modified to support this syntax). This could also be used to allow for where clauses on a new line (#31378).

What are the opinions on this?

@c42f
Copy link
Member

c42f commented Apr 1, 2020

Well I still think I had some good points about the design tradeoffs in #29273 (comment) but it didn't generate much discussion 🤷‍♂️ I'm certainly happy to continue the discussion here.

Generally I think it would be helpful to open a new PR for \# because having a candidate implementation is a great way to stimulate discussion (as you can see, #29273 generated useful ideas even though it's unlikely to be merged). If you implement \# we can try running PkgEval against it to see whether it does in fact break any packages. If it's non-breaking in practice we could consider it a minor change which can make it into 1.x.

Implementation details... I think \# should always lexed consistently as a line continuation token regardless of parser context (so not like what I did here: https://github.com/JuliaLang/julia/pull/29273/files#r218999435). There are some other design decisions to make. At minimum we need to decide whether \# is allowed based on whether it's currently parsing a macro... or we could just allow it in all cases. For a PR you could just allow it in all cases and see how that goes. (Another option is to go with the most conservative language change which is to only allow it while parsing macros; we can always relax that restriction later in a non-breaking way.) I'm not quite sure line continuation is the best way to allow where to continue a line — if that is doable without an explicit line continuation it might be better.

@c42f
Copy link
Member

c42f commented Apr 1, 2020

Another almost non-breaking option would be :# because in most circumstances having a : at the end of a line is already forbidden to help people coming from python get clear errors. (The exception is ternary syntax a ? b : c where the : is allowed at the end of line and could be succeeded by a #.)

Edit: actually there's some other cases where it's allowed. Eg, a = :

@tkf
Copy link
Member

tkf commented Apr 1, 2020

Was @# considered before? It may not be a nice syntax outside macros, though.


Edit: So, it'd be breaking because this works:

julia> @#
       inline f(x) = x
f (generic function with 1 method)

But do people use it? It is parsed like this

julia> """
       @#
       a
       """ |> Meta.parse |> dump
Expr
  head: Symbol macrocall
  args: Array{Any}((2,))
    1: Symbol @a
    2: LineNumberNode
      line: Int64 2
      file: Symbol none

@tkf
Copy link
Member

tkf commented Apr 1, 2020

So, it looks like you can implement line continuation as a macro

macro c(x)
    esc(x)
end

@#
c @inline @#
c @inbounds @#
c f(x, i) = x[i]

@c42f
Copy link
Member

c42f commented Apr 1, 2020

I remember thinking about @# though I'm not sure it was discussed.

I don't think anyone would be worried if @# stopped meaning what it currently does 😆

@ a is forbidden and yet the following is allowed:

@ #
a

@tkf
Copy link
Member

tkf commented Apr 1, 2020

@c is my favorite macro now 😀. I can make Julia look like Fortran.

OK, but thinking a bit more seriously, I think @# cannot be used since some code might be parsed as a valid Julia code with current parser:

@inbounds @#
f(x)[i] = 1

I think a new syntax should just throw an error in old parser as much as possible. I think it's too easy to get a currently valid code with @#.

@c42f
Copy link
Member

c42f commented Apr 1, 2020

To get the full beauty of Fortran shouldn't you have that c in the 6th column :-) ?

It's true that having both backward and forward compatibility would be nice. It wouldn't be great if the older parsers would happily interpret a new line continuation syntax as valid syntax meaning something completely different. It's true @# is very prone to this and \# seems somewhat the same. .# seems better from that point of view and :# might also be fine.

The implementation would be the same in any case.

@pazner pazner linked a pull request Apr 1, 2020 that will close this issue
@pazner
Copy link
Contributor

pazner commented Apr 2, 2020

@c42f I think your suggestions are good. I made a PR (#35336) for \#, we can continue to discuss there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
speculative Whether the change will be implemented is speculative
Projects
None yet
Development

Successfully merging a pull request may close this issue.