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