有沒有辦法測驗一個型別是否是一個型別類的成員?例如:
trait Foo[A]
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node] extends Marshaller[Node] {
override def isFoo(n: Node): Boolean = ???
}
顯然有在成員存在時執行代碼的解決方案:
def isFoo(a: A)(implicit ev: Foo[A]) = // do something Foo-like with `a`
在我的用例中,我覆寫了該isFoo方法,因此我也無法更改其簽名。
現實世界的問題
桑格利亞是Scala中創建GraphQL服務庫。它有一個編組子系統,以有效型別類的形式編織到其中,InputUnmarshaller[Node]. 在代碼中可以看到由背景關系限定的型別引數:In: InputUnmarshaller。
概念是一個消耗的輸入值,并產生所述輸出資料集作為一個價值的生產,其中的每個元素需要被編組。該Node型別可以,例如,被限制io.circe.Json值時,如果一個被使用瑟茜的編組。
還有一個 Scala marshaller,它非常愚蠢,因為它只將Map型別作為 map-like 處理。目標是擴展它以支持案例類,例如,通過 Shapeless 和類似地圖的型別類。
uj5u.com熱心網友回復:
嘗試引入型別類 IsFoo
trait Foo[A]
trait IsFoo[A] {
def value(): Boolean
}
trait LowPriorityIsFoo {
implicit def noFoo[A]: IsFoo[A] = () => false
}
object IsFoo extends LowPriorityIsFoo {
implicit def existsFoo[A](implicit foo: Foo[A]): IsFoo[A] = () => true
}
def isFoo[A](implicit isFooInst: IsFoo[A]): Boolean = isFooInst.value()
測驗:
implicit val intFoo: Foo[Int] = null
isFoo[Int] // true
isFoo[String] // false
實際上,我想我isFoo的只是@LuisMiguelMejíaSuárez的
一個更復雜的變體def isFoo[A](implicit ev: Foo[A] = null): Boolean = Option(ev).isDefined
您想定義Marshaller通過繼承/子型別多型的行為。在 JVM 語言中,它是動態調度的(最近在運行時)。現在您想將它與Foo靜態調度的隱式/型別類 ( ) / 臨時多型性(早期,在編譯時)混合使用。您必須使用一些運行時工具,例如運行時反射(Node使用TypeTags 將有關運行時的編譯時資訊持久化)、運行時編譯。
import scala.reflect.runtime.universe.{TypeTag, typeOf, Quasiquote}
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val tb = currentMirror.mkToolBox()
trait Foo[A]
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node: TypeTag] extends Marshaller[Node] {
// override def isFoo(n: Node): Boolean =
// tb.inferImplicitValue(
// tb.typecheck(tq"Foo[${typeOf[Node]}]", mode = tb.TYPEmode
// ).tpe).nonEmpty
override def isFoo(n: Node): Boolean =
util.Try(tb.compile(q"implicitly[Foo[${typeOf[Node]}]]")).isSuccess
}
implicit val intFoo: Foo[Int] = null
new MyMarshaller[Int].isFoo(1) //true
new MyMarshaller[String].isFoo("a") //false
Scala 在運行時決議類/型別 型別類約束
無論如何,在 Scala 中,是否可以從更通用的型別中獲取單例型別?
從動態生成的案例類加載資料集
使用 ToolBox 反射時隱式決議失敗
在 Scala 2 或 3 中,是否可以在運行時除錯隱式決議程序?
If you just want to check that Node is Map[String, _] then just runtime reflection is enough
import scala.reflect.runtime.universe.{TypeTag, typeOf}
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node: TypeTag] extends Marshaller[Node] {
override def isFoo(n: Node): Boolean = typeOf[Node] <:< typeOf[Map[String, _]]
}
new MyMarshaller[Map[String, _]].isFoo(Map()) //true
new MyMarshaller[Int].isFoo(1) //false
See also Typeable
import shapeless.Typeable
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node] extends Marshaller[Node] {
override def isFoo(n: Node): Boolean = Typeable[Map[String, _]].cast(n).isDefined
}
new MyMarshaller[Map[String, _]].isFoo(Map()) //true
new MyMarshaller[Int].isFoo(1) //false
In Scala 3 you could use pattern matching by implicits
import scala.compiletime.summonFrom
trait Foo[A]
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node] extends Marshaller[Node] {
override inline def isFoo(n: Node): Boolean = summonFrom {
case given Foo[Node] => true
case _ => false
}
}
given Foo[Int] with {}
new MyMarshaller[Int].isFoo(1) //true
new MyMarshaller[String].isFoo("a") //false
Actually, I guess the simplest would be to move implicit parameter from method to class
trait Foo[A]
trait IsFoo[A] {
def value(): Boolean
}
trait LowPriorityIsFoo {
implicit def noFoo[A]: IsFoo[A] = () => false
}
object IsFoo extends LowPriorityIsFoo {
implicit def existsFoo[A: Foo]: IsFoo[A] = () => true
}
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node: IsFoo] extends Marshaller[Node] {
// ^^^^^ HERE
override def isFoo(n: Node): Boolean = implicitly[IsFoo[Node]].value()
}
implicit val intFoo: Foo[Int] = null
new MyMarshaller[Int].isFoo(1) //true
new MyMarshaller[String].isFoo("a") //false
@LuisMiguelMejíaSuárez's idea with default implicit also can be used in such case
trait Foo[A]
trait Marshaller[Node] {
def isFoo(n: Node): Boolean
}
class MyMarshaller[Node](implicit ev: Foo[Node] = null) extends Marshaller[Node] {
override def isFoo(n: Node): Boolean = Option(ev).isDefined
}
implicit val intFoo: Foo[Int] = new Foo[Int] {}
new MyMarshaller[Int].isFoo(1) //true
new MyMarshaller[String].isFoo("a") //false
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/312788.html
