I'm running into this problem in every project of mine, and I simply can't find 
a good solution, although the problem seems very basic.

Let's say I write a dummy `lib.nim` like this:
    
    
    # The lib provides a definition of an abstract base type
    # without any actual instantiations...
    type
      Element* = ref object of RootObj
        # Example for a field that should be an implementation
        # detail, used only internally in `lib`. It should
        # neither be public nor should there be getter/setter
        # so that we can rely on the fact that it does not
        # change after construction.
        id: string
    
    proc run*(elements: openarray[Element]) =
      for element in elements:
        echo "Touching element: " & element.id
    
    
    Run

The library is intended to be instantiated on user side, for example:
    
    
    import lib
    
    type
      ElementA = ref object of Element
      ElementB = ref object of Element
    
    # But how to construct them?
    
    proc newElementA(): ElementA =
      ElementA(id: "A")
    
    proc newElementB(): ElementB =
      ElementB(id: "B")
    
    let elements = @[
      newElementA().Element,
      newElementB(),
    ]
    
    run(elements)
    
    
    Run

This doesn't compile because the user has no access to `id` at all, even in the 
constructor. Usually I provide a constructing proc in the scope of the type, 
e.g.:
    
    
    # in lib.nim
    proc newElement*(id: string): Element =
      Element(id: id)
    
    
    Run

Now users can construct an `Element`, but I'm not sure if it helps constructing 
subtypes. This crashes at runtime with an invalid conversion (kind of expected):
    
    
    proc newElementA(): ElementA =
      result = newElement("A").ElementA
    
    
    Run

I also don't want to add setter procs/templates to `lib.nim` because than the 
field gets fully exposed.

Am I missing some basic template/macro tricks to solve this basic problem? I 
have spend hours going back and forth making fields private and public again or 
moving code around because I can't seem to get the visibility right.

Reply via email to