2017-01-10 2 views
1

Я пишу декодер для синтаксического анализа из потока байтов в объект с предопределенным форматом. Я придумал ниже классасгладить вложенный кортеж в scala (без бесформенного)

trait Decoder[In] { 
    type Out 
    def decode(bs: In): (Out, In) 
} 

object Decoder { 
    type ByteString = List[Byte] // for testing... 

    // define some blocks for client to chain up to build format pattern 
    val int = new Decoder[ByteString] { 
    type Out = Int 
    def decode(bs: ByteString): (Out, ByteString) = { 
     val (fst, snd) = bs.splitAt(4) 
     val value = fst.foldLeft(0)((acc, b) => acc * 0xff + b) 
     (value, snd) 
    } 
    } 
    val long = new Decoder[ByteString] { 
    type Out = Long 
    def decode(bs: ByteString): (Out, ByteString) = { 
     val (fst, snd) = bs.splitAt(8) 
     val value = fst.foldLeft(0L)((acc, b) => acc * 0xff + b) 
     (value, snd) 
    } 
    } 
} 

затем создал вспомогательный конструктор для цепи блока вместе:

class DecoderBuilder[In](decoder: Decoder[In]) { 
    def ~(d: Decoder[In]) = { 
    val combine = new Decoder[In] { 
     type Out = (decoder.Out, d.Out) 
     def decode(bs: In): (Out, In) = { 
     val (el1, remain1) = decoder.decode(bs) 
     val (el2, remain2) = d.decode(remain1) 
     ((el1, el2), remain2) 
     } 
    } 
    new DecoderBuilder(combine) 
    } 

    def buildApply[T](data: In)(f: decoder.Out => T) = f(decoder.decode(data)._1) 
} 
object DecoderBuilder { 
    implicit def ddd[In](d: Decoder[In]) = new DecoderBuilder(d) 
} 

С выше кода я уже могу написать код так:

import Decoder._ 
import DecoderBuilder._ 

    val data = List[Byte](
    0x00, 0x00, 0x00, 0x01, // the 1st field: Int 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // 2nd field: Long 
    0x00, 0x00, 0x00, 0x03 // the 3rt field: Int 
    ) 
    val format_type = int ~ long ~ int 
    val result_type = format_type.buildApply(data) { 
    case ((i, l), ii) => MyObject(i, l, ii) 
    } 
    println(result_type) // --> MyObject(1,2,3) 

Однако , когда шаблон формата становится длиннее, вложенный кортеж становится более трудным для чтения. Нужно ли вообще переписать выше DecoderBuilder, чтобы клиентская сторона (с buildApply) могла вместо этого использовать сплющенный кортеж? Я знаю, что бесформенный может легко сделать это, но я не хочу добавлять дополнительную библиотеку только для этого.

р/с: Теперь внимательно смотрим на код еще раз, я понял, что он не может определить тип внутри buildApply, то есть я не могу это сделать

val result_type = format_type.buildApply(data) { 
    a => MyObject(a._1._1, a._1._2, a.2) 
} 

, потому что тип a является format_typedecoder.Out, не `((Int, Long), Int).

Что мне делать, чтобы это разрешить?

ответ

3

бесформенный опубликован под разрешительной лицензией Apache 2.0, вам нечего мешать просто копировать и вставлять соответствующие типы классов и экземпляры в ваш проект. Вы не будете делать намного лучше, и вы очень любезны сделать именно это.

+0

Я понял, что мы можем свернуть через HList, так потрясающе! Поэтому я думаю, что буду кодировать формат, такой как 'int :: long :: int :: HNil', затем сбрасывать его, используя другой HList в качестве аккумулятора для возврата' 1 :: 2 :: 3 :: HNil'. Но, глядя на пример сложения на бесформенном репо, я все еще не знаю, как это сделать, или если это вообще возможно. Не могли бы вы дать мне какой-то намек? И еще раз спасибо за такую ​​полезную библиотеку :) – it4rb