Scala 3 联合类型的给定/隐式

问题描述 投票:0回答:1

我有一些类型类,例如:

trait ExampleTypeClass[A]
  // imagine one or more methods here

object ExampleTypeClass:
  given ExampleTypeClass[Int] = new ExampleTypeClass{/* ... */}
  given ExampleTypeClass[Boolean] = new ExampleTypeClass{/* ... */}

此外,如果我有类型

A
B
的类型类实例,我会知道如何实现
A | B
的实例(我知道
A | B
相当于
=:=
B | A
,但就我而言,顺序无关)。

实现类似功能的最明显的方法是:

  given unionExampleTypeClass[A, B](
    using a: ExampleTypeClass[A],
    b: ExampleTypeClass[B]
  ): ExampleTypeClass[A | B] = 
    new ExampleTypeClass[A | B]{
      // imagine some logic that depends on a and b here
      // (which does not depend on the order of a and b)
    }

当这样实现时,我可以显式地调用它 - 以下编译正常:

val intOrBoolTC1: ExampleTypeClass[Int | Boolean] = 
  unionExampleTypeClass[Int, Boolean]

val intOrBoolTC2: ExampleTypeClass[Int | Boolean] = 
  unionExampleTypeClass[Boolean, Int]

但是,隐式解析失败:

val intOrBoolTC3 = summon[ExampleTypeClass[Int | Boolean]]
// results in:
//[error] Ambiguous given instances: both given instance given_ExampleTypeClass_Int in object ExampleTypeClass and given instance given_ExampleTypeClass_Boolean in object ExampleTypeClass match type ExampleTypeClass[A] of parameter x of method summon in object Predef
// [error] val intOrBoolTC3 = summon[ExampleTypeClass[Int | Boolean]]
// [error]                                                           ^    

有没有办法使

A | B
的组合实现在隐式范围内可用,以便调用此类实例可以工作?

我还尝试了

inline
summonInline
的一些变体,但这些并没有解决主要问题(据我所知)似乎是
unionExampleTypeClass[Int, Boolean]
unionExampleTypeClass[Boolean, Int]
都返回所需的类型,导致含糊不清。

我发现了一种相关问题Implicit conversion between a Union Type and Either in Scala 3,但除了证实我的怀疑根本问题可能是什么之外,我没有在那里找到答案。

scala typeclass implicit union-types given
1个回答
0
投票

为了解决歧义,您可以优先考虑类型类的实例

trait LowPriorityExampleTypeClass:
  given ExampleTypeClass[Int] = null

object ExampleTypeClass extends LowPriorityExampleTypeClass:
  given ExampleTypeClass[Boolean] = null

您可以尝试在Scala 2中使用类似于

shapeless.Lub
的类型类https://github.com/milessabin/shapeless/blob/main/core/shared/src/main/scala/shapeless/typeoperators.scala#L186 -L202 Scala 中 3 种类型之间的协方差

trait Lub[-A, -B, Out]:
  def left(a: A): Out
  def right(b: B): Out
object Lub:
  given [T]: Lub[T, T, T] with
    def left(a: T): T = a
    def right(b: T): T = b
given unionExampleTypeClass[A, B, Out](using
  ExampleTypeClass[A],
  ExampleTypeClass[B],
  Lub[A, B, Out],
): ExampleTypeClass[Out] = null

但是不仅

summon[ExampleTypeClass[Int | Boolean]]
会编译,例如
summon[ExampleTypeClass[Any]]
也会

https://scastie.scala-lang.org/DmytroMitin/680pTCXQQkqRxaXa2V6ldQ/5

如果你想禁止

ExampleTypeClass[Any]
等,你可以添加约束
NotGiven[Out =:= Any]
NotGiven[Out =:= AnyVal]
NotGiven[Out =:= AnyRef]

given unionExampleTypeClass[A, B, Out](using
  ExampleTypeClass[A],
  ExampleTypeClass[B],
  Lub[A, B, Out],
  NotGiven[Out =:= Any],
  NotGiven[Out =:= AnyVal],
  NotGiven[Out =:= AnyRef],
): ExampleTypeClass[Out] = null

https://scastie.scala-lang.org/DmytroMitin/680pTCXQQkqRxaXa2V6ldQ/8

但是仍然会定义类型类的实例,而不仅仅是为

Int | Boolean
。例如,如果您引入抽象
type T >: Int | Boolean
summon[ExampleTypeClass[T]]
也会编译。

© www.soinside.com 2019 - 2024. All rights reserved.