简化类型注释

发布于 2024-10-14 05:48:16 字数 3573 浏览 0 评论 0原文

我创建了一个 pimp 方法 collat​​e,它可以从任何 Traversable 或任何可以强制转换为可遍历的类型使用,如下例所示:

val ints = List(0,9,4,5,-3,-5,6,5,-2,1,0,6,-3,-2)
val results = ints collate {
  case i: Int if(i < 0) => i.floatValue
} andThen {
  case i: Int if(i>5) => i.toString
} andThen {
  case i: Int if(i==0) => i
} toTuple

/*
results: (List[Float], List[java.lang.String], List[Int], List[Int]) =
(List(-3.0, -5.0, -2.0, -3.0, -2.0),List(9, 6, 6),List(0, 0),List(4, 5, 5, 1))
*/

将其视为如果你愿意的话,联合体'twixtcollectpartition的邪恶产物......

它的定义如下:

import collection.generic.CanBuildFrom

class Collatable[Repr <% Traversable[T], T](xs: Repr) {

  // Results handling stuff, bit like a poor-man's HList, feel free to skip...

  trait Results {
    def remainder: Repr

    type Append[That] <: Results
    def append[That](tup: (That, Repr)): Append[That]

    def andThen[R, That](pf: PartialFunction[T, R])
    (implicit
      matchesBuilder: CanBuildFrom[Repr, R, That],
      remainderBuilder: CanBuildFrom[Repr, T, Repr]
    ) = {
      val more = (new Collatable[Repr,T](remainder)).collateOne[R,That](pf)
      append(more)
    }
  }

  case class Results9[M1,M2,M3,M4,M5,M6,M7,M8,M9](
    m1: M1, m2: M2, m3: M3, m4: M4, m5: M5, m6: M6, m7: M7, m8: M8, m9: M9,
    remainder: Repr)
  extends Results {
    implicit def toTuple = (m1, m2, m3, m4, m5, m6, m7, m8, m9, remainder)
    def append[That](tup: (That, Repr)) = error("too many")
  }

  // ... skip a bit, still in results logic ...

  case class Results2[M1,M2](
    m1: M1, m2: M2, remainder: Repr)
  extends Results {
    implicit def toTuple = (m1, m2, remainder)
    type Append[That] = Results3[M1,M2,That]
    def append[That](tup: (That, Repr)) = Results3(m1, m2, tup._1, tup._2)
  }

  case class Results1[M1](matches: M1, remainder: Repr) extends Results {
    implicit def toTuple = (matches, remainder)

    type Append[That] = Results2[M1, That]
    def append[That](tup: (That, Repr)) = Results2(matches, tup._1, tup._2)
  }

  // and now... Our feature presentation!

  def collateOne[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuildFrom[Repr, R, That],
    remainderBuilder: CanBuildFrom[Repr, T, Repr]
  ) = {
    val matches = matchesBuilder(xs)
    val remainder = remainderBuilder(xs)
    for (x <- xs) if (pf.isDefinedAt(x)) matches += pf(x) else remainder += x
    (matches.result, remainder.result)
  }

  def collate[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuildFrom[Repr, R, That],
    remainderBuilder: CanBuildFrom[Repr, T, Repr]
  ): Results1[That]  = {
    val tup = collateOne[R,That](pf)
    Results1(tup._1, tup._2)
  }
}

object Collatable {
  def apply[Repr, T](xs: Repr)(implicit witness: Repr => Traversable[T]) = {
    new Collatable[Repr, T](xs)
  }
}

implicit def traversableIsCollatable[CC[X] <: Traversable[X], A](xs: CC[A]) =
  Collatable[CC[A], A](xs)
implicit def stringIsCollatable(xs: String) =
  Collatable[String, Char](xs)

从概念上讲,一旦你了解如何CanBuildFrom 可以工作,但我发现它被样板文件淹没了 - 尤其是隐式的。

我知道我可以通过使用 HList 来极大地简化 ResultX 逻辑,这也是我可能会做的事情,所以那段代码并不特别让我担心。

我还知道,如果我能够将 Repr 限制为 Traversable 的子类型,我的生活就会显着变得更轻松。但我拒绝这样做,因为那样它就不能用于对抗字符串。出于同样的原因,我也希望避免强制部分函数返回 T 的子类型 - 尽管这不太重要,因为我总是可以将逻辑分解为不同的整理和映射操作。

更令人担忧的是 CanBuildFrom[Repr, T, Repr],我似乎一直在重复它,并且它掩盖了我的方法签名中的重要内容。我确信这可以在班级级别定义一次,但我还没有找到使其发挥作用的方法。

有什么想法吗?

I've created a pimp method, collate, that's usable from any Traversable or any type that can be coerced to a traversable as per the following example:

val ints = List(0,9,4,5,-3,-5,6,5,-2,1,0,6,-3,-2)
val results = ints collate {
  case i: Int if(i < 0) => i.floatValue
} andThen {
  case i: Int if(i>5) => i.toString
} andThen {
  case i: Int if(i==0) => i
} toTuple

/*
results: (List[Float], List[java.lang.String], List[Int], List[Int]) =
(List(-3.0, -5.0, -2.0, -3.0, -2.0),List(9, 6, 6),List(0, 0),List(4, 5, 5, 1))
*/

Think of it as the unholy spawn of a union 'twixt collect and partition, if you will...

It's defined like this:

import collection.generic.CanBuildFrom

class Collatable[Repr <% Traversable[T], T](xs: Repr) {

  // Results handling stuff, bit like a poor-man's HList, feel free to skip...

  trait Results {
    def remainder: Repr

    type Append[That] <: Results
    def append[That](tup: (That, Repr)): Append[That]

    def andThen[R, That](pf: PartialFunction[T, R])
    (implicit
      matchesBuilder: CanBuildFrom[Repr, R, That],
      remainderBuilder: CanBuildFrom[Repr, T, Repr]
    ) = {
      val more = (new Collatable[Repr,T](remainder)).collateOne[R,That](pf)
      append(more)
    }
  }

  case class Results9[M1,M2,M3,M4,M5,M6,M7,M8,M9](
    m1: M1, m2: M2, m3: M3, m4: M4, m5: M5, m6: M6, m7: M7, m8: M8, m9: M9,
    remainder: Repr)
  extends Results {
    implicit def toTuple = (m1, m2, m3, m4, m5, m6, m7, m8, m9, remainder)
    def append[That](tup: (That, Repr)) = error("too many")
  }

  // ... skip a bit, still in results logic ...

  case class Results2[M1,M2](
    m1: M1, m2: M2, remainder: Repr)
  extends Results {
    implicit def toTuple = (m1, m2, remainder)
    type Append[That] = Results3[M1,M2,That]
    def append[That](tup: (That, Repr)) = Results3(m1, m2, tup._1, tup._2)
  }

  case class Results1[M1](matches: M1, remainder: Repr) extends Results {
    implicit def toTuple = (matches, remainder)

    type Append[That] = Results2[M1, That]
    def append[That](tup: (That, Repr)) = Results2(matches, tup._1, tup._2)
  }

  // and now... Our feature presentation!

  def collateOne[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuildFrom[Repr, R, That],
    remainderBuilder: CanBuildFrom[Repr, T, Repr]
  ) = {
    val matches = matchesBuilder(xs)
    val remainder = remainderBuilder(xs)
    for (x <- xs) if (pf.isDefinedAt(x)) matches += pf(x) else remainder += x
    (matches.result, remainder.result)
  }

  def collate[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuildFrom[Repr, R, That],
    remainderBuilder: CanBuildFrom[Repr, T, Repr]
  ): Results1[That]  = {
    val tup = collateOne[R,That](pf)
    Results1(tup._1, tup._2)
  }
}

object Collatable {
  def apply[Repr, T](xs: Repr)(implicit witness: Repr => Traversable[T]) = {
    new Collatable[Repr, T](xs)
  }
}

implicit def traversableIsCollatable[CC[X] <: Traversable[X], A](xs: CC[A]) =
  Collatable[CC[A], A](xs)
implicit def stringIsCollatable(xs: String) =
  Collatable[String, Char](xs)

Conceptually, it's not all that daunting once you understand how CanBuildFrom works, but I'm finding that it's it's overwhelmed by boilerplate - especially with the implicits.

I know that I could simplify the ResultX logic a great deal by using an HList, and that's something I probably will do, so that piece of code doesn't especially worry me.

I also know that I could make my life significantly easier if I was able to constrain Repr as a subtype of Traversable. But I refuse do that, because then it couldn't be used against Strings. By the same token, I'd also like to avoid forcing the partial functions to return a subtype of T - although this is less of a concern as I could always break my logic into distinct collate and map operations.

More concerning is CanBuildFrom[Repr, T, Repr], which I seem to keep repeating, and which obscures the important stuff from my method signatures. This is something I feel sure can be defined just once at the class level, but I haven't yet found a way to make it work.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

述情 2024-10-21 05:48:16

只需定义类型:

class Collatable[Repr <% Traversable[T], T](xs: Repr) {

  // Results handling stuff, bit like a poor-man's HList, feel free to skip...

  type With[-Elem] = CanBuildFrom[Repr, Elem, Repr]
  type CanBuild[-Elem, +To] = CanBuildFrom[Repr, Elem, To]

  trait Results {
    def remainder: Repr

    type Append[That] <: Results
    def append[That](tup: (That, Repr)): Append[That]

    def andThen[R, That](pf: PartialFunction[T, R])
    (implicit
      matchesBuilder: CanBuild[R, That],
      remainderBuilder: With[T]
    ) = {
      val more = (new Collatable[Repr,T](remainder)).collateOne[R,That](pf)
      append(more)
    }
  }

  def collateOne[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuild[R, That],
    remainderBuilder: With[T]
  ) = { 
    val matches = matchesBuilder(xs)
    val remainder = remainderBuilder(xs)
    for (x <- xs) if (pf.isDefinedAt(x)) matches += pf(x) else remainder += x
    (matches.result, remainder.result)
  }   
}

另一方面,我刚刚注意到整个 Collat​​able 是在 ReprT 上参数化的,所以为什么不呢?获得该级别的隐式 remainderBuilder编辑因为尚未推断出T。现在,我不知道如何摆脱额外的隐式。

Just define types:

class Collatable[Repr <% Traversable[T], T](xs: Repr) {

  // Results handling stuff, bit like a poor-man's HList, feel free to skip...

  type With[-Elem] = CanBuildFrom[Repr, Elem, Repr]
  type CanBuild[-Elem, +To] = CanBuildFrom[Repr, Elem, To]

  trait Results {
    def remainder: Repr

    type Append[That] <: Results
    def append[That](tup: (That, Repr)): Append[That]

    def andThen[R, That](pf: PartialFunction[T, R])
    (implicit
      matchesBuilder: CanBuild[R, That],
      remainderBuilder: With[T]
    ) = {
      val more = (new Collatable[Repr,T](remainder)).collateOne[R,That](pf)
      append(more)
    }
  }

  def collateOne[R, That](pf: PartialFunction[T, R])
  (implicit
    matchesBuilder: CanBuild[R, That],
    remainderBuilder: With[T]
  ) = { 
    val matches = matchesBuilder(xs)
    val remainder = remainderBuilder(xs)
    for (x <- xs) if (pf.isDefinedAt(x)) matches += pf(x) else remainder += x
    (matches.result, remainder.result)
  }   
}

On the other hand, I just noticed that the whole Collatable is parameterized on Repr and T, so why don't you get the implicit remainderBuilder at that level? Edit Because T has not been inferred yet. For now, I don't know how to get rid of the extra implicits.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文