我试图使用无形来创建一个可以带有副产品的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)
}
}
这给了我一个错误
错误:(48,15)找不到参数cse的隐含值: shapeless.poly.Case [samples.IndexUpdater.updateCopy.type,不成形。:: [samples.IndexedItem,不成形。:: [samples.IndexUpdater.Indexable,shapeless.HNil]]] updateCopy(现有,项目)
我认为这是合理的,因为Poly2正在处理项目的实例,而不是扩展的副产品类型(即为Item1
生成了隐含而不是Indexable
)
但是,如果我没有将poly2应用于PolyApply重载,而是执行:
def mergeWithExisting(existing: IndexedItem, item: Indexable): IndexedItem = {
item.foldLeft(existing)(updateCopy)
}
然后它确实有效。我不确定foldleft正在做什么,以便类型解决。如果这是可接受的方式,我该如何制作这种通用的,以便我可以使用Poly3?还是Poly4?
有没有办法扩展poly中的类型以使其与apply方法一起使用?也许我以错误的方式解决这个问题,我愿意接受建议
答案 0 :(得分:2)
为了使带有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)
我个人认为这更具可读性 - 您将副产品折叠到更新功能,然后将该功能应用于现有的IndexedItem
。不过,这可能主要是风格问题。
你可以创建Poly2
个Case.Aux[IndexedItem, Indexable, IndexedItem]
案例,允许您直接使用apply
,但这样会更加冗长,更少惯用比其中一个折叠方法(在那一点上你甚至不需要多态函数值 - 你可以使用普通的(IndexedItem, Indexable) => IndexedItem
)。
最后,我不确定将折叠方法扩展到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
,则无形测试会通过})。我会仔细看看,也许会打开一个问题。