MLStyle.jl icon indicating copy to clipboard operation
MLStyle.jl copied to clipboard

& might be a bad choice for de-interpolation

Open Roger-luo opened this issue 2 years ago • 7 comments

it has different pred rule as $ so it is not very intuitive to use it sometimes

julia> ex = :(&A.B(a, b, c))
:(&(A.B(a, b, c)))

julia> ex.head
:&

julia> ex.args
1-element Vector{Any}:
 :(A.B(a, b, c))

julia> ex = :(:($A.B(a, b, c)))
:($(Expr(:quote, :(($(Expr(:$, :A))).B(a, b, c)))))

this as a result caused us eval the entire call instead of the object in the following case

function Tree.print_node(io::IO, node::SpatialModulationType)
    @match node begin
        &SpatialModulationType.Global => print(io, "Global")
        &SpatialModulationType.ScaledLocations(_) =>  print(io, "ScaledLocations")
    end
end

& is still a nice and well-adopted symbol tho, though maybe we just need to find a way to fix the pred by matching more Julia expression patterns

Roger-luo avatar Mar 21 '23 20:03 Roger-luo

Some information:

If we keep the concept of "stage"s in mind (e.g, compile time in CPP is stage 0 and the run time is stage 1), we can explain things cleaner:

$ means "splicing" which inserts into the next stage a value that is created at the current stage.

& means "pinning" which references a value in the current stage.

Julia already uses $ for "splicing", this notation is traditional. However, "&" for pinning is quite new (I only see this in Elixir if we don't consider the PL textbook/paper stuffs). Actually, almost all pattern matching programming languages decided to avoid pinning semantics because:

  1. pin operators can be confusing as $ exists
  2. pin operators are difficult to optimize (MLStyle just leaves the optimization to Julia itself)
  3. guards are considered good alternatives to pin operators

thautwarm avatar Mar 30 '23 14:03 thautwarm

So perhaps in our new rewrite, we should consider removing this operator completely? But it would less convenient (basically meaning needs to write another layer of the match in practice) when a pattern need to re-use the first matched variable's value.

Roger-luo avatar Mar 30 '23 18:03 Roger-luo

Yes, we need to consider this in our new rewrite.

We can learn from Python's design: https://peps.python.org/pep-0636/

A.b == matching by value is pretty okay as the introduction in Python doesn't cause issues in practice. We don't need pin operators in this case.

However, I'm not sure how to support matching a value that comes from the local scope, or we just do not support this? Actually, this is only supported in pattern matching languages that support pin operators. See this:

function func(local_var)
    @match value begin
        &local_var => ...
    end
end

thautwarm avatar Apr 06 '23 05:04 thautwarm

True, I think we still need a distinguish of local/global scope just because this is not available inside a Julia macro, but if somehow we can get macro caller scope, this would not be necessary the rule is the same as other cases if the name is defined then match the value, instead of assigning a new variable.

It seems we have no choice but to have something like this because the support has to be implemented as a macro instead of after-scope gets resolved. It's just we need to think about the operators pred more so perhaps need something else other than &

Roger-luo avatar Apr 06 '23 16:04 Roger-luo

I think we should just use $, the meaning of this is actually clear outside quotes, e.g

function Tree.print_node(io::IO, node::SpatialModulationType)
    @match node begin
        $SpatialModulationType.Global => print(io, "Global")
        $SpatialModulationType.ScaledLocations(_) =>  print(io, "ScaledLocations")
    end
end

just means we insert a value from outside to the pattern expression, so it will reference to whatever SpatialModulationType points to within this scope. Within a quote expression, we just need to do it twice

@match ex begin
    :($($x) + 1) => true
    _ => false
end

this means we reference an external value and then insert it into the pattern, if x = 1 this is equivalent to writing

@match ex begin
    :($(1) + 1) => true
    _ => false
end

Roger-luo avatar Apr 12 '23 21:04 Roger-luo

I think you are correct. I'm checking expressions created from Julia parsers could support this.

thautwarm avatar Apr 16 '23 04:04 thautwarm

Here is a use case for &. Suppose I want to rewrite x + x to 2x for any x.

julia> @match :(x + x) :($e + $f) && if e == f end => :(2($e))
:(2x)

julia> @match :(x + x) :($e + $(&e)) => :(2($e))
:(2x)

I don't think either of these syntaxes is especially pretty, but the & is probably nicer.

jariji avatar Feb 22 '24 00:02 jariji