我正在尝试使用无形来创建可以采用副产品的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)
}
}
这给我一个错误
考虑到Poly2正在处理项目的实例,而不是扩展的副产品类型(即隐式是为
Item1
而不是Indexable
生成的),我认为这是有道理的但是,如果我不将poly2与PolyApply重载一起应用,请执行以下操作:
def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem = {
item.foldLeft(existing)(updateCopy)
}
然后就可以了。我不确定foldleft在做什么,以便类型解析。如果这是公认的方式,如何使它通用,以便可以使用Poly3?还是Poly4?
有什么办法可以扩大poly的类型,以使其与apply方法一起工作?也许我走错路了,我愿意接受建议
最佳答案
为了用Poly2
保留一个副产品,该函数需要提供Case.Aux[A, x, A]
类型的情况,其中A
是(固定的)累加器类型,而x
是该副产品中的每个元素。
您的updateCopy
正是针对累加器类型IndexedItem
和副产品Indexable
做到了这一点,因此您可以将Indexable
与初始IndexedItem
一起折叠以获得IndexedItem
。如果我理解正确,那么这正是您想要的— updateCopy
中的唯一适当情况将应用于初始IndexedItem
和副产品的值,您将获得更新的IndexedItem
。
将此操作视为“左折”有点不直观,您也可以将其写为普通折叠,即简单地将副产品折叠为一个值。
object updateCopy extends Poly1 {
type U = IndexedItem => IndexedItem
implicit val caseItem1: Case.Aux[Item1, U] = at[Item1](i => _.copy(item1 = i))
implicit val caseItem2: Case.Aux[Item2, U] = at[Item2](i => _.copy(item2 = i))
implicit val caseItem3: Case.Aux[Item3, U] = at[Item3](i => _.copy(item3 = i))
}
然后:
def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem =
item.fold(updateCopy).apply(existing)
我个人觉得这更具可读性-您将联乘分解为一个update函数,然后将该函数应用于现有的
IndexedItem
。不过,这可能主要是样式问题。您可以使用一个
Poly2
案例创建一个Case.Aux[IndexedItem, Indexable, IndexedItem]
,使您可以直接使用apply
,但是比其中一种折叠方法更冗长,而且没有更多习惯用法(在那时,您甚至不需要多态函数值-您可以只使用普通的(IndexedItem, Indexable) => IndexedItem
)。最后,我不确定将fold方法扩展到
Poly3
等是什么意思,但是如果要提供其他要转换的初始值,则可以使累加器类型为元组(或Tuple3
,等等。)。例如:object updateCopyWithLog extends Poly2 {
type I = (IndexedItem, List[String])
implicit val caseItem1: Case.Aux[I, Item1, I] = at {
case ((a, log), b) => (a.copy(item1 = b), log :+ "1!")
}
implicit val caseItem2: Case.Aux[I, Item2, I] = at {
case ((a, log), b) => (a.copy(item2 = b), log :+ "2!")
}
implicit val caseItem3: Case.Aux[I, Item3, I] = at {
case ((a, log), b) => (a.copy(item3 = b), log :+ "2!")
}
}
然后:
scala> val example: Indexable = Coproduct(Item1(10))
example: Indexable = Inl(Item1(10))
scala> val existing: IndexedItem = IndexedItem(Item1(0), Item2(), Item3())
existing: IndexedItem = IndexedItem(Item1(0),Item2(),Item3())
scala> example.foldLeft((existing, List.empty[String]))(updateCopyWithLog)
res0: (IndexedItem, List[String]) = (IndexedItem(Item1(10),Item2(),Item3()),List(1!))
如果那不是
Poly3
部分的意思,我很乐意扩展答案。作为一个脚注,
LeftFolder
source建议案例可以具有与累加器类型不同的输出类型,因为tlLeftFolder
具有OutH
类型参数。这对我来说似乎有点奇怪,因为据我所知OutH
一定总是In
(如果删除OutH
并仅使用In
,Shapeless测试就会通过)。我会仔细看一下,也许还会提出一个问题。关于scala - 与Poly的副产品,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39927666/