2014-10-06 4 views
2

Я нахожу, что хочу, чтобы написать это:Как сделать класс flatMappable?

def allAsAndFs(node: Node): PairOfSeqs[Action, Factor] = 
    node.regularNames.flatMap { name => regularAsAndFs(name) } ++ 
    node.specialNames.flatMap { name => specialAsAndFs(name) } 

где:

def regularNames: Seq[String] 
def specialNames: Seq[String] 

def regularAsAndFs(name: String): PairOfSeqs[Action, Factor] 
def specialAsAndFs(name: String): PairOfSeqs[Action, Factor] 

и:

class PairOfSeqs[A, B](as: Seq[A], bs: Seq[B]) { 
    def ++(that: PairOfSeqs[A, B]): PairOfSeqs[A, B] = 
    PairOfSeqs(as ++ that.as, bs ++ that.bs) 
    . . . 
} 

Что мне нужно сделать, чтобы flatMap сливаться в PairsOfSeqs по , вызывающий соответствующий метод ++?

Похоже PairOfSeqs должна распространяться GenTraversableOnce, но GenTraversableOnce имеет довольно удивительные 47 абстрактные методы для переопределения.

Что такое идиоматический, скалистический способ сделать это?

ответ

3

Вы используете несколько нестандартную идею flatMap. Нормальный тип подписи

class C[A] { 
    def flatMap[B](f: A => C[B]): C[B] = ??? 
} 

Но вы хотите (что-то похожее на - это не компилируется):

def foo[D <: { def ++(d: D): D }](f: A => D): D 

который гораздо больше похож fold чем flatMap.

Одна из возможностей состоит в том, чтобы сделать PairOfSeqs полноценной коллекцией. Это будет сложно, потому что типы действительно не выстраиваются так хорошо, и потому что расширение коллекций затруднено. (Соотношение вознаграждение/усилие может быть хорошо, потому что даже несмотря на усилия, большая награда огромный, если вам действительно нужно.)

Возможно лучшая возможность просто иметь метод расширения на Seq.

case class PairOfSeqs[A, B](as: Seq[A], bs: Seq[B]) { 
    def ++(that: PairOfSeqs[A, B]): PairOfSeqs[A,B] = 
    PairOfSeqs(as ++ that.as, bs ++ that.bs) 
} 

implicit class SeqCanPairMap[A](val underlying: Seq[A]) extends AnyVal { 
    def flatPair[B,C](f: A => PairOfSeqs[B,C]): PairOfSeqs[B,C] = { 
    val ps = underlying.map(f) 
    PairOfSeqs(ps.map(_.as).fold(Seq[B]())(_ ++ _), ps.map(_.bs).fold(Seq[C]())(_ ++ _)) 
    } 
} 

Seq("fish", "fowl").flatPair(s => new PairOfSeqs(s.map(_.toChar), s.map(_.toInt))) 

Есть множество других вариантов (например, определить более традиционные flatMap на PairOfSeqs, и переход от регулярных seqs к PairOfSeqs с пустым вторым слотом), но это, вероятно, покрывает ваш случай использования достаточно хорошо.

И наконец, чтобы сделать класс flatMappable, все, что вам нужно сделать, это определить flatMap. В частности, для понимания можно использовать его. (Но вам тоже понадобится map)

+0

Я думал, что добавление метода 'flatMap' к' PairOfSeqs' не будет работать, потому что 'Seq.flatMap' фактически получает называется. 'Seq.flatMap' может совместить (« сгладить »)' List', 'Option',' Set' и другие вещи правильно. Я хочу знать, как заставить его правильно скомбинировать «PairsOfSeqs». Я что-то недопонимаю в отношении «flatMap»? (Я все еще перевариваю 'SeqPairCanMap' ...) –

+0

@BenKovitz -' flatMap' на самом деле вызывает сборщиков, полученных из неявного 'CanBuildFrom'. Но я думаю, что вы спрашиваете, можете ли вы получить преобразование _implicit_ из 'Seq' в' PairOfSeqs', когда вы используете 'flatMap'. Там ответ - нет. –

+0

ОК, теперь я думаю, что вижу свое замешательство, которое хорошо объясняется сигнатурами типа в верхней части вашего ответа. Я забыл, что 'Seq.flatMap' возвращает' Seq', 'Set.flatMap' возвращает' Set' и т. Д., Просто не обязательно с теми же параметрами типа.Я думал, что 'flatMap (f)' будет возвращать значение с тем же типом, который возвращает 'f' (что, как я полагаю, при обычном использовании). Благодаря! –

Смежные вопросы