JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.43k stars 5.45k forks source link

permit a<space>.b syntax, if possible #17305

Open xitology opened 8 years ago

xitology commented 8 years ago

Julia 0.4 deprecated and Julia 0.5-dev made an error the a<space>.b syntax:

julia> a .b
ERROR: syntax: space before "." not allowed in "a ."

It prevents me from using a "method-chaining" pattern, which is common in some languages and frameworks:

(obj.meth1(...)
    .meth2(...)
    .meth3(...))

Admittedly, it is less common in Julia as it lacks special method syntax, but I happen to use it in my experimental Julia-based query DSL:

@query(
    table
    .filter(predicate)
    .group(attribute)
    .select(...))

It is possible to un-deprecate this syntax? I found the original commit 28e7bd45, but it has no justification for deprecating it in the first place.

tkelman commented 8 years ago

If you're making a macro dsl, you should probably use an infix operator.

xitology commented 8 years ago

@tkelman From my perspective, . is an infix operator since (a+b).(c+d) is well-formed Julia syntax.

JeffBezanson commented 8 years ago

This was discussed extensively in #11891 and #7232.

Since julia doesn't require semicolons at the ends of statements, the pattern

(obj.meth1(...)
    .meth2(...)
    .meth3(...))

doesn't work well because after obj.meth1(...) we don't know to expect any further expressions. However putting the dot at the end of the line solves this. The following parses fine:

@query(
    table.
    filter(predicate).
    group(attribute).
    select(...))
gabrielgellner commented 8 years ago

@xitology but an infix operator that doesn't allow spaces ;) Is there any reason not to use |> instead? feels like it would read better. I like that dot is forced to be written tightly. We have so many uses of . now with the broadcast notation, I think it is important to keep the use of it constrained so that code is written in a more consistent manner.

xitology commented 8 years ago

My (ab)use of Julia syntax is certainly unorthodox and I'm sure will be frowned upon by Julia developers. The . is used as a monadic composition operation (like >=>), while for |> I use :. You can see real examples here and here. As you can see, . is being used to mimic attribute access and using any other operator in place of . will make the syntax much more heavyweight.

That said, I don't want to make it a discussion of my stylistic choices in DSL design. This is hardly a great argument for changing language parser. I just used it as one example where one might prefer method-chaining.

As opposed to f<space>(, which is, arguably, poor style, and shouldn't appear in real code, it's not unreasonable to see a<space>.b in code like this:

somefunc(
    big_expr
    .attr)

Indeed, you could write

somefunc(
    big_expr.
    attr)

but I think the former is more stylistically appropriate and would be the first choice for most programmers. I understand that you cannot use it without wrapping in parentheses, but that's also the case in Python.

I don't know if it interferes with other language features (like @macro<space>()), but if not, I just don't see why it must be prohibited.

xitology commented 8 years ago

I'm sorry for spamming this issue, but here's another argument. Even though currently method-chaining APIs for Julia do not exist, it looks like you are going to enable overriding the attribute access operator (see #1974). If so, I'm sure method-chaining APIs will appear even without macros, so this issue will probably be raised again.

KristofferC commented 6 years ago

There doesn't seem to be anything actionable here.

clarkevans commented 6 years ago

@JeffBezanson Is it that it would be inconvenient in the parser to support this, or that it's just plain impossible to support? This is a particularly important case for us for code ergonomics reasons.

JeffBezanson commented 6 years ago

It's certainly possible; indeed we allowed it prior to v0.4. We decided to remove it in #11891, but it seems to me the argument there wasn't overwhelming, and indeed a couple people said we might want to revisit this if we added dot overloading, which we did.

Could you give an example of the specific syntax you'd like to use? One thing we probably cannot support is

obj.method1(x)
   .method2(y)
   .method3(y)

However it already works if you put the dots at the end of lines:

obj.method1(x).
    method2(y).
    method3(y)

Some people seem to consider that completely unacceptable, but that seems overly fussy to me.

StefanKarpinski commented 6 years ago

Would it be possible to at least allow parsing the former syntax inside of parens? In either case, it would be a nonbreaking change.

JeffBezanson commented 6 years ago

If we removed the rule about spaces before dots, yes, I think that would just work. People will surely then try it in statement position though.

clarkevans commented 6 years ago

First, thank you for looking again at this ticket. As Kyrylo noted above, our primary use case is "abusing" the @macro syntax parser in order to construct a familiar "fluent interface". As such, I don't think using leading periods "in statement position" would be particularly common. Currently, we have single line queries, such as below.

@query(department.filter(count(employee)>2).count())

As the queries get much longer and involved, we'd like a way to make them multi-line. Of course, we could ask users to use the period at the end of each line, as Jeff noted. I don't think it is particularly nice to read or customary. Further, as you append new operators to the end, you have to modify the previous line for the trailing period, and hence it shows up as 2 lines changed in source code control rather than a single insertion. Here's how we'd prefer to line-break the previous query for readability.

# doesn't work
@query(
  department
 .filter(count(employee)>2)
 .count()
)

One potential alternative is the begin/end variant. However, I can't collapse it all into a single line without adding periods; this is an odd rule to explain to casual users. That said, adding another query in the chain doesn't involve modifying previous lines. So, this is probably the alternative we'd promote if leading-periods is not permitted.

@query begin
    department
    filter(count(employee)>2)
    count()
end

We have tried to use |> at the beginning of each line, and that works, however, |> is a visually large indicator and gets to be quite ugly in single-line forms with involved compositions. The period (.) is really the best "non-distracting" indicator for expression composition. Of course, long term, we may need to implement our own parser. That said, our desire to create a "fluent" interface for a complex library will not be uncommon.

JeffBezanson commented 6 years ago

Ok, we can make the parenthesized @query( ) case work just by removing the error. Beyond that, the "fluent" syntax comes from languages that require semicolon statement delimiters, so we're probably stuck there.

clarkevans commented 6 years ago

Kyrylo also mentioned it's usefulness in regular function calls.

somefunc(
    big_expr
    .attr)

Is this the same production? Also, I think Kyrylo noted that having parenthesis required is the case for Python as well.

JeffBezanson commented 6 years ago

Yes.