使用编译时宏调用scala函数时,如何在导致编译错误时顺利进行故障转移? [英] When calling a scala function with compile-time macro, how to failover smoothly when it causes compilation errors?

查看:50
本文介绍了使用编译时宏调用scala函数时,如何在导致编译错误时顺利进行故障转移?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我打算在scala程序中使用singleton/literal类型特性,这个特性在scala 2.12的shapeless库中提供(scala 2.13支持原生文字类型,但我们以shapeless为例)

在 shapeless 中,文字类型表示为 Witness 对象的路径相关的内部类型,可以从 Scala 文字/常量隐式转换:

<预><代码>导入 com.tribbloids.spike.BaseSpec进口无形.见证导入 scala.util.Randomval w: Witness.Lt[Int] = 3val w2: Witness.Lt[Int] = Random.nextInt(3)//这不会编译

第二行导致编译抛出异常:

<预><代码>[错误] .../WitnessSuite.scala:14: 表达式 scala.util.Random.nextInt(3) 不计算为常数或稳定的参考值发现一个错误

现在,假设我想写一些类似 Option[Witness.Lt[Int]] 的东西,它可以从 Int 转换,无论它是否是文字.在 Scala 类型类约定中,我应该这样写:

 trait MayHaveWitness {类型点亮}特质 MayHaveWitness_Implicits0 {class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {类型 升 = w.T}对象无扩展 MayHaveWitness {输入 Lit = 无}隐式 def fromNonLit(v: Int): None.type = None}对象 MayHaveWitness 扩展了 MayHaveWitness_Implicits0 {隐式 def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)}val v1:MayHaveWitness = 3println(v1.getClass)val v2:MayHaveWitness = Random.nextInt(3)println(v2.getClass)

MayHaveWitness_Implicits0 属于较低级别,如果 Witness 隐式转换成功,理论上应该被 fromLit 掩盖.不幸的是,当我执行这段代码时,我得到的是:

class com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$类 com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$

Witness 隐式转换永远不会发生.我的问题是:

  1. 为什么隐式证明:T =>Witness.Lt[Int] 不是以下无形宏的成功召唤者?

 隐式 def apply[T](t: T): Witness.Lt[T] = 宏 SingletonTypeMacros.convertImpl

  1. 我如何使用类型类 &其他 Scala 功能来实现这种类型级推导的平滑回退?最好:

    • 不使用宏

    • 如果不可能,不要使用白盒宏

    • 如果不是不可能,不要使用会被 dotty 丢弃的宏

解决方案

Shapeless 定义 隐式实例 Witness.Aux[T]

implicit def apply[T]: Witness.Aux[T] = macro SingletonTypeMacros.materializeImpl[T]

隐式<强>转换从类型TWitness.Lt[T]

implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl

隐式实例 Witness.Aux[T] 是否仅基于类型 T 解析(无论是 T 是单例类型或也不是)像普通类型类的隐式实例.但隐式转换 T =>Witness.Lt[T] 不像普通的隐式转换.根据要转换的值的类型来解析或不解析普通的隐式转换.但是 T =>Witness.Lt[T] 的解析与否不仅取决于 T 的类型,还取决于 t 本身的值(是否t代码> 是恒定/稳定的或不).

如果你打开 scalacOptions ++= Seq("-Ymacro-debug-lite", "-Xlog-implicits") 你会在

中看到

val w: Witness.Lt[Int] = 3//编译//警告:scalac:在source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-9处执行宏扩展shapeless.this.Witness.apply[Int](3),偏移=205//警告:scalac:_root_.shapeless.Witness.mkWitness[Int(3)](3.asInstanceOf[Int(3)])val w2: Witness.Lt[Int] = Random.nextInt(3)//不编译//警告:scalac:在源-/media/data/Projects/macrosdemo213/core/src/main/scala处执行宏扩展shapeless.this.Witness.apply[Int](scala.util.Random.nextInt(3))/App114_2.scala,line-10,offset=249//警告:scalac:宏扩展失败:表达式 scala.util.Random.nextInt(3) 不计算为常量或稳定的参考值//错误:表达式 scala.util.Random.nextInt(3) 不计算为常量或稳定的参考值

only implicit def apply[T](t: T): Witness.Lt[T] 被检查(并在 w 中工作,但在 <代码>w2).

也在

val v1: MayHaveWitness = 3//编译但给出 None//警告:scalac:宏扩展延迟:shapeless.this.Witness.apply[T]//警告:scalac:执行宏扩展 shapeless.this.Witness.apply[T]//警告:scalac:宏扩展失败:类型参数T不是单例类型//信息:shapeless.this.Witness.apply 不是 Int => 的有效隐式值shapeless.Witness.Lt[Int] 因为://hasMatchingSymbol 报错:多态表达式无法实例化为预期类型;//找到:[T]shapeless.Witness.Aux[T]//(扩展为)[T]shapeless.Witness{type T = T}//必需:Int =>shapeless.Witness.Lt[Int]//(扩展为)Int =>shapeless.Witness{type T <: Int}//信息:App.this.MayHaveWitness.fromLit 不是 Int(3) => 的有效隐式值App.MayHaveWitness 因为://Int => 没有可用的隐式视图shapeless.Witness.Lt[Int].

并在

val v2: MayHaveWitness = Random.nextInt(3)//编译但给出 None//警告:scalac:宏扩展延迟:shapeless.this.Witness.apply[T]//警告:scalac:执行宏扩展 shapeless.this.Witness.apply[T]//警告:scalac:宏扩展失败:类型参数T不是单例类型//警告:scalac:执行宏扩展 shapeless.this.Witness.apply[T]//信息:App.this.MayHaveWitness.fromLit 不是 Int => 的有效隐式值App.MayHaveWitness 因为://Int => 没有可用的隐式视图shapeless.Witness.Lt[Int].//信息:shapeless.this.Witness.apply 不是 Int => 的有效隐式值shapeless.Witness.Lt[Int] 因为://hasMatchingSymbol 报错:多态表达式无法实例化为预期类型;//找到:[T]shapeless.Witness.Aux[T]//(扩展为)[T]shapeless.Witness{type T = T}//必需:Int =>shapeless.Witness.Lt[Int]//(扩展为)Int =>shapeless.Witness{type T <: Int}//信息:App.this.MayHaveWitness.fromLit 不是 Int => 的有效隐式值App.MayHaveWitness 因为://Int => 没有可用的隐式视图shapeless.Witness.Lt[Int].

implicit def apply[T]: Witness.Aux[T]implicit def apply[T](t: T): Witness.Lt[T]进行了检查,但没有一个起作用.

<块引用>

为什么 隐式证明:T =>Witness.Lt[Int] 不是以下无形宏的成功召唤者?

编译器处理函数类型的隐式A =>B 与其他类型的隐式不同.它可以将它们视为隐式转换(视图).但是它是否真的将它们视为转换或只是 A => 类型的隐式实例;B(和其他类型一样)依赖于 boolean flag isView.

当你这样做

val w: Witness.Lt[Int] = 3//编译val w2: Witness.Lt[Int] = Random.nextInt(3)//不编译val v1: MayHaveWitness = 3//编译val v2: MayHaveWitness = Random.nextInt(3)//编译

isViewtrue.但是当你这样做

隐含地[Int =>Witness.Lt[Int]]//不编译隐含地[3 =>Witness.Lt[Int]]//不编译隐式[Int =>MayHaveWitness]//不编译隐含地[3 =>MayHaveWitness]//不编译

或这里

implicit def fromLit...(隐式证明:T => Witness.Lt[Int])...______________________________________

isViewfalse.

在简单的情况下存在隐式 A =>;BAB的隐式转换是一样的

A 类B级//隐式 val aToB: A =>B = null//这个隐式 def aToB(a: A): B = null//或者这个隐含地[A =>B]//编译val b: B = new A//编译

但在我们的情况下不是.有隐式转换 3 =>Witness.Lt[3] 但不是这种类型的实例

val w: Witness.Lt[3] = 3.asInstanceOf[3]//编译隐含地[3 =>Witness.Lt[3]]//不编译//信息:shapeless.this.Witness.apply 不是 3 => 的有效隐式值shapeless.Witness.Lt[3] 因为://hasMatchingSymbol 报错:多态表达式无法实例化为预期类型;//找到:[T]shapeless.Witness.Aux[T]//(扩展为)[T]shapeless.Witness{type T = T}//需要:3 =>shapeless.Witness.Lt[3]//(扩展为)3 =>shapeless.Witness{type T <: 3}//错误:3 => 没有可用的隐式视图shapeless.Witness.Lt[3].

所以它检查 implicit def apply[T]: Witness.Aux[T] 但不检查 implicit def apply[T](t: T): Witness.Lt[T].我没有深入调试隐式解析,但我怀疑在解析隐式之前没有推断出某些类型.

没有标准方法可以打开 isView 以完全模拟隐式转换的行为,同时解析 ... def fromLit... 中的 proof(隐式证明:T => Witness.Lt[Int])....如果我们使用 c.inferImplicitView 而不是 c.inferImplicitValue

,我们可以使用宏打开 isView

import scala.language.experimental.macros导入 scala.reflect.macros.whitebox特质隐式视图 [A, B] {def 实例:A =>乙}对象隐式视图{隐式 def mkImplicitView[A, B]: ImplicitView[A, B] = 宏 mkImplicitViewImpl[A, B]def mkImplicitViewImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {导入 c.universe._val tpA = weakTypeOf[A]val tpB = weakTypeOf[B]val x = TermName(c.freshName("x"))val 转换 = c.inferImplicitView(树 = EmptyTree,从 = tpA,到 = tpB,无声 = 假)q"""new ImplicitView[$tpA, $tpB] {def 实例:$tpA =>$tpB = ($x: $tpA) =>$转换($x)}"""}

让我们替换

implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)

implicit def fromLit[T](literal: T)(implicit proof: ImplicitView[T, Witness.Lt[Int]]): MayHaveWitness.Some = new Some(proof.instance(literal))

我们还要修改

implicit def fromNonLit(v: Int): None.type = None

因为它与 fromLit 有歧义.原因类似于那些.最简单的解决方法是将其替换为

implicit def fromNonLit[T](v: T): None.type = None

现在两个

val v1:MayHaveWitness = 3println(v1.getClass)val v2:MayHaveWitness = Random.nextInt(3)println(v2.getClass)

Some(我怀疑这不是你想要的).这是可以理解的.Random.nextInt(3)Int.我们仅根据类型解析 MayHaveWitness.并且有隐式转换 Int =>Witness.Lt[Int].所以它是Some.

所以看起来如果我们希望 v1Somev2None 那么我们不能仅基于类型来做到这一点.因此,使用类型类的方法将不起作用,我们将不得不使用宏.

trait MayHaveWitness {类型点亮}对象可能有见证{class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {类型 升 = w.T}对象无扩展 MayHaveWitness {输入 Lit = 无}隐式 def fromLit[T](literal: T): MayHaveWitness = 宏 fromLitImpl[T]def fromLitImpl[T: c.WeakTypeTag](c: whitebox.Context)(literal: c.Tree): c.Tree = {导入 c.universe._val 转换 = c.inferImplicitView(树 = 文字,从 =weakTypeOf[T],到 = typeOf[Witness.Lt[Int]],silent = false)util.Try(c.typecheck(q"new MayHaveWitness.Some($conversion($literal))")).getOrElse(q"MayHaveWitness.None")}}

这里我们用 c.inferImplicitView... 替换了 (implicit proof: T => Witness.Lt[Int]) 并且我们不仅探索了 (implicit proof: T => Witness.Lt[Int])code>literal 以及 literal 本身.

现在

val v1:MayHaveWitness = 3println(v1.getClass)val v2:MayHaveWitness = Random.nextInt(3)println(v2.getClass)

v1 给出 Somev2 给出 None.

如果你制作 fromLit 黑盒,它仍然可以工作,但会返回 MayHaveWitness 而不是 MayHaveWitness.SomeMayHaveWitness.None.

Assuming that I intend to use the singleton/literal type feature in a scala program, this feature is provided in shapeless library in scala 2.12 (scala 2.13 supports native literal type but let's use shapeless as an example)

In shapeless, literal type is represented as a path-dependent inner type of Witness object, which can be implicitly converted from a scala literal/const:


import com.tribbloids.spike.BaseSpec
import shapeless.Witness

import scala.util.Random

    val w: Witness.Lt[Int] = 3

    val w2: Witness.Lt[Int] = Random.nextInt(3) // this doesn't compile

The second line cause compilation to throw an exception:


[Error] .../WitnessSuite.scala:14: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
one error found

Now, assuming that I want to write something like Option[Witness.Lt[Int]] that can be converted from an Int if it is a literal or not. In scala type class convention I should write something like this:

    trait MayHaveWitness {

      type Lit
    }

    trait MayHaveWitness_Implicits0 {

      class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {

        type Lit = w.T
      }
      object None extends MayHaveWitness {

        type Lit = Nothing
      }

      implicit def fromNonLit(v: Int): None.type = None
    }

    object MayHaveWitness extends MayHaveWitness_Implicits0 {

      implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)
    }

    val v1: MayHaveWitness = 3
    println(v1.getClass)

    val v2: MayHaveWitness = Random.nextInt(3)
    println(v2.getClass)

MayHaveWitness_Implicits0 is of lower level and theoretically should be overshadowed by fromLit if the Witness implicit conversion is successful. Unfortunately when I execute this code all I got was:

class com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$
class com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$

The Witness implicit conversion never happens. My questions are:

  1. why implicit proof: T => Witness.Lt[Int] is not a successful summoner of the following shapeless macro?

  implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl

  1. how do I use type classes & other scala features to implement this smooth fallback of type-level deduction? preferrably:

    • NOT using macro

    • If not possible, NOT using whitebox macro

    • If also not impossible, NOT using macro that will be discarded by dotty

解决方案

Shapeless defines implicit instance of type Witness.Aux[T]

implicit def apply[T]: Witness.Aux[T] = macro SingletonTypeMacros.materializeImpl[T]

and implicit conversion from type T to Witness.Lt[T]

implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl

Implicit instance Witness.Aux[T] is resolved or not based on type T only (whether T is a singleton type or nor) like implicit instances of ordinary type classes. But implicit conversion T => Witness.Lt[T] is not like ordinary implicit conversions. Ordinary implicit conversions are resolved or not based on type of a value to be conversed. But T => Witness.Lt[T] is resolved or not based not only on the type T but also on the value t itself (whether t is constant/stable or not).

If you switch on scalacOptions ++= Seq("-Ymacro-debug-lite", "-Xlog-implicits") you'll see that in

val w: Witness.Lt[Int] = 3 //compiles
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](3) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-9,offset=205
//Warning:scalac: _root_.shapeless.Witness.mkWitness[Int(3)](3.asInstanceOf[Int(3)])  

val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](scala.util.Random.nextInt(3)) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-10,offset=249
//Warning:scalac: macro expansion has failed: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
//Error: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value

only implicit def apply[T](t: T): Witness.Lt[T] was checked (and worked in w but didn't work in w2).

Also in

val v1: MayHaveWitness = 3 // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found   : [T]shapeless.Witness.Aux[T]
//    (which expands to)  [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
//    (which expands to)  Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int(3) => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].

and in

val v2: MayHaveWitness = Random.nextInt(3) // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found   : [T]shapeless.Witness.Aux[T]
//    (which expands to)  [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
//    (which expands to)  Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].

both implicit def apply[T]: Witness.Aux[T] and implicit def apply[T](t: T): Witness.Lt[T] were checked and none of them worked.

why implicit proof: T => Witness.Lt[Int] is not a successful summoner of the following shapeless macro?

Compiler treats implicits of functional types A => B differently than implicits of other types. It can treat them as implicit conversions (views). But whether it actually treats them as conversions or just implicit instances of type A => B (like other types) depends on boolean flag isView.

When you do

val w: Witness.Lt[Int] = 3 //compiles
val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
val v1: MayHaveWitness = 3 //compiles
val v2: MayHaveWitness = Random.nextInt(3) //compiles

isView is true. But when you do

implicitly[Int => Witness.Lt[Int]] //doesn't compile
implicitly[3 => Witness.Lt[Int]] //doesn't compile
implicitly[Int => MayHaveWitness] //doesn't compile
implicitly[3 => MayHaveWitness] //doesn't compile

or here

implicit def fromLit... (implicit proof: T => Witness.Lt[Int]) ...
                        ______________________________________

isView is false.

In simple cases existence of implicit A => B and implicit conversion from A to B are the same

class A
class B
// implicit val aToB: A => B = null // this one
implicit def aToB(a: A): B = null   // or this one
implicitly[A => B] //compiles
val b: B = new A //compiles

but not in our case. There is implicit conversion 3 => Witness.Lt[3] but not an instance of this type

val w: Witness.Lt[3] = 3.asInstanceOf[3] //compiles

implicitly[3 => Witness.Lt[3]] // doesn't compile
//Information: shapeless.this.Witness.apply is not a valid implicit value for 3 => shapeless.Witness.Lt[3] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found   : [T]shapeless.Witness.Aux[T]
//    (which expands to)  [T]shapeless.Witness{type T = T}
// required: 3 => shapeless.Witness.Lt[3]
//    (which expands to)  3 => shapeless.Witness{type T <: 3}
//Error: No implicit view available from 3 => shapeless.Witness.Lt[3].

so it checks implicit def apply[T]: Witness.Aux[T] but not implicit def apply[T](t: T): Witness.Lt[T]. I didn't debug implicit resolution deeply but I suspect that some type is not inferred before implicit is resolved.

There is no standard way to switch on isView in order to completely emulate behavior of implicit conversion while resolving proof in ... def fromLit... (implicit proof: T => Witness.Lt[Int]) .... We can switch on isView with macros if we use c.inferImplicitView rather than c.inferImplicitValue

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait ImplicitView[A, B] {
  def instance: A => B
}
object ImplicitView {
  implicit def mkImplicitView[A, B]: ImplicitView[A, B] = macro mkImplicitViewImpl[A, B]
  def mkImplicitViewImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._

    val tpA = weakTypeOf[A]
    val tpB = weakTypeOf[B]

    val x = TermName(c.freshName("x"))
    val conversion = c.inferImplicitView(tree = EmptyTree, from = tpA, to = tpB, silent = false)

    q"""new ImplicitView[$tpA, $tpB] {
      def instance: $tpA => $tpB = ($x: $tpA) => $conversion($x)
    }"""
  }

Let's replace

implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)

with

implicit def fromLit[T](literal: T)(implicit proof: ImplicitView[T, Witness.Lt[Int]]): MayHaveWitness.Some = new Some(proof.instance(literal))

Also we have to modify

implicit def fromNonLit(v: Int): None.type = None

because it's ambiguous with fromLit. Reasons are similar to those. The easiest fix is to replace it with

implicit def fromNonLit[T](v: T): None.type = None

Now both

val v1: MayHaveWitness = 3
println(v1.getClass)

val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)

give Some (I suspect that's not what you wanted). That's understandable. Random.nextInt(3) is Int. And we were resolving MayHaveWitness based only on types. And there is implicit conversion Int => Witness.Lt[Int]. So it's Some.

So it seems if we want v1 to give Some and v2 to give None then we can't do that based only on types. So approach with type classes will not work and we'll have to use macros.

trait MayHaveWitness {
  type Lit
}

object MayHaveWitness  {
  class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {
    type Lit = w.T
  }
  object None extends MayHaveWitness {
    type Lit = Nothing
  }

  implicit def fromLit[T](literal: T): MayHaveWitness = macro fromLitImpl[T]
  def fromLitImpl[T: c.WeakTypeTag](c: whitebox.Context)(literal: c.Tree): c.Tree = {
    import c.universe._
    val conversion = c.inferImplicitView(tree = literal, from = weakTypeOf[T], to = typeOf[Witness.Lt[Int]], silent = false)
    util.Try(c.typecheck(q"new MayHaveWitness.Some($conversion($literal))"))
      .getOrElse(q"MayHaveWitness.None")
  }
}

Here we replaced (implicit proof: T => Witness.Lt[Int]) with c.inferImplicitView... and we explored not only type of literal but also literal itself.

Now in

val v1: MayHaveWitness = 3
println(v1.getClass)

val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)

v1 gves Some and v2 gives None.

If you make fromLit blackbox it will still work but will return MayHaveWitness instead of MayHaveWitness.Some and MayHaveWitness.None.

这篇关于使用编译时宏调用scala函数时,如何在导致编译错误时顺利进行故障转移?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆