2

I want to get the instance of a singleton type in Scala, is this possible?

Example: (I know it can be done easier in this case)

sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal

trait Person {
  def name: String
  // Is there a "FavoriteAnimal.instance" syntax?
  def mostImportantThings = (FavoriteAnimal.instance, name)
  protected type FavoriteAnimal <: Animal with scala.Singleton
}

case class DogPerson(override val name: String) extends Person {
  override type FavoriteAnimal = Dog.type
}

case class CatPerson(override val name: String) extends Person {
  override type FavoriteAnimal = Cat.type
}
dtech
  • 13,741
  • 11
  • 48
  • 73
  • 2
    What is `scala.Singleton`? – Thilo Feb 20 '19 at 13:06
  • @Thilo It's not in Scaladoc (still) but it is the type extended by singleton types of `object`s and literals (see https://docs.scala-lang.org/sips/42.type.html). – Alexey Romanov Feb 20 '19 at 17:13
  • @AlexeyRomanov That's not just not in Scaladoc, it is not implemented in mainline Scala 2.12, either, is it? – Thilo Feb 20 '19 at 23:30
  • 2
    @Thilo it is implemented, try `def onlySingleton(arg: Singleton): String = arg.toString`: https://scastie.scala-lang.org/kU481gG3Rtih0rt6dEF2ag – dtech Feb 21 '19 at 07:32
  • @AlexeyRomanov Interesting. Is this a compile-time-only type? Because I cannot "Go to Definition" in IntelliJ or find the type via classpath search. Are there other things like that work like that, too? – Thilo Feb 21 '19 at 09:43
  • 1
    There are certainly other types which don't have a `.class` file. Don't remember which at the moment. But I don't know any which are as undocumented. – Alexey Romanov Feb 21 '19 at 20:31
  • @dtech My pull request was merged to Shapeless so you can try my answer with `resolvers ++= Seq( Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots") )` `libraryDependencies += "com.chuusai" %% "shapeless" % "2.4.0-SNAPSHOT"` in build.sbt. – Dmytro Mitin Mar 04 '19 at 23:56

2 Answers2

3

Using shapeless.Witness correct syntax is

sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal

trait Person {
  def name: String
  def mostImportantThings(implicit 
    witness: Witness.Aux[FavoriteAnimal]
  ): (FavoriteAnimal, String) = (witness.value, name)
  protected type FavoriteAnimal <: Animal with scala.Singleton
}

case class DogPerson(override val name: String) extends Person {
  override type FavoriteAnimal = Dog.type
}

case class CatPerson(override val name: String) extends Person {
  override type FavoriteAnimal = Cat.type
}

DogPerson("A Dog Person").mostImportantThings // (Dog, A Dog Person)

Unfortunately in the current version of Shapeless (2.3.3) there is a bug and this code doesn't compile. But after fix it does.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
2

Maybe you want something like

sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal

trait Person[A <: Animal] {
  def name: String
  def animal: A
  def mostImportantThings = (animal, name)
}

case class DogPerson(override val name: String) extends Person[Dog.type] {
  override val animal = Dog
}

case class CatPerson(override val name: String) extends Person[Cat.type] {
  override val animal = Cat
}
Thilo
  • 257,207
  • 101
  • 511
  • 656
  • I know this is possible, I was more curious whether my exact use-case is possible. Your approach is not ideal in the real situation I'm trying solve – dtech Feb 21 '19 at 07:28
  • Can you describe that use-case in more detail? It seems that if you at compile time already know the type of the singleton object, then you might just as well write your code to specify the singleton instead of the type (because it is easier to go from singleton to type than the other way around). Without more knowledge of your use case, the end result appears equivalent. – Thilo Feb 21 '19 at 09:47
  • it's partly interest, party that this would allow me to prevent changing a decent amount of code since the type is already available, but the instance is not. – dtech Feb 22 '19 at 08:32