gpt4 book ai didi

scala - 与 Poly 的联产品

转载 作者:行者123 更新时间:2023-12-02 02:40:15 27 4
gpt4 key购买 nike

我正在尝试使用 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?还是聚四?

有什么方法可以扩大 Poly 中的类型,使其与 apply 方法一起使用吗?也许我的处理方式是错误的,我愿意接受建议

最佳答案

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

对于累加器类型 updateCopy 和余积 IndexedItem ,您的 Indexable 正是执行此操作,因此您可以将带有初始值 IndexableIndexedItem 左折叠以获得 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 建议 case 的输出类型可以与累加器类型不同,因为 tlLeftFolder 具有 OutH 类型参数。这对我来说似乎有点奇怪,因为据我所知, OutH 必然始终是 In (如果您删除 OutH 并仅使用 In ,则 Shapeless 测试会通过)。我会仔细看看,也许会提出一个问题。

关于scala - 与 Poly 的联产品,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39927666/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com