-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] standardize constructors as: Foo.New(args) instead of newFoo(args) #34
Comments
I like this idea of generic procedures, not sure why no-one has thought of this before. It solves the problem beautifully and is much more flexible than the current system. I'd love to see this, the |
I'm using somewhat similar approach in nimx, but with a twist that So to summarize what I'd want, is a |
Still has some of the disadvantages that I've described on the forum and these are not addressed in your proposal.
Irrelevant without more justification of why that change is necessary in the first place.
Irrelevant without more justification of why that change is necessary in the first place.
How so? You need to elaborate on this point with real use cases.
These |
Simple example: type
Bar = object
age: int
Baz = object
height: int
proc new(T: typedesc[Bar], age: int): Bar = Bar(age: age)
proc new(T: typedesc[Baz], height: int): Baz = Baz(height: height)
let a = Bar.new(100)
template newInst(old: untyped, ageOrHeight: int): untyped =
old.type.new(ageOrHeight)
proc newInstProc(old: Bar | Baz, ageOrHeight: int): Bar | Baz =
old.type.new(ageOrHeight)
let b = a.newInst(123)
let c = a.newInstProc(123) Note that this trivial example is super dumb, but it's not impossible to imagine this being useful for lot's of other meta-programming cases, or just generic procedures as shown above. Doing what's done in the example today requires a macro that get's the type name and generates a call to As for namespace pollution I'd say having one name |
Well I asked for real use cases, not for toy examples. |
Personally, I think it would be nicer if we introduce a way to use the name of the type as a callable proc. type Foo = object ...
# some magic goes here (perhaps defining an `=init` proc)
# construct instances as in Python
var f = Foo(10, 20) Besides this, we can benefit from a "placement new" operator that places the return value of a call into a raw slot of memory. Then we can have custom alloc helpers that work like this: template myAlloc(x: typed) =
type T = type(x)
var mem = myAllocImpl(sizeof(T))
mem <-- x # this the special "placement new" operator
# but one could argue that the good old `mem[] = x` syntax
# could work here without problems
# the usage looks like this
var f = myAlloc Foo(10, 20)
|
@Araq, here's a real-life use case: myWindow.makeLayout:
- SplitView:
...
- TableView:
...
- View:
... The |
@Araq, the same thing that @yglukhovs example does is what is done in the various genui macros. If this syntax was adopted those macros would be a lot easier to create. |
You can easily generate I also don't see a point in this, other than "it looks nicer". The convention for Edit: Okay, I can see it composing better for generics. That is indeed an advantage. Not sure it's enough to warrant this sweeping change though. |
Yeah it's not particularly hard, it's just unnecessary. If you look at my above example you would be able to do things that was previously only achievable with a macro using only a simple template, or even a proc. This to me is enough of a benefit in it's own right. As for if it's worth it for all the breakage it will cause I'd say so. None of the old code is going to break, it's not like "new" or "init" is going to stop working. So things can be updated at a leisurely pace. Whenever we get nim-format it could of course complain if you tried to name your proc initThis or newThat so to let people know not to use it anymore (as if deprecation warnings from the stdlib wasn't enough). Nim is inching ever closer to that 1.0 release, and it should try to look as best as it can when it get's there. This solution definitely feels more thought through and "clean" as opposed to the old solution which always felt like a bit of a hack. |
@dom96, there's also a problem with type
Foo = ref object of RootRef
Bar = ref object of Foo
proc newFoo(): Foo =
result.new()
# ... initialize result
proc newBar(): Bar =
result.new()
# ... err, now how do I initialize Foo? Surely, this is just a cosmetics question, and can be worked around. In my case I've just introduced |
@yglukhov sorry, but I don't understand your example. Can you elaborate? @PMunch Even though it might not break anything, it will make a lot of code obsolete. Changing something like this, so close to v1.0 will mean that a lot of code out there will start showing deprecation warnings. Including most of the code from my book. |
What's worse though, being stuck with a bad design forever, or having a period of deprecation warnings close to 1.0? It's a shame about the book, but if we create a comprehensive style/syntax guide for the release of 1.0 I'd say it's fine, there is a disclaimer in the book about Nim being pre 1.0 isn't it? Besides, look at breaking changes between Python 2 and Python 3, breaking is normal for crossing version boundaries. And better to have them break before 1.0 than after.. And again, this doesn't even a real break. |
@dom96, my example is about constructors in terms of OOP. If we treat |
@yglukhov I understand now, it's not possible to do |
in C++, object construction from a single argument and explicit conversion are just the same things, why it's the problem? Foo(args) in languages with GC usually just does it - constructs object of type Foo using constructor with args. IMHO it's very important to keep this simple syntax for this task, rather than require to attach "new" or so on any side of it. This makes an entire program looking like we just dealing with values, without going into intrinsics where and what is constructed on the heap or stack: type Tree[T] = ref tuple
left: Tree[T]
x: T
right: Tree[T]
let t = Tree( nil, 1, Tree( nil, 2, nil)) |
Should we close this RFC in favor of #48 ? As far as I can see after zah's edit they have become very similar. Differences on first glance:
|
We should. |
motivation
this is the analog of https://github.com/nim-lang/Nim/issues/7430 (universal conversion operator (cf D's
a.to!T
) (std.conv.to)) but for constructors.similar motivation as:
https://forum.nim-lang.org/t/703 Constructors (and tiny bit of destructors)
https://forum.nim-lang.org/t/1190/3 [RFC] Constructors proposition
in short:
newFoo(args)
is not generic (hard to use in generic code given a generic type), pollutes namespace, not standard (lots of languages don't need composite names).options that have been considered as replacement for
newFoo(args)
:Foo(args)
:Foo(args) is already used for explicit conversion, eg https://forum.nim-lang.org/t/1190/1#7342
new Foo(args)
:eg proposed here: https://forum.nim-lang.org/t/703/1#3885
but that doesn't work as
new Foo
already has a meaning (GC allocation) and creates aref Foo
, not aFoo
My proposal is the following:
Foo.New(args)
Here's an example:
Advantages
newFoo
constructors can be changed gradually toFoo.New
constructors, independently for each Foo)another advantage is it allows to define constructors for multiple types, with just 1 definition, eg:
note
as for name to use, this can be debated , eg: instead of
New
:make
,create
,ctor
etcThe text was updated successfully, but these errors were encountered: