【问题标题】:Mapping over collections and returning the same type of container as the input映射集合并返回与输入相同类型的容器
【发布时间】:2019-10-21 19:29:54
【问题描述】:

出于学习目的,我正在尝试定义一个名为DoubleMap 的包装类,它提供了mapBoth 方法。它本质上需要两个函数fg,其中g 的域是f 的共同域。这个函数的组成(g o f)应该映射到DoubleMap

包裹的容器

这是我当前的代码:

implicit class DoubleMap
[A, B, C, F[X] <: List[X]] // just List for now
(xs: F[A])(implicit cbf: CanBuildFrom[F[A], C, F[C]]) {
  def mapBoth(f: A => B)(g: B => C): F[C] =
    xs.map(f andThen g).to[F]
}

但是,当我想使用这样的方法时:

List(true, false, false).mapBoth(!_)(!_)

我收到一条关于(找到的)类型 CanBuildFrom[List[_], Nothing, List[Nothing]] 和(必需的)类型 CanBuildFrom[List[Boolean], C, List[C]] 之间类型不匹配的神秘错误消息

为什么编译器会推断出第一种类型?

【问题讨论】:

  • Scala 2.12 还是 2.13? - (我假设 CanBuildFrom2.12.
  • @LuisMiguelMejíaSuárez 是的。我读到这在 2.13 中被重命名,并且这些泛型方法现在不再返回它们被调用的类型的集合。我没记错吗?
  • 2.13 中,集合框架被重新设计并大大简化,例如这种任务现在“更容易”
  • @LuisMiguelMejíaSuárez 随时为 Scala 2.13 提供答案!

标签: scala generics higher-kinded-types


【解决方案1】:

您的呼叫扩展为

DoubleMap(xs).mapBoth(!_)(!_)

所以DoubleMap 的所有类型参数和cbf 都需要在处理mapBoth 调用之前进行推断。要修复它,只需将这些参数移至mapBoth

implicit class DoubleMap
[A, F[X] <: List[X]] // just List for now
(xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C)(implicit cbf: CanBuildFrom[F[A], C, F[C]]): F[C] =
    xs.map(f andThen g).to[F]
}

那么BC是从fg确定的,而cbf是从FAC确定的。

【讨论】:

  • 这很有意义,几乎让人心疼。我不知道隐式类是这样扩展的。当像这样使用显式类型参数写出来时,它也可以很好地编译,并且很清楚为什么推理失败!谢谢。
  • 一个小补充,展示如何使用隐式类覆盖任何类型的集合以及最佳实践here
【解决方案2】:

如果您不希望与 List 绑定,请考虑 Cats 的 Functor

implicit class DoubleMap[F[_]: Functor, A](xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C): F[C] =
    xs.map(f andThen g)
}

或 vanilla Scala 2.13 方法(受 Luis 的 2.12 implementation 启发)

implicit class DoubleMap[F[x] <: IterableOnce[x], A](xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C)(implicit bf: BuildFrom[F[A], C, F[C]]): F[C] =
    bf.fromSpecific(xs)(xs.iterator.map(f andThen g))
}

两者都输出

List(true, false, false).mapBoth(!_)(!_)
Vector(true, false, false).mapBoth(!_)(!_)

res1: List[Boolean] = List(true, false, false)
res2: Vector[Boolean] = Vector(true, false, false)

【讨论】:

  • 是的,这似乎是正确的。我真的从来没有为转换任何类型的集合而烦恼过,我通常使用特定的集合或 Functor :p
  • 感谢您的回答。我在我的学士论文中写了 Scala 的类型系统,这就是为什么我不想在标准库之外使用 API。不过 2.13 版本看起来不错!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 2021-11-24
相关资源
最近更新 更多