scala - 编译时两个大小相同的 HList 的总和?

标签 scala shapeless

考虑到以下尝试检索两个 HList 的递归和,如下所示:

(请暂时原谅其名称中的产品。)

package net

import shapeless._
import shapeless.nat._
import shapeless.ops.nat.{Sum, Prod, Mod}

trait SumZippedProduct[L, M] {
  type S
}
object SumZippedProduct {

  type Aux[L, M, O] = SumZippedProduct[L, M] {
    type S = O
  }

  def apply[L <: HList, M <: HList](implicit ev: SumZippedProduct[L, M]) = ev

  // LH - L's head
  // L  - HList
  // MH - M's head
  // M  - HList
  // RS - Recursive Sum (L + H)
  // CS - Current Sum (LH + RH)
  // E  - RS + CS
  implicit def sumZippedInductiveEq5[LH <: Nat, L <: HList, MH <: Nat, M <: HList, RS <: Nat, CS <: Nat, E <: Nat](
    implicit ev: SumZippedProduct.Aux[L, M, RS],
             curr: Sum.Aux[LH, MH, CS],
             total: Sum.Aux[CS, RS, E]
  ): SumZippedProduct[LH :: L, MH :: M] = new SumZippedProduct[LH :: L, MH :: M] {
    type S = E
  }

  implicit val hnils: SumZippedProduct[HNil, HNil] = new SumZippedProduct[HNil, HNil] {
    type S = _0
  }

}

在尝试时,我认为它适用于 HNil 情况,但不适用于 1 元素 HList:

scala> import net.SumZippedProduct
import net.SumZippedProduct

scala> import shapeless._, nat._
import shapeless._
import nat._

// expecting 0 (0 + 0)
scala> SumZippedProduct[HNil, HNil]
res0: net.SumZippedProduct[shapeless.HNil,shapeless.HNil] = net.SumZippedProduct$$anon$2@794b4359

// expecting _4 (1 + 3)
scala> SumZippedProduct[_1 :: HNil, _3 :: HNil]
<console>:19: error: could not find implicit value for parameter ev: net.SumZippedProduct[shapeless.::[shapeless.nat._1,shapeless.HNil],shapeless.::[shapeless.nat._3,shapeless.HNil]]
       SumZippedProduct[_1 :: HNil, _3 :: HNil]
                       ^

为什么传递 _1::HNil_3::HNil 时不编译?

另外,如何获取res0中的_0

scala> res0.S
<console>:20: error: value S is not a member of net.SumZippedProduct[shapeless.HNil,shapeless.HNil]
       res0.S
            ^

注意 - 如果这样的实现已经存在于无形中,我很感激,但我问这个问题是为了学习。

最佳答案

您必须在隐式函数的返回类型中使用Aux。否则 S 的具体类型将会丢失。 对于召唤方法 apply ,出于同样的原因,您还必须使用更精确的返回类型。由于 SumZippedProduct 扩展了 AnyRef,因此您可以只使用 ev.type;没有比这更精确的了。

scala> :paste
// Entering paste mode (ctrl-D to finish)

import shapeless._
import shapeless.nat._
import shapeless.ops.nat.{Sum, Prod, Mod, ToInt}

trait SumZippedProduct[L, M] {
  type S <: Nat
  final def S(implicit toInt: ToInt[S]): Int = toInt()
}
object SumZippedProduct {

  type Aux[L, M, O] = SumZippedProduct[L, M] {
    type S = O
  }

  def apply[L <: HList, M <: HList](implicit ev: SumZippedProduct[L, M]): ev.type = ev

  implicit def sumZippedInductiveEq5[LH <: Nat, L <: HList, MH <: Nat, M <: HList, RS <: Nat, CS <: Nat, E <: Nat](
    implicit ev: SumZippedProduct.Aux[L, M, RS],
             curr: Sum.Aux[LH, MH, CS],
             total: Sum.Aux[CS, RS, E]
  ): SumZippedProduct.Aux[LH :: L, MH :: M, E] = new SumZippedProduct[LH :: L, MH :: M] {
    type S = E
  }

  implicit val hnils: SumZippedProduct.Aux[HNil, HNil, _0] = new SumZippedProduct[HNil, HNil] {
    type S = _0
  }

}

// Exiting paste mode, now interpreting.

import shapeless._
import shapeless.nat._
import shapeless.ops.nat.{Sum, Prod, Mod}
defined trait SumZippedProduct
defined object SumZippedProduct

scala> SumZippedProduct[HNil, HNil]
res1: SumZippedProduct.<refinement>.type = SumZippedProduct$$anon$2@673bac03

scala> val a: res1.S = _0
a: res1.S = shapeless._0@4f450e01

scala> SumZippedProduct[_1 :: HNil, _3 :: HNil]
res2: SumZippedProduct.Aux[shapeless.::[shapeless.nat._1,shapeless.HNil],shapeless.::[shapeless.nat._3,shapeless.HNil],this.Out] = SumZippedProduct$$anon$1@2a53bcfa

scala> val a: res2.S = _4
a: res2.S = Succ()

scala> val a: res2.S = _5
<console>:26: error: type mismatch;
 found   : shapeless.nat._5
    (which expands to)  shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]]
 required: res2.S
    (which expands to)  shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
       val a: res2.S = _5
                       ^

scala> res2.S
res3: Int = 4

我还添加了生成相应整数值的方法。实际上,我不知道现有的方法可以从 Nat 类型中调用 Nat 值(或者也许我只是盲目的......)。但我认为在大多数情况下 Nat 仅在类型级别有用,并且您宁愿在值级别使用实际的 Int。

如果您确实想要值(value)级别Nat,那么自己实现也不难。但如果你看看它们的无形实现,它们实际上只是空盒子,所以你不能用它们做太多事情。只有它们的类型才有用。

关于scala - 编译时两个大小相同的 HList 的总和?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42115144/

相关文章:

scala - 理解 Scala 类型系统中的辅助模式

scala - 将 [String,Object] 从数据库(或键值存储)映射到无形可扩展记录

scala - 如何定义一个 HList 类型,但基于另一个 HList 类型

scala - 如何记录所有未过滤的请求

java - 如何向 Spark 数据集添加 Map 列?

scala - 字符串中唯一字符的数量

scala - 获取具有特定键的无形记录进行编译

scala - 当由 Scala 宏生成时,依赖类型似乎为 “not work”

scala - 来自周围范围的隐式参数解析

json - 如何将 map 转换为类型为AnyRef的json