First of all thanks to OP redditor and everyone else for this thread. I have 
been curious about Nim for a long time and interested in its balance of great 
features, nice syntax and maybe some rough edges.

Please forgive my completely uninformed participation, as someone entirely new 
to Nim, but I also wanted to understand a bit how it works (I previously asked 
a [related 
question](https://www.reddit.com/r/nim/comments/fuu8ps/what_would_be_the_nim_equivalent_of_a/)
 on Reddit a few months ago and the answer was basically "use marshal").

My impression is that Nim behaviour here seems a bit inconsistent...
    
    
    import json
    
    type
      DocItem = object of RootObj
        tags: seq[string]
      
      TextDoc = ref object of DocItem
        text: string
      
      TodoDoc = ref object of DocItem
        todo: string
    
    var docs: seq[ref DocItem]
    
    let textdoc = TextDoc(text: "some doc", tags: @["help"])
    let tododoc = TodoDoc(todo: "some todo")
    
    docs.add textdoc
    docs.add tododoc
    
    echo textdoc[]
    # (text: "some doc", tags: @["help"])
    echo textdoc.text
    # some doc
    echo tododoc[]
    # (todo: "some todo", tags: @[])
    echo tododoc.todo
    # some todo
    
    # ...all good so far
    
    echo docs[0] of TextDoc
    # true
    
    echo docs[0][]
    # (tags: @["help"])
    # 🤔  we are missing the `text` field
    # must be because type of var docs: seq[ref DocItem]
    # but above we just said this element is still "of TextDoc"
    
    echo docs[0].text
    # does not compile:
    # Error: undeclared field: 'text'
    
    
    Run

It seems confused about whether elements of `docs` sequence have been truncated 
down to `DocItem` or still have their full type. I'm sure someone who knows Nim 
better can explain the semantics here?

And if it's going to squash them to fit the type of the sequence then it would 
be useful if Nim had proper sum types so you could widen the definition of the 
`docs` type.

We can find a description of two options for heterogenous sequences here: 
[https://forum.nim-lang.org/t/4233#26367](https://forum.nim-lang.org/t/4233#26367)
 The second is the one above ("boxed types") that doesn't work properly.

The first is using "object variants", which seems to be what Nim has instead of 
sum types. 
[https://github.com/nim-lang/Nim/wiki/Common-Criticisms#sum-types-are-weird](https://github.com/nim-lang/Nim/wiki/Common-Criticisms#sum-types-are-weird)

The need to have a "discriminator" field to use in the `case` means we now have 
to make a custom `%` proc for our variant type, so that we can omit that 
superfluous field from the serialized output.

So you end up with this: 
    
    
    import json
    
    type
      DocKind = enum BaseDoc, TextDoc, TodoDoc
      
      DocItem = object
        tags: seq[string]
        case kind: DocKind
        of TextDoc: text: string
        of TodoDoc: todo: string
        else: discard
    
    var docs: seq[DocItem]
    
    docs.add DocItem(kind: TextDoc, text: "some doc", tags: @["help"])
    docs.add DocItem(kind: TodoDoc, todo: "some todo")
    
    proc `%`*(doc: DocItem): JsonNode =
      result = case doc.kind
        of TextDoc: %*
          {"text": doc.text, "tags": doc.tags}
        of TodoDoc: %*
          {"todo": doc.todo, "tags": doc.tags}
        else: %*
          {"tags": doc.tags}
    
    echo %docs
    
    
    Run

I spent a long time trying to use `fieldPairs` in the `%` proc and just filter 
out the `kind` field, but I could not get anything to work (it seems 
`fieldPairs` is some kind of fragile magic that only works in a for loop syntax 
position and gives an error in other places)

I'd love to know if there is a way to write this `%` proc that doesn't need to 
be updated each time you add a new kind to `DocKind`. It is fairly cumbersome 
as is. 

Reply via email to