与 Poly 的联积
Coproduct with Poly
我正在尝试使用 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)
}
}
这给我一个
的错误
Error:(48, 15) could not find implicit value for parameter cse:
shapeless.poly.Case[samples.IndexUpdater.updateCopy.type,shapeless.::[samples.IndexedItem,shapeless.::[samples.IndexUpdater.Indexable,shapeless.HNil]]]
updateCopy(existing, item)
我认为这是有道理的,因为 Poly2 正在处理项目的实例,而不是扩展的余积类型(即隐式是为 Item1
而不是 Indexable
生成的)
但是,如果我不应用带有 PolyApply 重载的 poly2,而是执行:
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
执行此操作,因此您可以将带有初始 IndexedItem
的 Indexable
左折叠到得到一个 IndexedItem
。如果我理解正确,这正是您想要的——updateCopy
中唯一的适当大小写将应用于初始 IndexedItem
和余积的值,您将获得更新后的 IndexedItem
。
将此操作视为 "left fold" 有点不直观,您也可以将其写成普通折叠,它只是将余积折叠为一个值。
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
并仅使用 [=39= 则无形测试通过]).我会仔细看看,也许会提出一个问题。
我正在尝试使用 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)
}
}
这给我一个
的错误Error:(48, 15) could not find implicit value for parameter cse: shapeless.poly.Case[samples.IndexUpdater.updateCopy.type,shapeless.::[samples.IndexedItem,shapeless.::[samples.IndexUpdater.Indexable,shapeless.HNil]]] updateCopy(existing, item)
我认为这是有道理的,因为 Poly2 正在处理项目的实例,而不是扩展的余积类型(即隐式是为 Item1
而不是 Indexable
生成的)
但是,如果我不应用带有 PolyApply 重载的 poly2,而是执行:
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
执行此操作,因此您可以将带有初始 IndexedItem
的 Indexable
左折叠到得到一个 IndexedItem
。如果我理解正确,这正是您想要的——updateCopy
中唯一的适当大小写将应用于初始 IndexedItem
和余积的值,您将获得更新后的 IndexedItem
。
将此操作视为 "left fold" 有点不直观,您也可以将其写成普通折叠,它只是将余积折叠为一个值。
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
并仅使用 [=39= 则无形测试通过]).我会仔细看看,也许会提出一个问题。