理解使用路径依赖类型来模拟存在量化类型



我一直在看这篇博文,试图理解如何在Scala 3中使用路径依赖类型来模拟存在量化类型:https://dev.to/raquo/existential-crisis-implementing-mapk-in-scala-3-2fo1

然后我做了下面的例子。

首先定义一元群:

trait Monoid[A]:
val id: A
def combine(l: A, r: A): A
object StringMonoid extends Monoid[String]:
val id = ""
def combine(l: String, r: String) = l + r
object AdditiveIntMonoid extends Monoid[Int]:
val id = 0
def combine(l: Int, r: Int) = l + r
object MultiplicativeIntMonoid extends Monoid[Int]:
val id = 1
def combine(l: Int, r: Int) = r * r

现在假设我要编写一段代码,它可以接受一组monoids,这些monoids可能不都具有相同的底层类型。例如

def ids(ms: Monoid*) =        // This won't compile because Monoid with no argument 
for { m <- ms } yield m.id           //  is not a type

def asList(pairs: ((E, Monoid[E]) for any E)*) =   // This is also not valid scala
pairs.toList

我可以通过一些工作,按照博客中的模式实现我想要的行为。

首先定义一个具有路径依赖的内部类型来模拟forSome:

type any[F[_]] = {
type Member;
type Ops = F[Member]
}

和一些命名不佳的隐式转换来帮助我创建相关类型的实例:

given any_algebra[F[_], A]: Conversion[F[A], any[F]#Ops] = _.asInstanceOf[any[F]#Ops]
given any_algebra_with_member[F[_], A]: Conversion[(A, F[A]), (any[F]#Member, any[F]#Ops)] =
_.asInstanceOf[(any[F]#Member, any[F]#Ops)]

现在我可以写

def ids(ms: List[any[Monoid]#Ops]) =
for { m <- ms } yield m.id
def all[F[_]](fs: any[F]#Ops*) = fs.toList
val ms = all(StringMonoid, MultiplicativeIntMonoid, AdditiveIntMonoid, StringMonoid)
val units = ids(all(AdditiveIntMonoid, StringMonoid, MultiplicativeIntMonoid))
def many[F[_]](pairs: (any[F]#Member, any[F]#Ops)*) = pairs.toList
val mms = many(
7 -> AdditiveIntMonoid,
3 -> MultiplicativeIntMonoid,
"foo" -> StringMonoid,
"bar" -> StringMonoid
)

,它都工作。我不能写

val ms = all(StringMonoid, "hello", AdditiveIntMonoid, StringMonoid)

,因为"hello"不是单群,或者

val mms = many(
"foo" -> StringMonoid,
3 -> StringMonoid
)

因为3不是字符串。

我的问题是为什么这最后一部分是我想要的。为什么不能many(3 -> StringMonoid)?在def many[F[_]](pairs: (any[F]#Member, any[F]#Ops)*)中,是什么限制了元组类型中any[F]的两个外观引用相同的类型?我应该从哪里开始阅读(在Scala语言规范或其他任何地方)才能从基本原理理解这一点?

这是因为你有这个隐式转换

Conversion[(A, F[A]), (any[F]#Member, any[F]#Ops)]

many接受(any[F]#Member, any[F]#Ops)的元组。因此,当您提供元组"foo" -> StringMonoid时,编译器使用隐式转换将其转换为所请求类型的元组。但是这种转换只适用于符合(A, F[A])形状的元组。即两个成员中的A必须是相同的类型。但是当您提供3 -> StringMonoid时,左边的AInt,右边的AString,因此隐式转换将不起作用。

编译器可能仍然试图通过推断A = Any使其工作,但Monoid[String]不是Monoid[Any]的子类型,因为它是不变的。那也不行

相关内容

  • 没有找到相关文章

最新更新