Search code examples
scalascala-3singleton-typedottymatch-types

How to get match type to work correctly in Scala 3


I was very curious to see if I could port my untyped project to be typed with Scala 3. Here was my start:

object Main {
  type HtmlNodeRecord[X]= X match {
    case "tag" => String
    case "attrs" => List[(String, String)]
    case "children" => List[HtmlNode]
  }
  case class HtmlNode(tag: String, attrs: List[(String, String)], children: List[HtmlNode]) {
    def apply(s: "tag" | "attrs" | "children"):  HtmlNodeRecord[s.type] = s match {
      case "tag" => tag
      case "attrs" => attrs
      case "children" => children
    }
  }
}

It does not compile, it throws an error:

> [E007] Type Mismatch Error: Main.scala:10:22
> [error] 10 |      case "tag" => tag
> [error]    |                    ^^^
> [error]    |   Found:    (HtmlNode.this.tag : String)
> [error]    |   Required: Main.HtmlNodeRecord[
> [error]    |     (s : ("tag" : String) | ("attrs" : String) | ("children" : String))
> [error]    |   ]

I think it comes from the fact that it does not perceive the pattern matching as a "type filter" for s, since it believes that, in this case, s has the type "tag" | "attrs" | "children", whereas the pattern matching case should reduce it to "tag".

How can I implement my requested behavior?


Solution

  • Correct is

    type HtmlNodeRecord[X] = X match {
      case "tag" => String
      case "attrs" => List[(String, String)]
      case "children" => List[HtmlNode]
    }
    case class HtmlNode(tag: String, attrs: List[(String, String)], children: List[HtmlNode]) {
      def apply(s: "tag" | "attrs" | "children"): HtmlNodeRecord[s.type] = s match {
        case _: "tag" => tag
        case _: "attrs" => attrs
        case _: "children" => children
      }
    }
    

    https://scastie.scala-lang.org/DmytroMitin/sHIgdt5wR7mKZyJm6vEXJA/1

    See item 4 in

    1. The match expression patterns do not have guards
    2. The match expression scrutinee's type is a subtype of the match type scrutinee's type
    3. The match expression and the match type have the same number of cases
    4. The match expression patterns are all Typed Patterns, and these types are =:= to their corresponding type patterns in the match type

    http://dotty.epfl.ch/docs/reference/new-types/match-types.html