scalascala-3singleton-typematch-types

Scala 3 "a match type could not be fully reduced" with literal types


I'm learning Scala 3, and I was intrigued by match types and literal types.

I'd like to write a function that takes one of a few literal types, and returns a particular type as a function of which literal type was passed in.

Here's a fairly minimal example of what I'm trying to do:

type TestMatchType[T] = T match
  case "String1" => Int
  case "String2" => String

def testMatchType[T](input: T): TestMatchType[T] =
  input match
    case "String1": "String1" => 1
    case "String2": "String2" => "Two"

val string1Output: Int = testMatchType("String1")

In words:

However, when I try to compile the above code, I get the following error:

1 |val string1Output: Int = testMatchType("String1")
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^
  |               Found:    TestMatchType[String]
  |               Required: Int
  |
  |               Note: a match type could not be fully reduced:
  |
  |                 trying to reduce  TestMatchType[String]
  |                 failed since selector  String
  |                 does not match  case ("String1" : String) => Int
  |                 and cannot be shown to be disjoint from it either.
  |                 Therefore, reduction cannot advance to the remaining case
  |
  |                   case ("String2" : String) => String

I'm trying to grok that error message. I'm struggling to understand the line "selector String does not match case ("String1" : String) => Int". I would love some advice if there's a way to make this work.

Thank you!


Solution

  • You don't need to use "foo": "foo", just use _: "foo".

    However, the error happens because T will not be inferred as a singleton type, so the return type will never be Int or String either, it'll remain TestMatchType[String]. You'll either have to use T <: Singleton or use input.type as an argument to TestMatchType (I prefer the latter).

    def testMatchType[T](input: T): TestMatchType[input.type] =
      input match
        case _: "String1" => 1
        case _: "String2" => "Two"
    

    Scastie