在Shapeless中实现路径依赖的地图类型

威尔·萨金特

我想放一张包含从外部到内部的路径相关类型映射的映射:

import shapeless._
import shapeless.ops.hlist._

abstract class Outer {
  type Inner
  def inner: Inner
}

private case class Derp(hl: HList) {
  def get(outer: Outer): outer.Inner = hl.select[outer.Inner]
  def put(outer: Outer)(inner: outer.Inner): Derp = Derp(hl :: inner :: HNil)
  def delete(outer: Outer)(c: outer.Inner): Derp = ???
}

从理论上讲,通过使用HList,我可以避免使用类型选择器,并确保程序只能获取Inner由创建的实例Outer

这可能,甚至是个好主意吗?HList的大多数问题似乎都是关于arity和case类的,而且我觉得我在框外工作。

请注意,我知道https://stackoverflow.com/a/30754210/5266,但这个问题特别是关于Shapeless HList实现的-我不知道如何从HList中删除元素并可能返回Option [outer。内]。

杰里米

首先,您几乎肯定需要参数化Derp

case class Derp[L <: HList](hl: L)

这是因为只要您有一个Derp,编译器就需要知道其静态HList类型是什么,以便进行任何有用的操作。

HList 类型对列表中每种类型的信息进行编码,例如

type Foo = Int :: String :: Boolean :: HNil

只要您说hl: HList,该信息就会丢失。

接下来,您将需要正确指定操作的返回类型:

def get[O <: Outer](o: O)(implicit selector: Selector.Aux[L, o.type, o.Inner]): o.Inner = selector(hl)

def put[O <: Outer](o: O)(i: o.Inner): Derp[FieldType[o.type, o.Inner] :: L] = copy(hl = field[o.type](i))

这是Inner使用Outer的类型“标记”每个,因此您以后可以检索它(它就是这样Selector.Aux做的)。Shapeless中所有有趣的东西都是通过它附带的类型类(或您自己定义的)发生的,它们依赖于类型信息来工作。因此,您可以在操作中保留的类型信息越多,它越容易。

在这种情况下,您将永远不会返回Option,因为如果您尝试访问映射中未包含的值,则该值将无法编译。这通常是您要使用HList的,但我不确定它是否与您的用例匹配。

Shapeless也有HMap,它像普通一样使用键到值映射Map不同之处在于每种键类型都可以映射到不同的值类型。这似乎更符合您的用例,但其组织方式有所不同。要使用HMap,您需要定义一个关系作为类型函数。类型函数是具有从属类型的类型类:

trait MyRelation[Key] {
  type Value
}

object MyRelation {
  type Aux[K, V] = MyRelation[K] { type Value = V }

  implicit val stringToInt: Aux[String, Int] = new MyRelation[String] { type Value = Int }
  implicit val intToBool: Aux[Int, Boolean] = new MyRelation[Int] { type Value = Boolean }

}

现在,您可以定义HMapover MyRelation,因此在使用String键时,您将添加/检索Int值,在使用Int键时,您将添加/检索Boolean值:

val myMap = HMap[MyRelation.Aux]("Ten" -> 10, 50 -> true)
val myMap2 = myMap + ("Fifty" -> 50)
myMap2.get("Ten") // Some(10), statically known as Option[Int]
myMap2.get(44)    // None, statically known as Option[Boolean]

这与您的示例有些不同,因为您有一个具有从属类型,并且您想将外部类型用作键,而将内部类型用作值。这是可能表达这种对关系HMap中,通过使用K#Inner =:= V作为关系。但这通常会使您不工作,这会让您感到惊讶,因为依赖于路径的类型非常棘手,并且实际上取决于外部的具体子类型(这将需要大量样板)或单例类型(在不丢失必要内容的情况下很难通过)类型信息)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章