Open pietroppeter opened 1 year ago
One problem we discussed in a past Speaking Hour is the fact that we must be able to serialize all our objects to JSON. The problem being that we don't have static knowledge of the exact type of each block. If we use the dynamic dispatch functionality of Nim, we can create a dumpHook
for the base type and then call a method
that returns the string of the specific type:
import jsony
type
Nimib = ref object of RootObj
a, b, c: int
NimibChild = ref object of Nimib
d, e: float
method dump(n: Nimib): string =
n[].toJson()
method dump(n: NimibChild): string =
n[].toJson()
proc dumpHook*(s: var string, v: Nimib) =
s.add v.dump()
let n1: Nimib = Nimib(a: 1, b: 2, c: 3)
let n2: Nimib = NimibChild(d: 3.14, e: 4.56)
echo n1.toJson()
echo n2.toJson()
The next problem then becomes how we parse the JSON again without losing the type information...
Here is a solution that works:
import jsony, tables
type
Nimib = ref object of RootObj
a, b, c: int
typename: string
NimibChild = ref object of Nimib
d, e: float
method dump(n: Nimib): string =
n[].toJson()
method dump(n: NimibChild): string =
n[].toJson()
proc dumpHook*(s: var string, v: Nimib) =
s.add v.dump()
let n1: Nimib = Nimib(a: 1, b: 2, c: 3, typename: "Nimib")
let n2: Nimib = NimibChild(a: 100, d: 3.14, e: 4.56, typename: "NimibChild")
echo n1.toJson()
echo n2.toJson()
proc parseNimib(s: string, i: var int): Nimib =
var v = Nimib()
parseHook(s, i, v[])
result = v
proc parseNimibChild(s: string, i: var int): Nimib =
var v = NimibChild()
parseHook(s, i, v[])
result = v
let parseDefs = {
"Nimib": parseNimib,
"NimibChild": parseNimibChild
}.toTable()
proc parseHook*(s: string, i: var int, v: var Nimib) =
var n: Nimib = Nimib()
let current_i = i
parseHook(s, i, n[])
i = current_i
let typename = n.typename
v = parseDefs[typename](s, i)
let s = n2.toJson().fromJson(Nimib)
echo s.toJson()
We store the type name as a string in the mandatory field typename
and add a proc for each type name in a table. Then we can look up the correct proc when parsing the JSON in parseHook
. The downside of this is that we must construct the table and know all the blocks that exists. But this could be solved by requiring blocks to be "registered". The act of registering a block could then generate all the above necessary proc/methods automatically.
This is a lot to take in, but it works.
And here the template version is:
import jsony, tables
type
Nimib = ref object of RootObj
a, b, c: int
typename: string
NimibChild = ref object of Nimib
d, e: float
var parseDefs: Table[string, proc (s: string, i: var int): Nimib]
template registerBlock(typename: untyped) =
parseDefs[$typename] =
proc (s: string, i: var int): Nimib =
var v: typename
new v
parseHook(s, i, v[])
result = v
method dump(n: typename): string =
n[].toJson()
# neccecary to avoid compile error
method dump(n: Nimib): string =
n[].toJson()
proc dumpHook*(s: var string, v: Nimib) =
s.add v.dump()
proc parseHook*(s: string, i: var int, v: var Nimib) =
# First parse the typename
var n: Nimib = Nimib()
let current_i = i
parseHook(s, i, n[])
# Reset i
i = current_i
# Parse the correct type
let typename = n.typename
v = parseDefs[typename](s, i)
registerBlock(Nimib)
registerBlock(NimibChild)
let n1: Nimib = Nimib(a: 1, b: 2, c: 3, typename: "Nimib")
let n2: Nimib = NimibChild(a: 100, d: 3.14, e: 4.56, typename: "NimibChild")
echo n1.toJson()
echo n2.toJson()
let s = n2.toJson().fromJson(Nimib)
echo s.toJson()
It's just one extra line of code for a block developer with this approach.
With this, I think we might actually be able to start working for real on a refactoring of Nimib.
Wow this looks very interesting, thanks a lot! I will have to look at it more closely and try it out! Will let you know my thoughts!
some discussion here: https://github.com/nimib-land/nblog/issues/20