I'd like to discuss code styling for a minute. It would be good to agree on a general approach of the code we'd like to write. Here are some examples, I'd like to hear more about what you think
##Code styling
Embedded type and optimized struct sizes
type Format struct {
SampleRate uint32
Channels int16
}
type Buffer32 struct {
Format
Data []float32
}
vs pointers and convenient field types
type Format struct {
SampleRate int
Channels int
}
type Buffer32 struct {
Format *Format
Data []float32
}
Convenient but straight forward constructor
func NewBuffer32(format Format, sampleCount int) *Buffer32 {
return &Buffer32{
Format: format,
Data: make([]float32, sampleCount, sampleCount),
}
}
vs DIY approach. (note that in this code, the sample count should probably
multiple by the number of channels which is an easy thing to forget.)
&Buffer32{
Format: format,
Data: make([]float32, sampleCount),
}
Explicit, manually, duplicated functions
func (buffer *Buffer32) Process32(output *Buffer32) error {
// stuff
return nil
}
func (buffer *Buffer64) Process64(output *Buffer64) error {
// stuff
return nil
}
vs interface and type switching
func Process(buf1, buf2 Buffer) error {
switch t := t.(type) {
*Buffer32:
// some stuff
*Buffer64:
// some other stuff
}
return nil
}
vs using go generate for any functions implemented the same way in float32 and 64
I personally like:
2: I don't think the convenience of a having a field in a type we can easily manipulate trumps the memory size gain. Plus using a pointer would allow us to provide predefined formats.
3: Constructors are often convenient, especially as entry points and when the setup isn't trivial. This example is a good example since it was was written by hand and technically has a bug. This bug would be mitigated by a constructor. It does come at a price of a bigger API and potentially surprising behaviors.
Finally 5: It might be a bit of a pain at first but it works better with code completion (easier for people not knowing the API), it can be optimized (custom logic and/or SIMD) but can be the source of more bugs (fix something on the float32 side but not float64 for instance). 7 (code generation) is nice but it always feels a bit clunky to have to move things outside of the code generation path because of some edge cases.
@egonelbre @kisielk @nigeltao what do you think?
I'd like to discuss code styling for a minute. It would be good to agree on a general approach of the code we'd like to write. Here are some examples, I'd like to hear more about what you think
##Code styling
Embedded type and optimized struct sizes
vs pointers and convenient field types
Convenient but straight forward constructor
vs DIY approach. (note that in this code, the sample count should probably
multiple by the number of channels which is an easy thing to forget.)
Explicit, manually, duplicated functions
vs interface and type switching
vs using go generate for any functions implemented the same way in float32 and 64
I personally like:
2: I don't think the convenience of a having a field in a type we can easily manipulate trumps the memory size gain. Plus using a pointer would allow us to provide predefined formats.
3: Constructors are often convenient, especially as entry points and when the setup isn't trivial. This example is a good example since it was was written by hand and technically has a bug. This bug would be mitigated by a constructor. It does come at a price of a bigger API and potentially surprising behaviors.
Finally 5: It might be a bit of a pain at first but it works better with code completion (easier for people not knowing the API), it can be optimized (custom logic and/or SIMD) but can be the source of more bugs (fix something on the float32 side but not float64 for instance). 7 (code generation) is nice but it always feels a bit clunky to have to move things outside of the code generation path because of some edge cases.
@egonelbre @kisielk @nigeltao what do you think?