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

Unions seem to be non-associative #16491

Closed
hmf opened this issue Dec 9, 2022 · 2 comments
Closed

Unions seem to be non-associative #16491

hmf opened this issue Dec 9, 2022 · 2 comments
Labels
itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label

Comments

@hmf
Copy link

hmf commented Dec 9, 2022

Compiler version

Versions 3.2.0 and 3.2.1-RC2

Minimized code

// Works
summon[(1 | ("2" | ('3' | (4 | Nothing)))) =:= (4 | ('3' | ("2" | (1 | Nothing)))) ]
summon[1 | "2" | '3' | 4 | Nothing <:< (1 | ("2" | ('3' | (4 | Nothing)))) ]
summon[1 | "2" | '3' | 4 | Nothing <:< (1 | ("2" | ('3' | (4 | Nothing)))) ]
summon[1 | "2" | '3' | 4 | Nothing <:< (4 | ('3' | ("2" | (1 | Nothing)))) ]

// Expected to work
summon[1 | "2" | '3' | 4 | Nothing =:= (1 | ("2" | ('3' | (4 | Nothing)))) ]
summon[(1 | ("2" | ('3' | (4 | Nothing)))) =:= 1 | "2" | '3' | 4 | Nothing ]
summon[1 | "2" | ('3' | 4) | Nothing =:= (4 | ('3' | ("2" | (1 | Nothing)))) ]

// Strange
summon[(1 | "2" ) | ('3' | 4) | Nothing <:< (4 | ('3' | ("2" | (1 | Nothing)))) ]
summon[1 | "2" | ('3' | 4) | Nothing <:< (4 | ('3' | ("2" | (1 | Nothing)))) ]
summon[(1 | "2" | '3' ) | 4 | Nothing <:< (4 | ('3' | ("2" | (1 | Nothing)))) ]

See examples

Output

No given instance of type (1 : Int) | ("2" : String) | ('3' : Char) | (4 : Int) | Nothing =:= ((1 : Int) |
   
(("2" : String) | (('3' : Char) | ((4 : Int) | Nothing)))) was found for parameter x of method summon in object Predef

Expectation

I expect any combination and order of the types in a union of the same individual types to be equivalent. Came across this when working on an HList using inline and used the Fold/Union match types as defined for the tuples.

May be related to:

@hmf hmf added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Dec 9, 2022
@WojciechMazur
Copy link
Contributor

WojciechMazur commented Dec 12, 2022

The main problem seems to be somehow parser oriented - by wrapping type into parentesses or extracting it into alias it would work:

@main def Test = 
  type L = 1 | "2" 
  type R = (1 | "2")
  val self = summon[L =:= L]
  val works = summon[L =:= R]
  val works2 = summon[(1 | "2") =:= (1 | "2")]

  val fails = summon[1 | "2" =:= L]
  val fails2 = summon[1 | "2" =:= 1 | "2"]

The failing case would be translated to following summon:

val fails: <error no implicit values were found that match type (1 : Int) | ("2" : String) =:= L> = 
          summon[1.type | =:=["2".type, L]](/* missing */summon[(1 : Int) | ("2" : String) =:= L])

As you can see the summon[1 | "2" =:= L] is interpreted as summon[1.type | =:=["2".type, L]] which is equivalent of summon[1.type | ("2".type =:= L)]]

That because =:= is not a special construct but a class constructed at compile time

@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
sealed abstract class =:=[From, To] extends (From <:< To)

The code was interpreted that way since 3.0.0 so probably it is not a bug

@WojciechMazur
Copy link
Contributor

There are no plans to change to how the types would be resolved, eg. to allow skip using parenthesis on the left/right hand side of =:=. Using type aliases or wrapping complex union/intersection types in parenthesis is recommended to mitigate this issue.

@WojciechMazur WojciechMazur closed this as not planned Won't fix, can't repro, duplicate, stale Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label
Projects
None yet
Development

No branches or pull requests

2 participants