类型不匹配的值类与值字段和导入的猫任一语法

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 版本中,来自EitherIdOpsBinCompat0value 字段具有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

以上是关于类型不匹配的值类与值字段和导入的猫任一语法的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch query查询语法 --- 2022-04-03

lua学习之类型与值篇

[译] Groovy 语法之Map

3.3.1 基本语法

MySQL增删改操作

Struts2 — OGNL与值栈