类型不匹配的值类与值字段和导入的猫任一语法
Posted
技术标签:
【中文标题】类型不匹配的值类与值字段和导入的猫任一语法【英文标题】:Type mismatch on value class with value field and imported cats either syntax 【发布时间】:2021-10-30 23:07:47 【问题描述】:我使用来自 scala-newtype 库的值类,但它们看起来与旧的(版本 1.4.0)cats-core 库不兼容。看例子:
import io.estatico.newtype.macros.newtype
import SomeClass.Foo
import cats.syntax.either._
class SomeClass
def takeFoo(foo: Foo): Unit =
val strValue: String = foo.value // here I have compile error
println(strValue)
object SomeClass
@newtype case class Foo(value: String)
object Application extends App
new SomeClass().takeFoo(Foo("x"))
此代码编译时出错:
type mismatch;
found : SomeClass.Foo
(which expands to) SomeClass.Foo.Type
required: String
scastie example here
构建设置:
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
依赖:
scalaVersion := "2.12.10"
libraryDependencies ++= Seq(
"io.estatico" %% "newtype" % "0.4.4",
"org.typelevel" %% "cats-core" % "1.4.0"
)
我发现旧的cats-core
有类EitherIdOpsBinCompat0
:
final class EitherIdOpsBinCompat0[A](val value: A) extends AnyVal
def leftNec[B]: Either[NonEmptyChain[A], B] = Left(NonEmptyChain.one(value))
def rightNec[B]: Either[NonEmptyChain[B], A] = Right(value)
具有A
类型的字段value
,如果我将strValue
定义替换为:
val strValue: String = catsSyntaxEitherIdBinCompat0(foo).value
我看到同样的错误。
所以,主要问题:为什么编译器看不到值类字段value
,而是试图从cats.syntax.either
隐式转换为EitherIdOpsBinCompat0
? (见catsSyntaxEitherIdBinCompat0
函数)
private[syntax] trait EitherSyntaxBinCompat0
implicit final def catsSyntaxEitherBinCompat0[A, B](eab: Either[A, B]): EitherOpsBinCompat0[A, B] =
new EitherOpsBinCompat0(eab)
implicit final def catsSyntaxEitherIdBinCompat0[A](a: A): EitherIdOpsBinCompat0[A] =
new EitherIdOpsBinCompat0(a)
我该如何避免这种行为?
PS:
在新的cats-core
版本中,来自EitherIdOpsBinCompat0
的value
字段具有private
修饰符,并且调用值类的value
字段没有问题。
final private[syntax] class EitherIdOpsBinCompat0[A](private val value: A) extends AnyVal ...
【问题讨论】:
这可能是关于newtype
的问题,而不是关于猫的问题。
【参考方案1】:
根据您链接到的 scala-newtype 的文档,您的新类型 SomeClass.Foo
在宏扩展后或多或少地转换为以下代码:
object SomeClass
type Foo = Foo.Type
object Foo
type Repr = String
type Base = Any type Foo$newtype
trait Tag extends Any
type Type <: Base with Tag
def apply(x: String): Foo = x.asInstanceOf[Foo]
implicit final class Ops$newtype(val $this$: Type) extends AnyVal
def value: String = $this$.asInstanceOf[String]
你可以看到value
实际上是一个隐式类添加的扩展方法。就像EitherIdOpsBinCompat0
中的意外扩展方法value
一样。因此,编译器要么必须优先选择其中一种扩展方法,要么编译失败。我很确定有一条规则说在这种情况下首选导入的隐式 (import cats.syntax.either._
)。
【讨论】:
你是对的。有趣的事实:在添加import SomeClass.Foo._
之后,尽管有任何语法导入,但一切都会编译。但是,如果我添加另一个具有相同字段value
的值类Bar
,它会再次失败并出现相同的错误。似乎最好澄清一下为什么编译器会从任一语法中选择implicit
。也许是因为它更通用,但我不确定。更新示例:scastie.scala-lang.org/AySmUs8tQM2UP2X5vV0HDA以上是关于类型不匹配的值类与值字段和导入的猫任一语法的主要内容,如果未能解决你的问题,请参考以下文章