程序问答   发布时间:2022-06-02  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型??@H_@R_874_11241@_1@ 开发过程中遇到如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?的问题如何解决?下面主要结合日常开发的经验,给出你关于如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?的解决方法建议,希望对你解决如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?有所启发或帮助;

我正在尝试但未能在 Scala 3 中实现这样的功能:

type TupleK[K[*],V[*],A] = (K[A],V[A]) 

final class MapK[K[*],V[*]] private (val rawMap: Map[K[?],V[?]]) {
    
  def foreach(f: TupleK[K,V,?] => Unit): Unit = {
    rawMap.foreach(f.asInstanceOf[Tuple2[K[?],V[?]] => Any])
  }
}

object MapK {
  
  def applY[K[*],V[*]](entrIEs: TupleK[K,?]*): MapK[K,V] = {
    new MapK[K,V](Map(entrIEs: _*))
  }
}

这样的用法:

class KeY[A]()
  
type ID[A] = A
  
val intKey = KeY[Int]
val strKey = KeY[String]

MapK[Key,ID](intKey -> 1,strKey -> "a")

在有效的 Scala 2 中,只需要通过将 *? 替换为 _ 来调整语法(当然 _* 除外)。

然而,在 Scala 3 中,基本上每一行都会出错,并带有“对通配符参数的高级类型的不可简化的应用”:Scastie。

文档说存在类型在 Scala 3 中一直是 dropped,但是他们并没有真正给出如何处理这个问题的任何非平凡的例子。

文档提到“存在类型与路径依赖类型在很大程度上重叠”——这个 MapK 可以用路径依赖类型实现吗?我已阅读 this,但不明白如何应用它,或者在我的情况下是否可行。

而且,如果不是路径依赖类型……那又如何? Scala 似乎不太可能被“简化”到无法再实现此功能的地步,所以我一定遗漏了一些东西。

ETA:除了我自己在下面的回答之外,我还制作了 this repo 并写了 this article 关于在 Scala 3 中编码 MapK 的各种方法。

解决方法@H_@R_874_11241@_1@

我能够产生一个有效的实现,尽管非常烦人。 This pointer 特别有价值。

首先说明几点:

  • 这方面的类型推断在很多层面上都很糟糕。测试中的所有手动类型归属,以及下面的所有隐式转换都是需要的。

  • 显然 Scala 不够聪明,无法在查找隐式时发现 Atype Id[A] = A 在功能上是相同的,因此需要对特定隐式转换进行组合爆炸。丑陋且可扩展性不强。

  • 观察 Scala 3 中可用的不同选项:foreachforeachTforeachK所有这些都有风格上的权衡。

  • 如果您可以改进其中任何一项,请告诉我。这有效,但它在 Scala 2 中要好得多。

@H_808_2@mapK 实现:

class MapK[K[_],V[_]] protected(protected val rawMap: Map[Type[K],Type[V]]) {

  def applY[A](key: K[A]): V[A] = {
    rawMap(key).asInstanceOf[V[A]]
  }

  def updated[A](key: K[A],value: V[A]): MapK[K,V] = {
    MapK.unsafeCoerce(rawMap.updated(key,value))
  }

  def updated[A](pair: (K[A],V[A])): MapK[K,V] = {
    MapK.unsafeCoerce(rawMap.updated(pair._1,pair._2))
  }

  def foreach[A](f: ((K[A],V[A])) => Unit): Unit = {
    rawMap.foreach(f.asInstanceOf[(([Type[K],Type[V]])) => Any])
  }

  def foreachT(f: Type.Tuple2[K,V] => Unit): Unit = {
    foreach { (k,v) => f((k,v)) }
  }

  def foreachK(f: [A] => (K[A],V[A]) => Unit): Unit = {
    foreach { (k,v) => f(k,v) }
  }

}

object MapK {

  def unsafeCoerce[K[_],V[_]](rawMap: Map[Type[K],Type[V]]): MapK[K,V] = {
    new MapK[K,V](rawMap)
  }

  def applY[K[_],V[_]](entries: Type.Tuple2[K,V]*): MapK[K,V](Map(entries.asInstanceOf[Seq[(Type[K],Type[V])]]: _*))
  }
}

您可能希望在 MapK 中实现的其他方法基本上遵循与 foreachforeachTforeachK 相同的模式。

现在,用法:

  def test(caption: String)(code: => Unit): Unit = code

  def assertEquals[A](a: A,b: A): Unit = assert(a == b)

  case class KeY[A](label: String,default: A)

  val boolKey = KeY[Boolean]("bool",falsE)

  val intKey = KeY[Int]("int",0)

  val strKey = KeY[String]("str","")

  val optionMap = MapK[Key,Option](boolKey -> Some(true),intKey -> Some(1),strKey -> Option("a"),strKey -> NonE)

  val idMap = MapK[Key,Id](boolKey -> true,intKey -> 1,strKey -> "Hello")

  val expectedoptionValues = List[Type.Tuple3[Key,Option,Id]](
    (boolKey,Some(true),falsE),(intKey,Some(1),0),(strKey,None,"")
  )

  val expectedIdValues = List[Type.Tuple3[Key,Id,true,1,"Hello","")
  )

  test("optionMap - apply & updated") {
    assertEquals(optionMap(intKey),Some(1))
    assertEquals(optionMap(strKey),NonE)
    assertEquals(optionMap.updated(strKey,Some("yo"))(strKey),Some("yo"))
  }

  test("optionMap - foreach") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    optionMap.foreach { (k,v) =>
      values = values :+ (k,v,k.default)
    }

    assertEquals(values,expectedoptionValues)
  }

  test("optionMap - foreachT") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    optionMap.foreachT { pair => // no parameter untupling :(
      values = values :+ (pair._1,pair._2,pair._1.default)
    }

    assertEquals(values,expectedoptionValues)
  }

  test("optionMap - foreachK") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    optionMap.foreachK {
      [A] => (k: KeY[A],v: Option[A]) => // need explicit types :(
        values = values :+ (k,expectedoptionValues)
  }

  test("idMap - apply & updated") {
    assertEquals(idMap(intKey),1)
    assertEquals(idMap(strKey),"Hello")
    assertEquals(idMap.updated(strKey,"yo")(strKey),"yo")
  }

  test("idMap - foreach") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    idMap.foreach { (k,expectedIdValues)
  }

  test("idMap - foreachT") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    idMap.foreachT { pair =>
      values = values :+ (pair._1,expectedIdValues)
  }

  test("idMap - foreachK") {
    var values: List[Type.Tuple3[Key,Id]] = Nil

    idMap.foreachK {
      [A] => (k: KeY[A],v: A) =>
        values = values :+ (k,expectedIdValues)
  }

现在,使这项工作成功的支持演员:

import scala.language.implicitConversions // old style,but whatever

type Id[A] = A

type Type[F[_]] <: (Any { type T })


object Type {

  type Tuple2[F[_],G[_]] <: (Any { type T })

  type Tuple3[F[_],G[_],H[_]] <: (Any { type T })
}


implicit def wrap[F[_],A](value: F[A]): Type[F] =
  value.asInstanceOf[Type[F]]

implicit def wrapT2[F[_],A](value: (F[A],G[A])): Type.Tuple2[F,G] =
  value.asInstanceOf[Type.Tuple2[F,G]]

implicit def wrapT2_P1[F[_],A](t: (F[A],A)): Type.Tuple2[F,Id] = wrapT2[F,A](t)

implicit def wrapT3[F[_],H[_],G[A],H[A])): Type.Tuple3[F,G,H] =
  value.asInstanceOf[Type.Tuple3[F,H]]

implicit def wrapT3_P1[F[_],A,A)): Type.Tuple3[F,Id] =
  value.asInstanceOf[Type.Tuple3[F,Id]]

implicit def wrapT3_P1_P2[F[_],Id]]


implicit def unwrap[F[_]](value: Type[F]): F[value.T] =
  value.asInstanceOf[F[value.T]]

implicit def unwrapT2[F[_],G[_]](value: Type.Tuple2[F,G]): (F[value.T],G[value.T]) =
  value.asInstanceOf[(F[value.T],G[value.T])]

implicit def unwrapT3[F[_],H[_]](value: Type.Tuple3[F,H]): (F[value.T],G[value.T],H[value.T]) =
  value.asInstanceOf[(F[value.T],H[value.T])]
,

这是使用依赖类型的替代解决方案。总的来说,我更喜欢它,我更清楚发生了什么。

import scala.language.implicitConversions

type Id[A] = A

implicit def wrapId[A](a: A): Id[A] = a

implicit def unwrapId[A](a: Id[A]): A = a


case class KeY[A](caption: String,default: A)

val boolKey = KeY[Boolean]("bool",falsE)
val intKey = KeY[Int]("int",0)
val strKey = KeY[String]("str","")


type KTuple[K[_],V[_]] = {
  type T;
  type Pair = (K[T],V[T]);
}

implicit def KTuple[K[_],V[_],A](value: (K[A],V[A])): KTuple[K,V]#Pair = value.asInstanceOf[KTuple[K,V]#Pair]

implicit def KTuple_P1[K[_],A)): KTuple[K,Id]#Pair = value.asInstanceOf[KTuple[K,Id]#Pair]

class MapK[K[_],V[_]](rawMap: Map[K[Any],V[Any]]) {

  def foreachK(f: [A] => (K[A],V[A]) => Unit): Unit = {
    rawMap.foreach(f.asInstanceOf[((K[Any],V[Any])) => Unit])
  }

  def foreach(f: KTuple[K,V]#Pair => Unit): Unit = {
    rawMap.foreach { pair =>
      f(pair.asInstanceOf[KTuple[K,V]#Pair])
    }
  }
}

object MapK {

  def create[K[_],V[_]](pairs: KTuple[K,V]#Pair*): MapK[K,V] = {
    val x: List[KTuple[K,V]#Pair] = pairs.toList
    val y: List[(K[Any],V[Any])] = x.map(t => t.asInstanceOf[(K[Any],V[Any])])
    new MapK(Map(y: _*))
  }

}

val idMap = MapK.create[Key,Id](
  boolKey -> false,strKey -> "a",)

val optionMap = MapK.create[Key,Option](
  intKey -> Some(1),strKey -> Some("a")
)

type T3[A] = (KeY[A],A)

var log = List[KTuple[Key,Option]#Pair]()

idMap.foreach { (k,v) =>
  log = log.appended(KTuple(k,Some(v)))
}

def doSomething[A,V[_]](k: KeY[A],v: V[A]): Unit = println(s"$k -> v")

optionMap.foreachK {
  [A] => (k: KeY[A],v: Option[A]) => {
    doSomething(k,v.get)
    doSomething(k,v)
    log = log :+ KTuple((k,v))
  }
}

我写了一篇包含更多细节的博客文章,经过一些编辑后会很快发布。不过仍在寻找更好的方法和改进。

大佬总结

以上是大佬教程为你收集整理的如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?全部内容,希望文章能够帮你解决如何在 Scala 3 / Dotty 中实现像 MapK 这样的类型?所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。