Skip to content

Implicit cases in enum? #5538

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

Closed
ctongfei opened this issue Nov 29, 2018 · 2 comments
Closed

Implicit cases in enum? #5538

ctongfei opened this issue Nov 29, 2018 · 2 comments

Comments

@ctongfei
Copy link

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?

@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented Nov 30, 2018

@ctongfei I'm really not convinced given how verbose and cumbersome the suggested IndexOf with enum is. Also, it seems that you are trying to use enum for something completely new and unrelated to their original use case, which is to defined algebraic data types.

What you really want here is dependent types, which would allow you to write is something like the following:

def indexOf(e: Any, l: Hlist): Nat =
  l match {
    case x :: xs => if (x == e) Zero else Succ(indexOf(e, xs))
    case HNil    => throw new NoSuchElementException("...")
  }

And have if run by typer in one way or another to infer the precise Nat that corresponds to the index of element e in l. With match types, you could translate the above function into a type:

type IndexOf[E, L <: Hlist] <: Nat = 
  L match {
    case X :: XS => if (X == E) Zero else Succ[IndexOf[E, XS]]
    case HNil    => error("no-such-element-exception")
  }

(not so sure about the X == E part, but you get the idea)

With what @gsps and I have been working on in #4806 and the follow up PR you would be able to annotate the value level indexOf method as a dependent def and get the same result.

@ctongfei
Copy link
Author

@OlivierBlanvillain Thanks for the explanation. Match types indeed seem to be the more elegant solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants