With foundational background in dynamically typed programming languages, I am struggling to come to terms with static typing.
Static typing itself would have been fine if it wasn’t causing the need to either violate the DRY principle, or use performance-costly reflection in certain situations. Like in this one:
Say we need to write a video stream parser. Video binary data is comprised of frames, each having a type, a certain header structure and a payload. My CCTV cameras produce streams of 5 frame types (video key, video non-key, audio, still picture and info), but for simplicity let’s consider only 2, simplified types. Full example with some fake data:
package main
import (
"fmt"
"encoding/binary"
"bytes"
"io"
)
type Type byte
const (
Video Type = 0xFC
Audio Type = 0xFA
)
var HMap = map[Type]string {
Video: "Video",
Audio: "Audio",
}
type CommonHeader struct {
Type Type
}
type Header interface {
GetLength() int
}
type HeaderVideo struct {
Width uint16
Height uint16
Length uint32
}
type HeaderAudio struct {
SampleRate uint16
Length uint16
}
func (h HeaderVideo) GetLength() int {
return int(h.Length)
}
func (h HeaderAudio) GetLength() int {
return int(h.Length)
}
var TMap = map[Type]func() Header {
Video: func() Header { return &HeaderVideo{} },
Audio: func() Header { return &HeaderAudio{} },
}
func main() {
data := bytes.NewReader([]byte{0xFC, 0x80, 0x07, 0x38, 0x04, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xAF, 0xFA, 0x10, 0x00, 0x01, 0x00, 0xFF})
var cHeader CommonHeader
for {
err := binary.Read(data, binary.LittleEndian, &cHeader)
if err != nil {
break
}
fmt.Println(HMap[cHeader.Type])
frame := TMap[cHeader.Type]()
binary.Read(data, binary.LittleEndian, frame)
fmt.Println(frame)
payload := make([]byte, frame.GetLength())
io.ReadFull(data, payload)
fmt.Println(payload)
}
}
See — exactly the same implementation of the GetLength()
method has to be repeated for each frame type. Not a big deal I hear you say. Indeed, in this case. Even if repeated 5 times in the real code (5 frame types). But the fact remains: the code cannot be DRYed in principle. Unless using reflection:
func GetLength(frame any) int {
return int(reflect.ValueOf(frame).Elem().FieldByName("Length").Uint())
}
var TMap = map[Type]func() any {
Video: func() any { return &HeaderVideo{} },
Audio: func() any { return &HeaderAudio{} },
}
So, I am just wondering: how do the current authors of Go see this problem? Is it recognised? Will it be tackled at some point? Or do they not see it a problem at all?