Arbitrary Infix Operators
Sometimes it's useful to have more verbose infix operators (e.g. isa). I would like to suggest adding a way to declare arbitrary identifiers <op> to be infix operators. Please consider the following syntax as a placeholder for the underlying idea.
@infix <precedence> <function> <op>
<precedence>would correspond to the currently existing listsprec-comparison,prec-plus,prec-times, etc. inside julia-parser.scm. Hence, allowed values should be predefined/limited.<function>is the function to be executed.<op>is the identifier of the new infix operator. This might be considered optional.
Example: Divisibility
@infix :comparison divides
divides(a, b) = b % a == 0
# Usage: `a divides b`
This syntax would also allow creating new infix operators from Unicode symbols, such that things like #39350 could just be done by a package. To simplify that workflow, it would be nice to define "tab completions" in a similar fashion.
@completion <ascii> <symbol>
Example: Independence (probability theory)
@infix :comparison indep ⫫
@completion "indep" ⫫
function indep(A, B) end
# Usage: `A \indep<TAB> B` or `A ⫫ B`
Extension to n-ary operators
The distinction between <function> and <op> allows this syntax to be extended to custom ternary operators.
@infix <precedence> <function> <op1> <op2> ...
Example: Conditional Independence
@infix :n_ary cond_indep ⫫ |
function cond_indep(A, B, C) end
# Potential usage: `A ⫫ B | C` or `@(A ⫫ B | C)`
The latter usage is currently invalid syntax.
Discussions concerning ternary operators (e.g. how to deal with precedence) might be better located in #39353.
Somewhat related (albeit with a broader scope) is: https://github.com/JuliaLang/julia/issues/16985
This would be a very large change as it would no longer make parsing canonical. Parsing itself would become stateful — and that's something that I know Jeff is very reticent to do.
Just so; there is a lot of complexity here since not only does parsing become stateful but there are new scoping issues, i.e. there needs to be a way to import/export syntax from a package vs. keeping it local.
there needs to be a way to import/export syntax from a package vs. keeping it local.
As it's already possible to use binary operators as identifiers,
julia> foo(_) = (_, _) -> true
cond_indep (generic function with 1 method)
julia> ⊕ = foo(42)
#1 (generic function with 1 method)
julia> 1 ⊕ 2
true
could this be handled the same as exporting functions?
module Foo
@infix :comparison compare comp
compare(_, _) = 42
export compare # only allows `compare(x, y)`
export comp # only allows `x comp y`
export compare, comp # allows both
@infix :comparison _comp_
_comp_(_, _) = 21
export _comp_ # allows both `_comp_(x, y)` and `x _comp_ y`
end
Rather than an @infix macro that turns a function into an infix operator, I think it'd be simpler to make @infix parse the current expression as infix, with every even-numbered top-level expression being treated as a function. So cmp(1, 2) could be written as @infix 1 cmp 2, and you could have e.g., @infix (1 + 2) (rand() < .5 ? cmp : +) (3 + 4), which would return -1 or 10. The @infix macro could also be placed before a begin ... end block to implicitly place @infix before every line.
The macro could also (somehow) take keyword arguments to convey the precedence and associativity of the infix-ed functions.
Any progress on this?