Open ashquarky opened 6 months ago
Ideally we would implement Scanner and Valuer so the database engine can work on our types directly (no conversions), but that would require adding
database/sql/driver
as a dependency which may or may not be acceptable. Let me know if I can do that though.
I was going to suggest this. We already do this for other types. Around a year ago I made https://github.com/PretendoNetwork/pq-extended which is a drop-in replacement for pq
that adds support for more types, namely []uint8
.
We could probably just add support for our types there, to keep the networking libraries "clean". But that would come at the cost of needing to manually handle each List
type.
@DaniElectra what do you think? I'm not SUPER against adding the Scan/Value interface to our types in the networking libraries, but only if there's a lot of demand for it. It will require some refactoring to do, since we already use Value()
and Value
for our own needs
That sounds okay to me. We could add the Scan/Value interface here on the primitive types and then handle the lists on pq-extended
Though first we'd need a different name for our current use of Value
. Maybe we can replace it with Real
or something like that?
Honestly what if we revisit the idea of making primitives just type aliases, and even just not using pointers everywhere all the time?
In the rewrite PR I had said that this wasn't viable due to us needing to use pointer receivers, but I was slightly incorrect https://github.com/PretendoNetwork/nex-go/pull/42#issuecomment-1868363357
We CAN use type aliases here, while using pointer receivers, and conforming to RVType
. We just have to make sure we call those methods on a pointer to the type:
package main
import (
"fmt"
)
type RVType interface {
ExtractFrom()
}
type UInt32 uint32
func (u32 *UInt32) ExtractFrom() {
*u32 = UInt32(20)
}
func main() {
var u32 UInt32 = 10
fmt.Println(u32) // 10
extract(&u32)
fmt.Println(u32) // 20
}
func extract(t RVType) {
t.ExtractFrom()
}
This also makes the types directly compatible with the built in types through type conversions:
package main
import (
"fmt"
)
type RVType interface {
ExtractFrom()
}
type UInt32 uint32
func (u32 *UInt32) ExtractFrom() {
*u32 = UInt32(20)
}
func main() {
var u32 UInt32 = 10
fmt.Println(u32) // 10
extract(&u32)
fmt.Println(u32) // 20
slice := make([]uint32, 0)
slice = append(slice, uint32(u32))
fmt.Println(slice) // [20]
}
func extract(t RVType) {
t.ExtractFrom()
}
However, unlike in your comment here https://github.com/PretendoNetwork/nex-go/pull/42#issuecomment-1868361526 we cannot, actually, use built in types in places like List.Append
so long as those generics expect RVType
. Which means:
list := List[UInt32]{}
list.Append(1)
Would NOT work here. We would still need to create a new instance of UInt32
, and the List
MUST take in pointers to the RVType
still:
package main
import (
"fmt"
)
type RVType interface {
ExtractFrom()
}
type UInt32 uint32
func (u32 *UInt32) ExtractFrom() {
*u32 = UInt32(20)
}
func NewUInt32(v int) *UInt32 {
var u32 UInt32 = UInt32(v)
return &u32
}
type List[T RVType] struct {
real []T
Type T
}
func (l *List[T]) Append(value T) {
l.real = append(l.real, value)
}
func main() {
var u32 UInt32 = 10
fmt.Println(u32) // 10
extract(&u32)
fmt.Println(u32) // 20
slice := make([]uint32, 0)
slice = append(slice, uint32(u32))
fmt.Println(slice) // [20]
list := List[*UInt32]{}
list.Append(&u32)
fmt.Println(list) // {[0xc000012110] <nil>}
list.Append(NewUInt32(1))
fmt.Println(list) // {[0xc000012110 0xc00001212c] <nil>}
}
func extract(t RVType) {
t.ExtractFrom()
}
That being said we still use Value
on other types outside of primitives. For example DateTime
and PID
have Value()
methods. Also several types in the protocols lib, like DataStoreKeyValue
and SimpleSearchCondition
have Value
fields.
Lists of RVTypes can be difficult to feed into other libraries. For example,
List[String]
shows up in DataStore in a few places, whereas the database engine expects a[]string
.These two functions allow for more ergonomic conversions between NEX types and their primitives. Using
List[String]
as an example, here's converting to primitives for the database:Converting from primitives is nicer again, due to the
NewString
function already having the right signature:Ideally we would implement Scanner and Valuer so the database engine can work on our types directly (no conversions), but that would require adding
database/sql/driver
as a dependency which may or may not be acceptable. Let me know if I can do that though.