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

Customizable Ternary Operators #39353

Closed
jonas-schulze opened this issue Jan 21, 2021 · 7 comments
Closed

Customizable Ternary Operators #39353

jonas-schulze opened this issue Jan 21, 2021 · 7 comments
Labels
parser Language parsing and surface syntax

Comments

@jonas-schulze
Copy link
Contributor

jonas-schulze commented Jan 21, 2021

Is there an idiomatic way to create ternary operators?

I assume with some experience in macros, one could make @ci A ⫫ B | C work. This doesn't feel like a "native solution", though. Maybe we could maintain a list of ternary operators, similar to the binary operators in julia-parser.scm, which users could eventually define/overwrite like this:

function ⫫|(A, B, C)
    # ...
end

≡|(x, y, n) = mod(x, n) == mod(y, n)

Usage would then look like A ⫫ B | C (conditional independence) or x ≡ y | n.

This would only work for ternary operators whose parts consist of a single character. Another problem might arise if parts of it are a binary operator already (e.g. | from above).


Update: #39355 mentions an alternate syntax to define ternary operators that lifts the restriction of "single character parts". It also mentions a new calling syntax that lifts the problem of "reused parts": @(A ⫫ B | C) could describe a single n-ary operator call (currently invalid syntax).


Update 2: Here is that macro mentioned above (using instead of , has the same operator preference):

function cond_indep(A, B, C)
  @info "testing $A indep $B given $C"
  true
end

macro ci(ex::Expr)
  indep, A, ex2 = ex.args
  @assert indep == :
  given, B, C = ex2.args
  @assert given == :|
  quote
    cond_indep($A, $B, $C)
  end
end
julia> @ci 1 ⟂ 2 | 3
[ Info: testing 1 indep 2 given 3
true

Update 3: yet another macro doing the job:

macro ci(A, indep::Symbol, B, given::Symbol, C)
  @assert indep == :indep
  @assert given == :given
  quote
    cond_indep($A, $B, $C)
  end
end
julia> @ci 1 indep 2 given 3
[ Info: testing 1 indep 2 given 3
true
@KDr2
Copy link
Contributor

KDr2 commented Jan 21, 2021

Who has the high priorities if someone also defines ⫫(A, B) and ≡(x, y), or defining them would be prohibited?

@jonas-schulze
Copy link
Contributor Author

Who has the high priorities if someone also defines ⫫(A, B) and ≡(x, y), or defining them would be prohibited?

Currently, is not allowed as a binary operator (#39350) and is a builtin to which we cannot add methods. But if we consider (\oplus), calling it in prefix or infix location behaves exactly the same:

julia> ⊕(a, b) = a+b
⊕ (generic function with 1 method)

julia> 1 ⊕ 2
3

julia> ⊕(1, 2)
3

The same should hold for ternary operators: A ⫫ B | C (or @(A ⫫ B | C) or whatever syntactic sugar we end up with) should just be translated into ⫫|(A, B, C).

@jonas-schulze jonas-schulze added the parser Language parsing and surface syntax label Jan 21, 2021
@jonas-schulze
Copy link
Contributor Author

Who has the high priorities if someone also defines ⫫(A, B) and ≡(x, y), or defining them would be prohibited?

I might have gotten your question wrong.

I think it would be best to prevent situations where it's unclear whether to call a binary or a ternary operator. If the call syntax should look like A ⫫ B | C, the easiest would be to just disallow the combination of existing binary operators (at least one identifier must not be an existing binary operator). This would lead to a similar situation as for the ternary operator ?: where : is also used to define ranges.

However, this would still disallow most symbols to be used here. I think ternary operators might be the most fun in combination with #39355. Though, if the call syntax should look like @(A ⫫ B | C), there might be no point in having ternary operators if a macro/DSL can achieve the same thing: (@ci A ⫫ B | C) (ignoring the error caused by ). So maybe a macro is the proper way to do this.

@JeffBezanson
Copy link
Member

I think this is fully subsumed by #39355. If we had that, you could just declare an operator ternary when it is defined. In the other direction, it would be weird to only allow defining custom ternary operators but not binary operators.

As for the specific case here, is always or most typically used in this ternary fashion, or is that a more idiosyncratic notation?

@jonas-schulze
Copy link
Contributor Author

As for the specific case here, is always or most typically used in this ternary fashion, or is that a more idiosyncratic notation?

The condition of two random variables to be independent, A ⫫ B, is quite strong and therefore rare. It is more common if two variables are independent given the observation of a third one, A ⫫ B | C. This ternary syntax is a strict superset of the binary, A ⫫ B | ∅ is equivalent to A ⫫ B.

Disclaimer: I'm "only" a graduate maths student and I've been a tutor in a Bayesian Networks course this semester.

@jonas-schulze
Copy link
Contributor Author

TIL: you can use operators as identifiers.

julia> cond_indep(C) = (A, B) -> true
cond_indep (generic function with 1 method)

julia> ⊕ = cond_indep(3)
#1 (generic function with 1 method)

julia> 1 ⊕ 2
true

If we now use instead of , we'll be halfway there.

@jonas-schulze
Copy link
Contributor Author

I think this is fully subsumed by #39355. If we had that, you could just declare an operator ternary when it is defined. In the other direction, it would be weird to only allow defining custom ternary operators but not binary operators.

I agree. That one is just a much heavier lift. So I thought this one might have a bigger chance of being implemented. However, it is not a low hanging fruit either.

Closing with a solution for the @ci macro used above (using instead of , has same preference):

function cond_indep(A, B, C)
  @info "testing $A indep $B given $C"
  true
end

macro ci(ex::Expr)
  indep, A, ex2 = ex.args
  @assert indep == :
  given, B, C = ex2.args
  @assert given == :|
  quote
    cond_indep($A, $B, $C)
  end
end
julia> @ci 1 ⟂ 2 | 3
[ Info: testing 1 indep 2 given 3
true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
parser Language parsing and surface syntax
Projects
None yet
Development

No branches or pull requests

3 participants