Re: How to immutably initialize parent object with private fields?
@Araq: Great to hear that there is some activity regarding write tracking, my most anticipated Nim feature ;). I think this is an independent problem though, that rather relates to object construction / initialization, not really related to immutability (my title was misleading). I'm not sure if it is worthy of a RFC, but I tried to sum it up more clearly here: [https://github.com/nim-lang/RFCs/issues/145](https://github.com/nim-lang/RFCs/issues/145) It could very well be that I'm missing something ;).
Re: How to immutably initialize parent object with private fields?
Sorry about this brief reply. 1. A readonly visibility like `field+: type` has been discussed before and seems rather easy to implement (famous last words). 2. I'm working on an update for my writetracking analysis that tackles the problem more systematically and would capture more then simply mutability like which parameters do "escape", which act as pure `out` parameters etc.
Re: How to immutably initialize parent object with private fields?
@Rania_d Note that the issue you linked is only a problem in languages that don't allow named parameters. It's not a problem in Nim because it has named parameters, so no need for the builder pattern. Nim's problem is rather that it doesn't allow to combine immutable objects with inheritance out of the box. @gemath Good idea, so it comes down to either setting the additional fields of the parent object vs the fields of the derived object.
Re: How to immutably initialize parent object with private fields?
Hi, How to design an immutable object with complex initialization: [https://stackoverflow.com/questions/355172/how-to-design-an-immutable-object-with-complex-initialization](https://stackoverflow.com/questions/355172/how-to-design-an-immutable-object-with-complex-initialization)
Re: How to immutably initialize parent object with private fields?
> The accessor are only visible for the internal library proc but not exported > outside of the library. In this case I cannot rely on hiding the fields via re-exporting, because the library is by design supposed to be extended on user side. Think of an UI component library, where users should be able to define their own components. @lscrd Good points about the runtime checks, but yes compile time security would be preferred. I have now found two solutions that seem to satisfy the requirements: 1\. Macro solution: # on lib side template privateInitializer(element: typed, idString: string): untyped = element.id = idString element macro newElement*(T: typedesc, id: string, args: varargs[untyped]): untyped = # convert varargs into an object constructor call of T let constructor = newTree(nnkObjConstr) constructor.add(T.getTypeInst[1]) for arg in args: expectKind(arg, nnkExprEqExpr) constructor.add(newColonExpr(arg[0], arg[1])) # apply post construction initialization of parent fields result = newCall(bindSym"privateInitializer", constructor, id) # on user side newElement(ElementA, id="A", a=1) Run 2\. Template solution with an explicit check that only object constructors are passed in: # on lib side macro verifyObjectConstructor(x: typed): untyped = if x.kind != nnkObjConstr: error($x.repr[0..^1] & " is not an object constructor") template newElement*(idString: string, constructor: typed): untyped = verifyObjectConstructor(constructor) let element = constructor element.id = idString element # on user side newElement("A", ElementA(a: 1)) # abusing the template as an arbitrary setter is now a compile time error: let el = ElementA(a: 1) newElement("A", el) Run @Araq: What is your opinion on this? I'm wondering if it would make sense to differentiate visibility for construction vs field access. As far as I can see a lot of visibility problems would be solved if there was an intermediate between fully-hidden and fully-exposed which is "exposed in object construction". Would it for instance make sense to have type Element* = ref object of RootObj id {.initializable.}: string Run so that public access to `id` is prevented in general, but `id` can be passed into the constructor including subtype constructors?
Re: How to immutably initialize parent object with private fields?
Yes, you are right :-(. I forgot your requirement. I fear there is no simple solution in this case, as you need to create the object with the right type (to get the right size) and, at this moment, you don’t have access to the private field. In fact, I’m sure that my first solution with a cast doesn’t work if extra fields exists in _ElementA_ (it compiles, but will cause problems at runtime). And I’m not sure that using a macro will change anything: you need to allocate in the client module to get the right size and to allocate in the _lib_ module to have access to the private field. This is incompatible. But there are ways to check that _initElement_ is not called several times. For instance, you can add a private boolean field _initialized_ in _Element_ which is false by default. Then _initElement_ becomes: proc initElement(elem: Element, id: string) = if elem.initialized: # already initialized: raise some exception … elem.id = id elem.initialized = true Run Or, if you require that _id_ cannot be the empty string, you no longer need the _initialized_ field: proc initElement(elem: Element, id: string) = if id.len == 0: # id must not be empty: raise some exception. … if elem.id.len != 0: # already initialized: raise some other exception. … elem.id = id Run I agree that these are not very elegant solutions. Maybe using a macro to initialize could solve the problem in a more elegant way. I can’t say for sure as I have not use macros for now.
Re: How to immutably initialize parent object with private fields?
I struggled with this as well several times in several libraries before giving up and assume that people don't hack my internal data structure. Here was my latest trial: type PublicKey* = object Fraw_key: array[64, byte] PrivateKey* = object Fraw_key: array[32, byte] # Hide the private fields and generate accessors. # This is needed to: # - Be able to not store the public_key in PrivateKey in the future and replace it by # an on-the-fly computation # - Signature: have different data representation template genAccessors(name: untyped, fieldType, objType: typedesc): untyped = # Access proc name*(obj: objType): fieldType {.noSideEffect, inline, noInit.} = obj.`F name` # Assignement proc `name=`*(obj: var objType, value: fieldType) {.noSideEffect, inline.} = obj.`F name` = value # Mutable proc `name`*(obj: var objType): var fieldType {.noSideEffect, inline.} = obj.`F name` genAccessors(raw_key, array[64, byte], PublicKey) genAccessors(raw_key, array[32, byte], PrivateKey) Run The accessor are only visible for the internal library proc but not exported outside of the library. However returning a var type was quite tricky. Also var result array generates a null pointer dereference and can't be used at all: [https://github.com/nim-lang/Nim/issues/8053](https://github.com/nim-lang/Nim/issues/8053). Now for ref types with visible fields there is also the issue of shallow vs deep immutability. You cannot have deep immutability for ref types at the moment. See [https://github.com/nim-lang/Nim/issues/8370](https://github.com/nim-lang/Nim/issues/8370) for the latest discussion.
Re: How to immutably initialize parent object with private fields?
@lscrd: The `initElement` is also a setter despite its name ;). You cannot stop users from calling `element.initElement("different id")` and mutating the field at any point in time, which is what I'm trying to avoid. In fact that is also true for my template constructor idea via `newElement("different id", existingElement)`. If anything the template makes it a bit less obvious. Maybe what I really need is a macro taking a typedesc/generic and a list of constructor args that are forwarded to the subtype construction. Then the macro can really only be used for constructing new elements with injected `id` modifications.
Re: How to immutably initialize parent object with private fields?
In this direction, to make it work, you have to do a cast: proc newElementA(): ElementA = cast[ElementA](newElement("A")) Run Another way consists to create a proc _initElement_ in _lib_ : proc initElement*(elem: Element, id: string) = elem.id = id Run then to define _newElementA_ this way: proc newElementA(): ElementA = new result result.initElement("A") Run
Re: How to immutably initialize parent object with private fields?
@Stefan_Salewski: That what I meant with mutable setters ;). I'm trying to avoid them so that user can cannot arbitrarily reset the field. I finally had an idea that I can at least abstract away the mutation of the field: # in lib.nim template newElement*(idString: string, body: typed): untyped = let element = body element.id = idString element Run On user side construction now works like this: type ElementA = ref object of Element a: int ElementB = ref object of Element b: int proc newElementA(): ElementA = newElement("A", ElementA(a: 1)) proc newElementB(): ElementB = newElement("B", ElementB(b: 2)) Run Not sure if there is something nicer, but this doesn't look too bad to me...
Re: How to immutably initialize parent object with private fields?
I tried it once this way: [https://forum.nim-lang.org/t/4226#26316](https://forum.nim-lang.org/t/4226#26316) After fixing the wrong var parameter type I think it was working as expected, but indeed we should have some lecture notes from the bright devs in a Nim textbook, together with the system.proccall usage for methods.
How to immutably initialize parent object with private fields?
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.