首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >区分Scala-3枚举和密封特征

区分Scala-3枚举和密封特征
EN

Stack Overflow用户
提问于 2021-11-17 18:17:22
回答 3查看 122关注 0票数 3

是否可以使用Mirrors甚至Macros来区分Scala-3枚举和密封特征?

代码语言:javascript
复制
transparent inline def isScalaEnum[A]: Boolean = ${ isScalaEnumImpl[A] }
private def isScalaEnumImpl[A: Type](using q: Quotes): Expr[Boolean] = ???

例如,如何实现上述宏?

代码语言:javascript
复制
sealed trait T
case class A(x: Int) extends T
case class B(x: String) extends T

enum Color(val rgb: Int):
  case Red   extends Color(1)
  case Green extends Color(2)

isScalaEnum[T]     should be false
isScalaEnum[Color] should be true
EN

回答 3

Stack Overflow用户

发布于 2021-11-17 22:52:27

你可以用<:<NotGiven做到这一点

用于检查您的类型是否为false的子类的

  • <:< reflect.Enum (这仅适用于用于将缺少<:<-evidence转换为false

的scala3 scala.reflect.Enum

下面是它的实现方式:

代码语言:javascript
复制
import scala.util.NotGiven

case class IsEnum[X](value: Boolean)
given isEnum[X](using X <:< reflect.Enum): IsEnum[X] = IsEnum(true)
given isNotEnum[X](using NotGiven[X <:< reflect.Enum]): IsEnum[X] = IsEnum(false)
inline def isScalaEnum[X](using inline ev: IsEnum[X]): Boolean = ev.value

下面是它的使用方法:

代码语言:javascript
复制
enum Foo:
  case Bar

sealed trait NotFoo

@main def demo(): Unit =
  println(isScalaEnum[Foo])         // true
  println(isScalaEnum[NotFoo])      // false

更新

如果你想在编译时获得更精确的类型信息,只需透明地内联所有内容:

代码语言:javascript
复制
import scala.util.NotGiven
case class IsEnum[X](value: Boolean)
transparent inline given isEnum[X](using X <:< reflect.Enum): IsEnum[X] =
  IsEnum(true)
transparent inline given isNotEnum[X](using NotGiven[X <:< reflect.Enum]): IsEnum[X] =
  IsEnum(false)
transparent inline def isScalaEnum[X](using inline ev: IsEnum[X]): ev.value.type =
  ev.value

然后,它的行为类似于您在自己的答案中提到的测试用例,但NullNothing是值得注意的例外:

代码语言:javascript
复制
enum Foo:
  case Bar

enum FooS(x:String):
  case Bar extends FooS("str")

sealed trait NotFoo
inline val a: true  = isScalaEnum[Foo]
inline val b: true  = isScalaEnum[Foo.Bar.type]
inline val c: true  = isScalaEnum[FooS]
inline val d: true  = isScalaEnum[FooS.Bar.type]
inline val e: false = isScalaEnum[NotFoo]
inline val f: false = isScalaEnum[Int]
inline val g: false = isScalaEnum[String]
inline val x: true = isScalaEnum[Null]
inline val y: true = isScalaEnum[Nothing]
票数 1
EN

Stack Overflow用户

发布于 2021-11-18 05:56:47

感谢@Ga的评论,这是我想出来的。不幸的是,它使用的是Macro,但很简单。

在一个文件中:

代码语言:javascript
复制
import scala.quoted.*

transparent inline def isScalaEnum[A]: Boolean =
  ${ isScalaEnumImpl[A] }

private def isScalaEnumImpl[A: Type](using q: Quotes): Expr[Boolean] =
  import q.reflect.*
  TypeRepr.of[A].asType match
    case '[Null]         => Expr(false) // Return false for Null, regardless of whether explicit-nulls are enabled.
    case '[Nothing]      => Expr(false) // I prefer false for Nothing
    case '[reflect.Enum] => Expr(true)
    case _               => Expr(false)

以下是我的测试:

代码语言:javascript
复制
enum Foo:
  case Bar

enum FooS(x:String):
  case Bar extends FooS("str")

sealed trait NotFoo

@main
def demo(): Unit =
  inline val a: true  = isScalaEnum[Foo]
  inline val b: true  = isScalaEnum[Foo.Bar.type]
  inline val c: true  = isScalaEnum[FooS]
  inline val d: true  = isScalaEnum[FooS.Bar.type]
  inline val e: false = isScalaEnum[NotFoo]
  inline val f: false = isScalaEnum[Int]
  inline val g: false = isScalaEnum[Null]
  inline val h: false = isScalaEnum[Nothing]

如果您认为有更简单/更好的解决方案,或者我在这里提供的内容有问题,请告诉我。

编辑:

多亏了@Andrey Tyukin注释/答案,我更改了宏以显式处理NullNothing

票数 0
EN

Stack Overflow用户

发布于 2021-11-18 18:33:08

感谢@Andrey Tyukin和@Ga,这是一个更简单的版本。

代码语言:javascript
复制
transparent inline def isScalaEnum[X]: Boolean = inline compiletime.erasedValue[X] match
  case _: Null         => false // Return false for Null, regardless of whether explicit-nulls are enabled.
  case _: Nothing      => false // I prefer false for Nothing
  case _: reflect.Enum => true
  case _               => false

以下是一些测试:

代码语言:javascript
复制
enum Foo:
  case Bar

enum FooS(x:String):
  case Bar extends FooS("str")

sealed trait NotFoo
inline val a: true  = isScalaEnum[Foo]
inline val b: true  = isScalaEnum[Foo.Bar.type]
inline val c: true  = isScalaEnum[FooS]
inline val d: true  = isScalaEnum[FooS.Bar.type]
inline val e: false = isScalaEnum[NotFoo]
inline val f: false = isScalaEnum[Int]
inline val g: false = isScalaEnum[String]
inline val x: false = isScalaEnum[Null]
inline val y: false = isScalaEnum[Nothing]
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70009589

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档