Упрощение аннотаций типов - PullRequest
5 голосов
/ 27 января 2011

Я создал метод сутенера, collate, который можно использовать из любого Traversable или любого типа, который может быть приведен к 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))
*/

Подумайте об этомкак нечестивое порождение союза 'twixt collect и partition, если хотите ...

Это определено так:

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, но я обнаружил, что он перегружен стандартным шаблоном - особенно с последствиями.

Я знаю, что мог бы значительно упростить логику ResultX, используя HList, иэто то, что я, вероятно, сделаю, так что этот фрагмент кода не особенно беспокоит меня.

Я также знаю, что мог бы значительно облегчить свою жизнь , если бы смог ограничить Repr как подтип Traversable.Но я отказываюсь делать это, потому что тогда это не может быть использовано против Стрингов.К тому же, я бы также хотел избежать принудительного вызова частичными функциями возврата подтипа T - хотя это не так важно, поскольку я всегда мог разбить свою логику на отдельные операции сопоставления и сопоставления.

Что касается CanBuildFrom[Repr, T, Repr], который я, кажется, повторяю и который затемняет важные вещи из моих сигнатур методов.Это то, что, я уверен, может быть определено только один раз на уровне класса, но я еще не нашел способ заставить его работать.

Есть идеи?

1 Ответ

1 голос
/ 27 января 2011

Просто определите типы:

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)
  }   
}

С другой стороны, я только что заметил, что целое Collatable параметризовано на Repr и T, так почему бы вам не получить неявноеremainderBuilder на этом уровне? Редактировать Поскольку T еще не было выведено.Пока я не знаю, как избавиться от дополнительных последствий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...