我正在编写一个解码器,以预定义的格式从字节流解析为对象。我想出了下面的类
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
)可以使用扁平元组代替?我知道无形可以很容易地做到这一点,但我不想为此添加一个额外的库。
p/s:现在再次仔细查看代码,我意识到它无法推断出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)。
我应该怎么做才能允许这样做?
shapeless 是在宽松的许可证 Apache 2.0 下发布的,没有什么可以阻止您简单地将相关的类型类和实例复制并粘贴到您的项目中。你不会做得更好,非常欢迎你这样做。