导入cats either语法后scala-newtype值类value字段类型不匹配问题
为什么会出现字段识别冲突
@newtype 宏的核心实现逻辑是将标注的case class编译为「无运行时开销的包装类型」,生成的Foo类型在编译期本身并不会暴露公开的value成员,你能直接调用foo.value本质是宏提供的语法糖,需要编译器在特定上下文触发识别。
而你导入cats.syntax.either._后,旧版本cats暴露的catsSyntaxEitherIdBinCompat0是作用于任意类型A的全局隐式转换,它生成的EitherIdOpsBinCompat0类带有公开的value字段。Scala编译器解析成员的优先级规则里,隐式转换提供的公开成员优先级高于newtype宏的语法糖匹配,因此编译器优先匹配到了隐式转换后的value,该字段的类型是Foo而非你预期的String,因此抛出类型不匹配错误。
你直接调用catsSyntaxEitherIdBinCompat0(foo).value当然也得不到正确结果,因为这个value就是你传入的foo本身,类型自然是Foo。
如何解决冲突
有几个可行方案,你可以根据自己的项目情况选择:
- 优先升级
cats-core到2.0及以上版本:后续版本的cats已经给EitherIdOpsBinCompat0的value字段添加了private[syntax]修饰符,外部无法访问该字段,编译器自然会回退到匹配newtype提供的value语法糖,冲突自动解决。 - 若无法升级依赖,修改cats语法的导入方式:不要全量导入
cats.syntax.either._,按需导入你实际用到的语法即可,避免引入作用于任意类型的catsSyntaxEitherIdBinCompat0隐式转换。 - 给newtype的取值逻辑加别名:在
Foo的伴生对象中新增专门的取值方法,避开value的命名冲突,示例如下:
object SomeClass { @newtype case class Foo(value: String) object Foo { def unwrap(f: Foo): String = f.value } } // 调用时直接用别名 val strValue: String = Foo.unwrap(foo)
- 强制调用newtype的取值扩展:通过newtype提供的Coerce扩展显式取值,写法如下(可读性较差,仅做临时应急使用):
import io.estatico.newtype.ops._ val strValue: String = foo.coerce[String]
内容的提问来源于stack exchange,提问作者Boris Azanov
相关产品推荐
相关产品推荐

