scala - 如何在 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, V, ?]*): 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 中被删除,但是他们并没有真正给出如何处理这个问题的任何重要示例。
文档提到“存在类型在很大程度上与路径相关类型重叠”——这个 MapK 可以用路径相关类型实现吗?我读过这个,但不明白如何应用它,或者在我的情况下是否可能。
而且,如果不是路径依赖类型......那又如何?Scala 似乎不太可能“简化”到无法再实现此功能的地步,所以我一定遗漏了一些东西。
ETA:除了下面我自己的答案之外,我还制作了这个 repo并写了这篇关于在 Scala 3 中编码 MapK 的各种方法的文章。
解决方案
这是使用依赖类型的替代解决方案。总的来说,我更喜欢它,对我来说发生了什么更明显。
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](value: (K[A], 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,
intKey -> 1,
strKey -> "a",
)
val optionMap = MapK.create[Key, Option](
intKey -> Some(1),
strKey -> Some("a")
)
type T3[A] = (Key[A], 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))
}
}
我写了一篇包含更多细节的博客文章,经过一些编辑后将很快发布。尽管如此,仍在寻找更好的方法和改进。
推荐阅读
- api - 什么是方法 API 测试?
- git - git 日志 --since
显示从迁移日期开始的提交,但没有“--since”显示所有提交 - javascript - Vue 有条件地阻止事件传播给子级
- xml - 提取 QDomElement 中的整个标签数据
- c# - 在 C# 中发送电子邮件,无法从传输连接中读取数据:net_io_connectionclosed
- python - 如何修复路由 GET POST 的“Flask 404”
- android - 从 activity_main 布局切换到 content_main 布局时,Android 应用程序崩溃且没有错误
- angular - Angular:子组件中自定义输入的2路数据绑定
- vue.js - 循环出插槽?
- kubernetes - kubernetes 容器导致 crashloopback off 错误