I have an type whose fields I want to keep immutable outside the module. In addition, the type is discriminated by an enum.
type E* = enum first, second
type T* = object
case kind: E
of first: a: int
of second: b: float
Sometimes I need to create objects of type T outside said module.
let t = T( kind: first, a: 5 ) # this would work in the base module
# but in the client it's no dice, because kind and a are not exported
As far as I can tell, the only way to create t is to create a function within T's module,
func new_first*(a: int): T {. inline .} = (
T(kind: first, a: 5)
)
...then call this from the client module:
let t: T = new_first(5)
Is that the idiomatic way to accomplish this? It feels like overkill to me, but I do understand its logic.
Put a different way: there's no way to mark a type's fields as "create-and-read-only", something like to Oberon-2's - visibilty flag, is there?
Thanks. I had a typo, and may have been editing my post while you were replying, so if anyone reads this and wants to know why ynfle wrote first instead of a, it's my fault.
I still have the question about objection creation, though. Is there some way to decorate the type so that I can create new instance like this:
let t: T = T( kind: first, a: 5 )
but things like this are bad?
T.a = 0
That is, can I "undefine" the setter proc `a=`*?
Sorry if my words are unclear, but: I want to create and read from a, but not write to it.
It seems that
proc `a=`*(t: T, value: int) = discard
prevents a from being assigned, but it doesn't raise a compile-time error, which is what I want. I guess it's time to dive into Nim's macro system, unless someone has a better idea.Can you share your code? I did not get the same result
Is this in reply to my erroneous reply, the one I edited out of existence? If so, please ignore it.
What is wrong with a constructor?
Let me make sure I know what you mean. You don't mean this (my inference from experience w/OO programming), because it's illegal Nim code (at least the compiler rejects it):
func T*(value: int): T = {
T( kind: first, a: value )
}
So I guess you mean something like func new_first* above? Of course I can do that, but the naming is inconvenient. I rather like this expression:
let t = T(...)
It's clear and concise. This is also clear, but not quite as concise:
let t: T = new_first(...)
I could leave out the : T but that would mean the reader might not know offhand what new_first is producing.
I don't have a problem with the Nim way, whatever it is; I just want to be sure I know what it is.
Here is how one might make it more idiomatic
type
E* = enum
first
second
T* = object
case kind: E
of first: a: int
of second: b: float
func a*(t: T): int = t.a
func init*(_: typedesc[T], a: int): T = T(kind: first, a: a)
func init*(_: typedesc[T], b: float): T = T(kind: second, b: b)
let
t = T.init(10)
y = T.init(32.0)
echo t
echo y
Thank you! Would this be acceptably idiomatic? I believe it would be more convenient to my use case.
func init*(_: typedesc[T], kind: E, value: int = 0, estimate: float = 0.0) : T = (
case kind
of first: T( kind: first, a: value )
of second: T( kind: second, b: estimate )
)