与Poly的Coproduct

dev*_*rts 5 scala shapeless

我正在尝试使用无形来创建一个可以带有副产品的poly2函数:

case class IndexedItem(
  item1: Item1,
  item2: Item2,
  item3: Item3
)

case class Item1(name: Int)

case class Item2()

case class Item3()

object IndexUpdater {
  type Indexable = Item1 :+: Item2 :+: Item3 :+: CNil

  object updateCopy extends Poly2 {
    implicit def caseItem1 = at[IndexedItem, Item1] { (a, b) => a.copy(item1 = b) }

    implicit def caseItem2 = at[IndexedItem, Item2] { (a, b) => a.copy(item2 = b) }

    implicit def caseItem3 = at[IndexedItem, Item3] { (a, b) => a.copy(item3 = b) }
  }

  def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem = {
    updateCopy(existing, item)
  }
}
Run Code Online (Sandbox Code Playgroud)

这给了我一个错误

错误:(48,15)找不到参数cse的隐含值:shapeless.poly.Case [samples.IndexUpdater.updateCopy.type,shapeless.:: [samples.IndexedItem,shapeless.:: [samples.IndexUpdater.Indexable, shapeless.HNil]]] updateCopy(现有的,项目)

我认为是有道理鉴于POLY2正在上的项目的情况下,而不是扩大副产品类型(即用于生成的implicits Item1而不是Indexable)

但是,如果我不将poly2应用于PolyApply重载,而是执行:

def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem = {
  item.foldLeft(existing)(updateCopy)
}
Run Code Online (Sandbox Code Playgroud)

然后它确实有效.我不确定foldleft正在做什么,以便类型解决.如果这是可接受的方式,我该如何制作这种通用的,以便我可以使用Poly3?还是Poly4?

是否有任何方法可以扩展poly中的类型以使其与apply方法一起使用?也许我会以错误的方式解决这个问题,我愿意接受建议

Tra*_*own 3

为了左折叠与 a 的余积Poly2,该函数需要提供类型的情况Case.Aux[A, x, A],其中A是(固定)累加器类型并且x是余积中的每个元素。

\n\n

您对累加器类型和余积updateCopy正是这样做的,因此您可以将带有首字母的an 左折叠以获得。如果我理解正确,这正是您想要的\xe2\x80\x94中的独特适当情况将应用于初始值和余积值,您将获得更新的值IndexedItemIndexableIndexableIndexedItemIndexedItemupdateCopyIndexedItemIndexedItem.

\n\n

将此操作视为“左折叠”有点不直观,您也可以将其写为普通折叠,将余积折叠为一个值。

\n\n
object updateCopy extends Poly1 {\n  type U = IndexedItem => IndexedItem\n\n  implicit val caseItem1: Case.Aux[Item1, U] = at[Item1](i => _.copy(item1 = i))\n  implicit val caseItem2: Case.Aux[Item2, U] = at[Item2](i => _.copy(item2 = i))\n  implicit val caseItem3: Case.Aux[Item3, U] = at[Item3](i => _.copy(item3 = i))\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

进而:

\n\n
def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem =\n  item.fold(updateCopy).apply(existing)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我个人认为这更具可读性\xe2\x80\x94你将余积分解为更新函数,然后将该函数应用于现有的IndexedItem. 不过,这可能主要是风格问题。

\n\n
\n\n

可以创建一个允许您直接使用的Poly2单一案例,但这比其中一种折叠方法更冗长且不惯用(此时您甚至不需要多态函数值\xe2\ x80\x94你可以用普通的Case.Aux[IndexedItem, Indexable, IndexedItem]apply(IndexedItem, Indexable) => IndexedItem)。

\n\n
\n\n

最后,我不确定将折叠方法扩展到Poly3等到底是什么意思,但如果您想要提供要转换的附加初始值,那么您可以使累加器类型为元组(或Tuple3等.)。例如:

\n\n
object updateCopyWithLog extends Poly2 {\n  type I = (IndexedItem, List[String])\n\n  implicit val caseItem1: Case.Aux[I, Item1, I] = at {\n    case ((a, log), b) => (a.copy(item1 = b), log :+ "1!")\n  }\n\n  implicit val caseItem2: Case.Aux[I, Item2, I] = at {\n    case ((a, log), b) => (a.copy(item2 = b), log :+ "2!")\n  }\n\n  implicit val caseItem3: Case.Aux[I, Item3, I] = at {\n    case ((a, log), b) => (a.copy(item3 = b), log :+ "2!")\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

进而:

\n\n
scala> val example: Indexable = Coproduct(Item1(10))\nexample: Indexable = Inl(Item1(10))\n\nscala> val existing: IndexedItem = IndexedItem(Item1(0), Item2(), Item3())\nexisting: IndexedItem = IndexedItem(Item1(0),Item2(),Item3())\n\nscala> example.foldLeft((existing, List.empty[String]))(updateCopyWithLog)\nres0: (IndexedItem, List[String]) = (IndexedItem(Item1(10),Item2(),Item3()),List(1!))\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果这不是你的意思Poly3我很乐意扩展答案。

\n\n
\n\n

作为脚注,LeftFolder来源表明情况可以具有与累加器类型不同的输出类型,因为tlLeftFolder有一个OutH类型参数。这对我来说似乎有点奇怪,因为据我所知,OutH必然总是如此(如果您删除并仅使用,In则无形状测试会通过OutHIn)。我会仔细看看,也许会提出一个问题。

\n