Match types and function arguments

Hey!

I have the following code:

type Size = "single" | "optional" | "many"

type Out[S <: Size, A] =
  S match
    case "single" => A
    case "optional" => Option[A]
    case "many" => List[A]

trait Runner[S <: Size, A]:

  def input: A

  inline def run: Out[S, A] =
    inline erasedValue[S] match 
      case _: "single" =>
        mkSingle(input) 
      case _: "optional" =>
        mkOption(input)
      case _: "many" =>
        mkList(input)

It works awesome and user of the Runner trait always gets the output type they need.

All above mk-functions accept the same type A and everything works fine. But now I change their signature and want for example that mkSingle accepted Boolean and thus my A becomes Boolean if and only if the S is “single”:

type MkInput[S <: Size, A] =
  S match
    case "single" => Boolean
    case "optional" => A
    case "many" => String

trait Runner[S <: Size, A]:
  inline def run(input: MkInput[S, A]): Out[S, A] =
    inline erasedValue[S] match 
      case _: "single" =>
        mkSingle(input)      // Now all mk-functions are different now
      case _: "optional" =>
        mkOption(input)
      case _: "many" =>
        mkList(input)

This code doesn’t however, although I expect it should - the S type is shared between MkInput and Out, hence we always know upfront that mkSingle or mkOption gets the expected type. However I’m getting the following error, indicating that match types are not expanded on argument position:

[error] 132 |        mkOption(input)
[error]     |                 ^^^^^
[error]     |Found:    (input : io.foldables.ratio.queries.Action.MkInput[S, A])
[error]     |Required: Boolean
[error]     |
[error]     |where:    S is a type in method foo with bounds <: io.foldables.ratio.queries.Action.Size

Is there a way to proove the compiler that my input is of proper type?

1 Like

What about

trait Runner[S <: Size, A]:
  inline def run(input: MkInput[S, A]): Out[S, A] =
    inline erasedValue[S] match
      case _: "single"   => summonFrom {
        case _: (MkInput[S, A] =:= Boolean) => mkSingle(input)
      }
      case _: "optional" => summonFrom {
        case _: (MkInput[S, A] =:= A)       => mkOption(input)
      }
      case _: "many"     => summonFrom {
        case _: (MkInput[S, A] =:= String)  => mkList(input)
      }

?

2 Likes

Another implementation:

type Transform[S <: Size, A] =
  S match
    case "single"   => Boolean => A
    case "optional" => A       => Option[A]
    case "many"     => String  => List[A]

trait Runner[S <: Size, A]:
  inline def run: Transform[S, A] =
    inline erasedValue[S] match
      case _: "single"   => mkSingle(_)
      case _: "optional" => mkOption(_)
      case _: "many"     => mkList(_)
1 Like