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

Pattern matching to equal objects of a certain variant #10

Open
mtfishman opened this issue Aug 15, 2024 · 3 comments
Open

Pattern matching to equal objects of a certain variant #10

mtfishman opened this issue Aug 15, 2024 · 3 comments
Labels
question Further information is requested

Comments

@mtfishman
Copy link
Collaborator

mtfishman commented Aug 15, 2024

Hi @Roger-luo, thanks for the nice package. I'm playing around with it for writing a symbolic tensor network library, it seems like a good fit for that and the combination of algebraic data types and pattern matching is very powerful.

I was hoping to pattern match to cases where the objects are equal and of a certain variant, is that possible? I tried the following:

using Moshi.Data: @data
using Moshi.Match: @match

@data Fruit begin
  struct Apple
    type::String
  end
  struct Orange
    type::String
  end
end

@match (Fruit.Apple("Honeycrisp"), Fruit.Apple("Honeycrisp")) begin
  (x::Fruit.Apple, x::Fruit.Apple) => "Same apples (both $x)"
  (x::Fruit.Apple, y::Fruit.Apple) => "Different apples ($x, $y)"
  _ => "No match"
end

but it returns "No match", i.e. it doesn't hit the (x::Fruit.Apple, x::Fruit.Apple) => ... branch.

Similarly, passing (Fruit.Apple("Honeycrisp"), Fruit.Apple("Fuji")) doesn't hit the (x::Fruit.Apple, y::Fruit.Apple) => ... branch.

I'm using:

(Moshi.jl) pkg> st Moshi
Status `...`
  [2e0e35c7] Moshi v0.3.3
@mtfishman
Copy link
Collaborator Author

mtfishman commented Aug 15, 2024

Here's one way to do it:

@match (Fruit.Apple("Honeycrisp"), Fruit.Apple("Honeycrisp")) begin
  (Fruit.Apple(; type=x), Fruit.Apple(; type=x)) => "Same apples (both type $x)"
  (Fruit.Apple(; type=x), Fruit.Apple(; type=y)) => "Different apples (types $x, $y)"
  _ => "No match"
end

but it seems unfortunate to have to list all of the arguments if you want the entire object to match.

EDIT: One interesting thing I noticed with that approach is that if you do something like the following:

@match (Fruit.Apple("Honeycrisp"), Fruit.Apple("Honeycrisp")) begin
  (x::Fruit.Type, x::Fruit.Type) => "Same fruit (both $x)"
  (x::Fruit.Type, y::Fruit.Type) => "Different fruits ($x, $y)"
  (Fruit.Apple(; type=x), Fruit.Apple(; type=x)) => "Same apples (both type $x)"
  (Fruit.Apple(; type=x), Fruit.Apple(; type=y)) => "Different apples ($x, $y)"
  _ => "No match"
end

it matches to (x::Fruit.Type, x::Fruit.Type) => ... rather than (Fruit.Apple(; type=x), Fruit.Apple(; type=x)) => ..., though it matches to (Fruit.Apple(; type=x), Fruit.Apple(; type=x)) => ... if you do:

@match (Fruit.Apple("Honeycrisp"), Fruit.Apple("Honeycrisp")) begin
  (Fruit.Apple(; type=x), Fruit.Apple(; type=x)) => "Same apples (both type $x)"
  (Fruit.Apple(; type=x), Fruit.Apple(; type=y)) => "Different apples ($x, $y)"
  (x::Fruit.Type, x::Fruit.Type) => "Same fruit (both $x)"
  (x::Fruit.Type, y::Fruit.Type) => "Different fruits ($x, $y)"
  _ => "No match"
end

I guess the precedence is based on the order of the definitions and not the level of specificity of the pattern, i.e. it is more like a series of if-statements rather than how the Julia compiler finds methods.

@mtfishman
Copy link
Collaborator Author

Sorry for the noise, I realized another way to achieve this is with nested @match statements:

x = Fruit.Apple("Honeycrisp")
y = Fruit.Apple("Honeycrisp")

@match (x, y) begin
  (Fruit.Apple(), Fruit.Apple()) => @match (x, y) begin
    (x, x) => "Same apples"
    (x, y) => "Different apples"
  end
  _ => "No match"
end

@Roger-luo
Copy link
Owner

Hi Matt,

Thanks for your interest!

I'm playing around with it for writing a symbolic tensor network library, it seems like a good fit for that and the combination of algebraic data types and pattern matching is very powerful.

Yes, this package is built precisely for this type of application, as we chatted about last time in New York. However, this is only step 1. To achieve what we want, a general-purpose framework that does not imply any algebra on the symbolic expression needs to be built.

I guess the precedence is based on the order of the definitions and not the level of specificity of the pattern, i.e. it is more like a series of if-statements rather than how the Julia compiler finds methods.

Moshi.Match provides only syntax-based pattern matching. The underlying pattern language does not define which pattern is more specific, unlike the type system, where the more specific pattern is defined by subtyping. On the other hand, it does not aim to match all the patterns. Matching all the patterns requires extra runtime computation that breaks the promise of zero-overhead Moshi provides. Doing so requires a rule-based rewrite system built on top of Moshi, which is what I'm planning to work on next.

I was hoping to pattern match to cases where the objects are equal and of a certain variant, is that possible? I tried the following:

Yes, that's your last example if you don't want to match specific fields. You don't need to write nested @match tho.

@Roger-luo Roger-luo added the question Further information is requested label Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants