Skip to content

Implicit cases in enum? #5538

Closed
Closed
@ctongfei

Description

@ctongfei

This is inspired by #5495 .

Should we allow implicit on enum cases? As @milessabin has stated, we could use an enum as a shapeless Poly with implicit cases corresponding to the Case's of the Poly.

For example, we have the following typelevel function that computes the index of a type in an HList:

trait IndexOf[A, U] extends DepFn0 {
  type Out <: Nat
  def toInt: Int
}

object IndexOf {

  def apply[A, U](implicit o: IndexOf[A, U]): Aux[A, U, o.Out] = o
  type Aux[A, U, I <: Nat] = IndexOf[A, U] { type Out = I }

  implicit def case0[At <: HList, U]: Aux[U :: At, U, _0] =
    new IndexOf[U :: At, U] {
      type Out = _0
      def apply() = Nat._0
      def toInt = 0
    }

  implicit def caseN[At <: HList, Ah, U, I <: Nat]
  (implicit p: IndexOf.Aux[At, U, I]): Aux[Ah :: At, U, Succ[I]] =
    new IndexOf[Ah :: At, U] {
      type Out = Succ[I]
      def apply() = Succ[I]()
      def toInt = p.toInt + 1
    }
}

This is quite verbose. Using implicit cases, I hope that this could be simplified to something like this:

enum IndexOf[A, U] extends DepFn0 {
  type Out <: Nat
  def toInt: Int

  implicit case Case0[At <: HList, U] extends IndexOf[U :: At, U] {
      type Out = _0
      def apply() = Nat._0
      def toInt = 0
    }

  implicit case CaseN[At <: HList, Ah, U, I <: Nat]
  (implicit p: IndexOf.Aux[At, U, I]) extends IndexOf[Ah :: At, U] {
      type Out = Succ[I]
      def apply() = Succ[I]()
      def toInt = p.toInt + 1
    }

}

where each implicit case corresponds to a clause (as in programming in Prolog).

When desugaring, each case is turned into a class in the enum. Since implicit class in Scala is treated as a class and an implicit def (that converts an element to the implicit class wrapper), we could desugar each implicit case into a class and an implicit def. For example, the above could be desugared as:

enum IndexOf[A, U] extends DepFn0 {
  type Out <: Nat
  def toInt: Int

  class Case0[At <: HList, U] extends IndexOf[U :: At, U] {
      type Out = _0
      def apply() = Nat._0
      def toInt = 0
    }

 class CaseN[At <: HList, Ah, U, I <: Nat]
  (implicit p: IndexOf.Aux[At, U, I]) extends IndexOf[Ah :: At, U] {
      type Out = Succ[I]
      def apply() = Succ[I]()
      def toInt = p.toInt + 1
    }

}

object IndexOf {
  implicit def Case0$[At <: HList, U]: Case0[At, U] = new Case0()
  implicit def CaseN$[At <: HList, Ah, U, I <: Nat](implicit p: IndexOf.Aux[At, U, I]): CaseN[Ah :: At, U, Succ[I]] = new CaseN()
}

Or I wonder what match-types would enable us in this situation?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions