【问题标题】:Can shapeless Record type be used as a Poly1? - Part 2无形记录类型可以用作 Poly1 吗? - 第2部分
【发布时间】:2021-02-05 00:03:27
【问题描述】:

由于我没有从第 1 部分中得到答案:

Can shapeless Record type be used as a Poly1?

我假设无形中不存在此功能。所以我决定自己的命运,自己写一个:

  import shapeless.record._

  case class GetV[H <: HList](hh: H) extends Poly1 {

    implicit def getter[S](
        implicit
        ev: Selector[H, S]
    ): Case.Aux[S, ev.Out] = at[S] { s =>
      val w = Witness(s)
      val _ev = ev.asInstanceOf[Selector[H, w.T]]

      val v = hh.apply(w)(_ev)
      v.asInstanceOf[ev.Out]
    }
  }

它按预期工作,唯一的问题是 2 次调用 asInstanceOf,我认为这是一种不安全的 hack,可以规避类型检查器对单例类型的陷阱。应该做些什么来改善它?

如果你想知道它的能力,这是我的测试代码:

  import shapeless.syntax.singleton._

  val record = ("a" ->> 1) ::
    ("b" ->> "x") ::
    HNil

  it("getV") {

    object get extends RecordUtils.GetV(record)

    assert(get.apply("a".narrow) == 1)
    assert(get("b".narrow) == "x")
  }

更新 1:如果我将测试用例更改为等效的东西,这只是我观察到的所有问题之一:

  it("getV") {

    // object get extends RecordUtils.GetV(record) <----- should be the same
    val get = RecordUtils.GetV(record)

    assert(get.apply("a".narrow) == 1)
    assert(get("b".narrow) == "x")
  }

它破坏了编译:

[Error] .../RecordUtilsSpec.scala:19: could not find implicit value for parameter cse: shapeless.poly.Case[get.type,String("a") :: shapeless.HNil]
[Error] .../RecordUtilsSpec.scala:20: could not find implicit value for parameter cse: shapeless.poly.Case[get.type,String("b") :: shapeless.HNil]
two errors found

两个测试用例有什么区别? val/object 是否都获得了两个不稳定的路径,因此它们的依赖类型具有本地范围?

【问题讨论】:

    标签: scala shapeless dependent-type singleton-type


    【解决方案1】:

    请参阅我对第 1 部分的回答。

    关于第 2 部分,您可以使用已经拥有的类型类作为隐式参数(而不是扩展方法和 Witness)更轻松地定义 implicit def getter 而不使用 asInstanceOf

    case class GetV[H <: HList](hh: H) extends Poly1 {    
      implicit def getter[S](implicit
        ev: Selector[H, S]
      ): Case.Aux[S, ev.Out] = at[S] { _ =>
        ev(hh)
      }
    }
    

    关于更新,Shapeless 中的常见情况是多态函数应该通过对象而不是vals 来定义。否则你必须导入implicits

    val get = GetV(record)
    import get._
    

    【讨论】:

    • 它完全有效,我只是不确定是否应该在库之外使用 Selector.apply() - 它是一个类型类,其实现对用户来说应该是不可知的。事实上,在许多库中,约定是声明 Selector.apply() protected 或 package protected
    • @tribbloid 如果你有一个类型类trait TC[A1, ..., An] { type B1; ...; type Bm; def doSmth1; ...; doSmthK },那么在任何地方都可以使用方法doSmth1,...,doSmthK。这就是在 FP 中定义行为的方式(类似于 OOP 中的类接口特征)。
    猜你喜欢
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    相关资源
    最近更新 更多