I think it is reasonable for users to write code in the latter style, but it errors, can this be improved?
julia> [2*3 3*4;
3*4 2*3]
2Γ2 Matrix{Int64}:
6 12
12 6
julia> [*(2, 3) *(3, 4);
*(3, 4) *(2, 3)]
ERROR: MethodError: no method matching *(::Int64, ::Tuple{Int64, Int64})
The function `*` exists, but no method is defined for this combination of argument types.
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...)
@ Base operators.jl:596
*(::Real, ::Complex{Bool})
@ Base complex.jl:330
*(::Integer, ::CartesianIndex{N}) where N
@ Base multidimensional.jl:129
...
Stacktrace:
[1] top-level scope
@ REPL[2]:1
This example indicates that the infix usage is the standard way to call *.
The prefix usage (i.e. function-like api) of * is thus rendered somewhat questionable.
Or, if we want the prefix style, we will have to write Base.:*(a, b)
But this point is not reflected in the docstring (which seems the usage *(a, b) is formal and standard). I think this point needs to be stressed.
But the docstring suggests the validity of this usage, in abundance
*(x, y...)
Multiplication operator.
Infix x*y*z*... calls this function with all arguments, i.e. *(x, y, z, ...), which by default then calls (x*y) * z * ... starting from the left.
...
Examples
β‘β‘β‘β‘β‘β‘β‘β‘
julia> 2 * 7 * 8
112
julia> *(2, 7, 8)
Another example
help?> β
"β" can be typed by \in<tab>
search: β
in(collection)
β(collection)
Create a function that checks whether its argument is in collection, i.e. a function equivalent to y -> y in collection. See also insorted for use with sorted
collections.
The returned function is of type Base.Fix2{typeof(in)}, which can be used to implement specialized methods.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
in(item, collection) -> Bool
β(item, collection) -> Bool
As Oscar explained, [1 *(2, 3)] is parsed as [(1 * (2, 3))]. For the time being, you can keep using the more common infix notation, which does not have this issue, or place more parentheses to clarify your intention: [1 (*(2, 3))].
Yes, it is certainly valid, as all infix operators have a prefix call form.
It is just not idiomatic in general. Certainly not for the case in your example: each * has two operands, and they are constants so they can be folded, and the whole operation is rather cheap.
But my biggest concern would be code readability. Yes, seasoned Julia users will figure out what *(a, b) is, but they will still be puzzled why you wrote it that way. Interrupting the flow of reading code for no good reason is not advisable IMO.
I think the real moral of this story is that parsing is very fussy in space-sensitive syntax mode, which occurs inside of array concatenation and macro calls. One has to be extra careful there, especially when it comes to infix operators. Everywhere else, prefix call syntax is unproblematic.
If we were going to change anything I think we should make asymmetrical spacing around infix operators an error. With that done, this would be unambiguously parsable as a prefix call.
Iβd say the moral of the story is that any nontrivial expression in an array instantiation needs to have parenthesis around it. As far as Iβm concerned,
[(*(2, 3)) (*(3, 4));
(*(3, 4)) (*(2, 3))]
is the one and only βcorrectβ way of writing this, and
[(2*3) (3*4)
(3*4) (2*3)]
is also the βcorrectβ way of defining the matrix with an infix operator. I consider the parsing in the context of an array constructor of any not completely trivial expression as βundefinedβ and not guaranteed to be stable between Julia versions. Hopefully, this is something that linting tools like JuliaFormatter can handle automatically.
Maybe a way to define the behavior in a particularly easy and intuitive way is to just not allow spaces in an array element unless the expression is enclosed in parentheses. I donβt know if that would be considered βbreakingβ, or merely changing βundefinedβ to βwell-definedβ.
This is much more of a βdonβt hold it that wayβ issue than a fundamental problem, IMO.
Yes, definitely. Might also be good to disallow misleading spacing where operators with lower precedence have less space around them. Eg 1+2 * 3 or a:b + 1. Also potentially mixing of operators with non-obvious precedence relationships, which I canβt think of an example of off the top of my head.
A related point is here: I think julia should discourage the users using infix operator across lines, when they are confronting lengthy expressions. New users may tend to write
Personally, I would never write that code like that and havenβt seen anything be else do it, so I think thatβs unlikely for be a style suggestion with unanimous support. I think the first two ways to write it are both fine.
Why? Formulas spanning multiple lines have been used for more than centuries at this point in mathematics and the sciences. They are understood well, and work fine in code, especially if you include parentheses.
Feel free to use any notation you like, but bending Julia towards non-standard notations for math is unlikely to get any significant buy-in from others. Pretty much the whole point of Juliaβs surface syntax is to get infix, otherwise we could have S-expressions and keep the parser very, very simple.