用例 class 复制方法并抽象命名参数
Use case class copy method and abstract over named parameters
我正在使用 scalaz state monad,但遇到以下问题:
我的状态是一个案例 class 包含不同类型的向量:
case class Repository(instances: Vector[Instance], vpcs: Vector[Vpc], subnets: Vector[Subnet]….)
我想通过状态操作对存储库进行更改。例如,我想根据更新函数更新向量中的一些元素,如下所示:
val applicative = Applicative[({type f[a] = State[Repository, a]})#f]
def createTags[T <: {def id : String}](memberSelector: Repository => Vector[T])(updateTags: T => T) =
for {
matchingResources <- State.gets((repo: Repository)=> memberSelector(repo).filter(t => resourcesIds.contains(t.id)))
_ <- applicative.traverse(matchingResources)(matchingResource => State.modify((repo: Repository) => repo.copy(*** = memberSelector(repo).replaceFirst(matchingResource, updateTags(matchingResource)))))
} yield ()
replaceFirst
简单地更新向量中的一个元素,隐式 class:
def replaceFirst(oldElem: A, newElem: A): Vector[A] = {
val i = v.indexOf(oldElem)
if (i == -1) v else v.updated(i, newElem)
}
问题是调用 Repository 的 copy 方法时无法抽象出要使用的命名参数 class(参见上面代码中的 ***)。
我想也许 Shapeless 可以帮助我做到这一点:我可以使用 case class 到 HList 同构来将 Repository case class 视为一个 HList,然后使用一个 HList 操作来更新相关的 Vector。虽然我没能让它工作。可以通过 Shapeless 实现吗?还有其他想法吗?
您应该可以为此使用镜头库,而不是直接使用 case class 复制功能。镜头是一种在 B
中获取和设置一些 A
的机制。您已经以 memberSelector
.
的形式获取
例如使用 monocale,它为创建镜头提供了很好的宏
(我实际上并没有打字)
object Repository {
import monocle.Lens
import monocle.macros.GenLens
val _instances: Lens[Repository, Vector[Instance]] = GenLens[Repository](_.instances)
val _vpcs: Lens[Repository, Vector[Vpc]] = GenLens[Repository](_.vpcs)
///...
val applicative = Applicative[({type f[a] = State[Repository, a]})#f]
def createTags[T <: {def id : String}](lens: Lens[Repository, Vector[T]])(updateTags: T => T) =
for {
matchingResources <- State.gets((repo: Repository)=> lens.get(repo).filter(t => resourcesIds.contains(t.id)))
_ <- applicative.traverse(matchingResources)(matchingResource => State.modify((repo: Repository) => lens.modify(_.replaceFirst(matchingResource, updateTags(matchingResource)))))
} yield ()
}
下一版本的 monacle 还应该包括使用 Lens 动作获得 scalaz.State 的好方法。这应该允许与下面的 scalaz.Lens 版本非常相似的语法。
您也可以使用 scalaz.Lens
也
object Repository {
import scalaz.Lens
val _instances: Lens[Repository, Vector[Instance]] = Lens.lensu((r, i) => r.copy(instances = i), _.instances)
///...
import scalaz.std.vector._
import scalaz.syntax.traverse._
def createTags[T <: {def id : String}](lens: Lens[Repository, Vector[T]])(updateTags: T => T) =
for {
matchingResources <- lens.map(_.filter(t => resourcesIds.contains(t.id)))
_ <- matchingResources.traverseS_(matchingResource => lens.mods_(_.replaceFirst(matchingResource, updateTags(matchingResource))))
} yield ()
}
我正在使用 scalaz state monad,但遇到以下问题:
我的状态是一个案例 class 包含不同类型的向量:
case class Repository(instances: Vector[Instance], vpcs: Vector[Vpc], subnets: Vector[Subnet]….)
我想通过状态操作对存储库进行更改。例如,我想根据更新函数更新向量中的一些元素,如下所示:
val applicative = Applicative[({type f[a] = State[Repository, a]})#f]
def createTags[T <: {def id : String}](memberSelector: Repository => Vector[T])(updateTags: T => T) =
for {
matchingResources <- State.gets((repo: Repository)=> memberSelector(repo).filter(t => resourcesIds.contains(t.id)))
_ <- applicative.traverse(matchingResources)(matchingResource => State.modify((repo: Repository) => repo.copy(*** = memberSelector(repo).replaceFirst(matchingResource, updateTags(matchingResource)))))
} yield ()
replaceFirst
简单地更新向量中的一个元素,隐式 class:
def replaceFirst(oldElem: A, newElem: A): Vector[A] = {
val i = v.indexOf(oldElem)
if (i == -1) v else v.updated(i, newElem)
}
问题是调用 Repository 的 copy 方法时无法抽象出要使用的命名参数 class(参见上面代码中的 ***)。
我想也许 Shapeless 可以帮助我做到这一点:我可以使用 case class 到 HList 同构来将 Repository case class 视为一个 HList,然后使用一个 HList 操作来更新相关的 Vector。虽然我没能让它工作。可以通过 Shapeless 实现吗?还有其他想法吗?
您应该可以为此使用镜头库,而不是直接使用 case class 复制功能。镜头是一种在 B
中获取和设置一些 A
的机制。您已经以 memberSelector
.
例如使用 monocale,它为创建镜头提供了很好的宏 (我实际上并没有打字)
object Repository {
import monocle.Lens
import monocle.macros.GenLens
val _instances: Lens[Repository, Vector[Instance]] = GenLens[Repository](_.instances)
val _vpcs: Lens[Repository, Vector[Vpc]] = GenLens[Repository](_.vpcs)
///...
val applicative = Applicative[({type f[a] = State[Repository, a]})#f]
def createTags[T <: {def id : String}](lens: Lens[Repository, Vector[T]])(updateTags: T => T) =
for {
matchingResources <- State.gets((repo: Repository)=> lens.get(repo).filter(t => resourcesIds.contains(t.id)))
_ <- applicative.traverse(matchingResources)(matchingResource => State.modify((repo: Repository) => lens.modify(_.replaceFirst(matchingResource, updateTags(matchingResource)))))
} yield ()
}
下一版本的 monacle 还应该包括使用 Lens 动作获得 scalaz.State 的好方法。这应该允许与下面的 scalaz.Lens 版本非常相似的语法。
您也可以使用 scalaz.Lens
也
object Repository {
import scalaz.Lens
val _instances: Lens[Repository, Vector[Instance]] = Lens.lensu((r, i) => r.copy(instances = i), _.instances)
///...
import scalaz.std.vector._
import scalaz.syntax.traverse._
def createTags[T <: {def id : String}](lens: Lens[Repository, Vector[T]])(updateTags: T => T) =
for {
matchingResources <- lens.map(_.filter(t => resourcesIds.contains(t.id)))
_ <- matchingResources.traverseS_(matchingResource => lens.mods_(_.replaceFirst(matchingResource, updateTags(matchingResource))))
} yield ()
}