【问题标题】:Mixing dependent types and 'concrete' types in Scala 3在 Scala 3 中混合依赖类型和“具体”类型
【发布时间】:2022-08-14 00:00:25
【问题描述】:

一般来说,我对 Scala 相当陌生,尤其是 Scala 3,我正在尝试编写一些代码,在将值传递给另一个库之前对它们进行透明编码+解码。

基本上,我需要将一组类型(如Ints)映射到底层库中的对应对象。我编写的代码过于冗长,无法在此处完整复制,但这里有一个最小示例来演示这种事情,使用更高种类的 Encoder 类型,它将编码值封装成依赖于值的类型\ ' 原始类型:

trait Encoder[T] {
    type U
    def encode(v: T): U
}

object Encoder {
    given Encoder[Int] with {
        override type U = String

        override def encode(v: Int): String = v.toString
    }
}

case class Value[T : Encoder](v: T) {
    val encoder: Encoder[T] = summon[Encoder[T]]
}

我还需要能够编写处理特定类型的Value 并且具有\'concrete\' 返回类型的函数。像这样:

def doStuff(v1: Value[Int]): String = {
    v1.encoder.encode(v1.v)
}

然而,即使在这种情况下v1.codec.encode 确实返回了String,我得到一个错误:

-- [E007] Type Mismatch Error: -------------------------------------------------
2 |    v1.encoder.encode(v1.v)
  |    ^^^^^^^^^^^^^^^^^^^^^^^
  |    Found:    v1.encoder.U
  |    Required: String

我可以做些什么来解决这个错误?真的很感谢任何可以帮助新手的建议????

  • 你怎么知道回报是String?顺便说一句,整个设计感觉很尴尬。
  • 因为Encoder[Int].UString
  • 您假设这将是唯一的,编译器不会。
  • 感谢您的回复。我假设将是唯一的一个,对不起?
  • encoder 的类型是 Encoder[Int]。句号。 Encoder 存在哪些子类目前无关紧要(而givens 只是另一个名称的子类),编译器不会去寻找它们。它只知道Encoder[int] 有一个名为U 的字段,其类型是“字面上的任何东西”。我们无法推断出任何关于它的信息。

标签: scala scala-3


【解决方案1】:

在 cmets 中回答问题

有什么明智的方式告诉编译器我只对编码为StringEncoders 的Values 感兴趣?

您可以使用额外的类型参数强制Value 记住其编码器的结果类型。

case class Value[T, R](val v: T)(
  using val encoder: Encoder[T],
        val eqv: encoder.U =:= R,
)

encoder 与您的 encoder 相同,只是移至 using 列表,因此我们可以在隐式解析中使用它。

eqv 证明R(我们的类型参数)等价于编码器的U 类型。

然后doStuff 可以拿一个Value[Int, String]

def doStuff(v1: Value[Int, String]): String = {
    v1.eqv(v1.encoder.encode(v1.v))
}

让我们清楚这里发生了什么。 v1.encoder.encode(v1.v) 返回一个 encoder.U。 Scala 不够聪明,不知道那是什么。然而,我们有一个证明encoder.U 等于String,并且该证明可用于将encoder.U 转换为String。这正是=:=.apply 所做的。

我们必须在case class 中执行此操作,因为在我们点击doStuff 时您已经丢失了类型信息。只有case class(它实例化隐式编码器)知道结果类型是什么,所以我们需要在那里公开它。

如果您的代码库中有其他不关心结果类型的地方,您可以为其填写类型参数R,或使用通配符Value[Int, ?]

【讨论】:

  • 另一种选择是使R 成为类型成员并使用Aux 类型来进行优化。
【解决方案2】:

如果我们在这里只讨论 Scala 3,我还建议尝试 Match Types

import scala.util.Try

type Encoder[T] = T match
  case Int => String
  case String => Either[Throwable, Int]

case class Value[T](v: T):
  def encode: Encoder[T] = v match
    case u: Int => u.toString
    case u: String => Try(u.toInt).toEither


object Main extends App:
  val (v1, v2) = (Value(1), Value(2))
  def doStuff(v: Value[Int]): String =
    v.encode

  println(doStuff(v1) + doStuff(v2)) //12
  println(Value(v1.encode).encode) //Right(1)

【讨论】:

    猜你喜欢
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2020-01-25
    • 1970-01-01
    相关资源
    最近更新 更多