The main difference is that new() is the only way to get a pointer to an
unnamed integer or other basic type. You can write "p := new(int)" but
you can't write "p := &int{0}". Other than that, it's a matter of
preference.
Ian
> The & operator provides the address of an existing entity or of a
> (newly allocated) literal. But you can't write e.g. 'a := &123', you
> have to write 'a := new(int); *a = 123;'. Without 'new' it wouldn't be
> possible to do such things as (explicitly) heap allocating an integer,
> string ...
As it hinted at in your first line above, not so:
--
Chris "allusive" Dollin
AFAICS, p := new(T) is exactly equivalent to:
var p *T; {var i T; p = &i}
but it's more convenient sometimes.
personally, i still think that new's functionality should be subsumed
into make.
For &{} - do you not have to provide initial values with that?
--
Scott Lawrence
<snip>
And why is this? Can't make (or new) be made smarter so that it checks
to see what needs to be created and just does it?
Regards,
Miek
new(T) returns *T.
so it can't return chan int, which doesn't have the form *T.
so another operator is needed.
for a short time, we tried getting by with just make,
and make(*T) was like new(T) is today.
people found it too surprising coming from
c, c++, and java.
russ
I am quite familiar with that page, and with the usual email that everyone new on this
list gets when it complains about it.
From my point of view, if the Go compiler was a bit smarter, new and make could be merged
into a common language construct. It is just a matter where to put the complexity, on the compiler itself,
or on the code the developers have to write.
new(T) returns type *T
make(T) returns T
it is true that the compiler could figure it out
with some common syntax, but it would be wrong.
We understand the distinction, and we understand that there is some
underlying issue that makes it convenient for the language developers
to have both new() and make(). But I think this conversation keeps
going around and around because, from a user perspective, there's no
apparent reason to require a special function to create a value/stack
instance of only _certain_types_. Given that we seem to have come to
the understanding that the compiler _could_ figure things out in
context, appeals to what's going on behind the scenes don't hold much
weight, in this frame of reference. I believe that, to many of us,
make() is a needless complication in the language.
There are plenty of sycophants on this list who will say it works and
it's "not that bad" and etc., but the number of things in the language
that garner that reaction are quite numerous... in aggregate, it seems
difficult to justify their existence.
> There are plenty of sycophants on this list who will say it works and
> it's "not that bad" and etc., but the number of things in the language
> that garner that reaction are quite numerous... in aggregate, it seems
> difficult to justify their existence.
what else are we talking about?
russ
I don't know what else we /are/ talking about, but Peter
/might/ have been alluding to absence of conditional
expressions, the capitalisation rule for exports, the
semicolon rule & its consequences, the restrictions on
the types of map keys, the absence of generic types,
or the absence of dynamic code loading. All of which seem
to be speed bumps for at least some people.
I'd rather he'd been explict rather than just saying "a number
of things" (0? 1? 2? i?) and hadn't IMAO unnecessarily turned
up the emotional temperature with "sycophants".
Chris
--
Chris "allusive" Dollin
Again, I understand what you're trying to say, but it's still reading
as post-hoc justification for the status quo. Why do we need a special
function to create & initialize channels, maps and slices? Why can't I
just _create_ them, same as any user-defined struct or primitive type?
I'm sympathetic to wanting to keep the compiler and toolchain simple,
but only to a point; only so long as it's a cost-benefit analysis, and
not simply deferring to one side consistently.
>> There are plenty of sycophants on this list who will say it works and
>> it's "not that bad" and etc., but the number of things in the language
>> that garner that reaction are quite numerous... in aggregate, it seems
>> difficult to justify their existence.
>
> what else are we talking about?
As Chris notes, my apologies for politicizing things with loaded
language. It wasn't my intention. And to be clear, I haven't given
this enough deep thought to really form a persuasive argument. But as
time goes on, and as I read the list and coagulate a "sense" of the
language in my head, I perceive a number of things that I consider
important get casually and continuously brushed to the side as either
working fine as-is or not important. Chris hits on some major ones, to
which I will add: the often confusing semantics surrounding
interface{}; that parallelization via GOMAXPROCS isn't even close to
as fast as it should be at this stage; that the GC is still apparently
MIA; a general reliance on copy-and-paste helper functions (for
example, why a string can't automatically satisfy a ReadClose); and a
number of package-specific gripes, notably that a number of user
requests for the JSON and HTTP packages have gone ignored for almost
the lifetime of the language.
I don't know what you mean by create.
You _can_ just create them: just call make.
What code would you like to write instead?
var ch chan int
? How does that work? What if it's in a new (zeroed) struct?
We thought about how to do this for a while
and don't see a way to do it. Do you?
> language in my head, I perceive a number of things that I consider
> important get casually and continuously brushed to the side as either
> working fine as-is or not important.
Most of your list are things we've admitted aren't working fine
and are important, but we are a small team with limited resources.
If any of these is a significant pain point for you, one option is
to roll up your sleeves and write some code.
Hitting a few, reordered:
> Chris hits on some major ones, to
> which I will add: the often confusing semantics surrounding
> interface{};
I don't know exactly what this means - probably the difference
between storing *T vs T in an interface - but interfaces are a new
concept. I don't think it's surprising or even problematic that
people coming from other languages would find certain aspects confusing,
as long as they make sense once explained.
> a general reliance on copy-and-paste helper functions (for
> example, why a string can't automatically satisfy a ReadCloser);
Because string doesn't have Read and Close methods.
This is the only design issue you listed, and it's simply not that
kind of language. Go aims to be small, not syntax-sugary sweet.
> that parallelization via GOMAXPROCS isn't even close to
> as fast as it should be at this stage; that the GC is still apparently
> MIA; and a
> number of package-specific gripes, notably that a number of user
> requests for the JSON and HTTP packages have gone ignored for almost
> the lifetime of the language.
All but one of these are things we know about and are going to
be addressed in time (see comment above about resources).
The exception is JSON: I don't know what you mean.
I think package json is in good shape and don't know about any
ignored user requests there. Search on the issue tracker
in open issues for json only turns up issues related to the
template package (because it was inspired by a template
package named json-template). Details?
Russ
> Again, I understand what you're trying to say, but it's still reading
> as post-hoc justification for the status quo. Why do we need a special
> function to create & initialize channels, maps and slices? Why can't I
> just _create_ them, same as any user-defined struct or primitive type?
Like this?
x := map[string]int{"hello": 1, "world": 16}
y := []int{1, 1, 2, 3, 5, 8}
However, that syntactic form doesn't help if you want to
specify a slice of type T, length L, and capacity C; or a map
sized to hold N elements "well". make does that. The functionality
/could/ be snuck into the value constructors with some special
extra rules, but then that would just be moving the bump in the
carpet around.
make does something that the existing value constructors don't
do but that one does want to do.
There's a case for
chan whatever{buffer size}
but I'm not sure that's any better.
> ... the often confusing semantics surrounding
> interface{};
?Unpack?
> that the GC is still apparently MIA;
I thought the was a GC implementation but that it wasn't
very fancy yet.
> new(T) allocates.
> make(T) allocates and constructs built-ins.
I think there is good reason to question why we have both new and &T{}.
I think there is good reason to question why we have a make function
that works with slices and maps, and T{}.
However, there is a clear difference between new and make. You can not
construct a new channel with new. You can only construct a pointer to a
nil channel. The only way to construct a new channel is with make. We
can't write make in terms of new and we can't write new in terms of
make. They do not duplicate each other.
And the only way to construct a channel is to use make. Recall that
declaring a new variable of channel type gives you a nil channel.
If we want to eliminate make, then we have to to permit the T{} syntax
to work with channels. c := chan int{}. It's a little weird, but we
could probably do it.
If we want to eliminate new, I don't think it's reasonable to require
people to name a variable and then takes its address. Therefore, we
would have to extend the &T{} syntax to permit any type, not just
composite types. That is, we would have to permit &int{}, and we would
have to permit &chan int{}. And since the latter would presumably
actually make a channel, there would then be no way to get a pointer to
a nil channel without giving it a name.
In short, when you look at all the details, the functions are not quite
as much duplicates as they first appear. There is probably some way to
simplify the language here. But it's not a straightforward change.
I would be happy to see complete suggestions.
Ian
that's quite hard to do in general.
i think you're talking about allowing something like this:
type Copier interface { Copy() Copier }
type Foo struct { x int }
func (f *Foo) Copy() *Foo { return &Foo{f.x} }
var c Copier = &Foo{}
the main difficulty is that, unlike some other
languages, all types are not the same size in Go,
so a function returning a word sized value (Foo.Copy)
cannot stand in for a function returning a double-word sized
value (Copier.Copy).
the above example isn't too hard - the conversion could
insert a shim function for Copy that turns the returned
*Foo value into a Copier interface - but why should
a singleton return value be special? surely this should
be allowed too:
type MultiCopier interface { MultiCopy(n int) []MultiCopier }
func (f *Foo) MultiCopy(n int) []*Foo {
r := make([]*Foo, n)
for i := range r {
r[i] = f.Copy()
}
return r
}
if *Foo is to satisfy the MultiCopier interface, then the
[]*Foo value has to look like []MultiCopy to the caller
of MultiCopy. there's no way of doing that without
copying the slice, and that is not only inefficient,
if breaks the reference semantics of the slice.
return &Foo{f.x} }
a slice of MultiCopier values does
type Copier interface { Copy() Copier }
is fine, with no x.(Copier) necessary.
the Foo method would have to return Copier
rather than *Foo however.
> As for the slice thingy, I would think it's no different, since I can easily
> turn it into an interface{} value.
> type MultiCopier interface { MultiCopy(int) []interface{} }
> func (f *Foo) MultiCopy(n int) []interface{} {
> r := make([]interface{}, n)
> for i := range r {
> r[i] = f.Copy()
> }
> return r
> }
> However, that's ugly, and again, my code would be riddled with type
> assertions after every Copy() or MultiCopy(). So, in skipping that, just
> type assert the *Foo to MultiCopier. If it comes back true, *Foo =
> MultiCopier, and can then skip the type assertions and convert easily.
> As for reference semantic, either []*Foo satisfies []MultiCopier, or []Foo
> satisfies []MultiCopier, since those two types are supposedly separate.
i'm not quite sure what you mean here.
neither of those two assertions are necessarily true. one
crucial attribute of a slice is its element size, and *Foo,
MultiCopier and Foo may all have different sizes.
that means that a copy *must* be made, because the
caller always expects a slice of MultiCopier values
(double-word sized) to be returned. not to mention
the fact that an interface value holds its type as well
as the value itself.
if you make a copy, then you break the reference
semantics.
> If the above examples
> could be built just using things that satisfy the interfaces, rather than
> having to return interface values, you could separate all links to
> interfaces from the things that implement them, without the ugliness and
> unsafeness of interface{}.
as i pointed out above, you don't have to return interface{},
you can return the particular interface type instead.
but that does mean that the object implementing the interface
must be aware that it is implementing that interface.
i agree with that, but i don't see a solution.
> I see
> the point about the slice now, though it could be conceivable to avoid the
> copying overhead in enough of the instances that I would prefer to see it
> implemented rather than not.
AFAICS the only instance where you could avoid the copying overhead
is when the result is []interface{} and the original type is a slice
of a pointer type.
that's really not worth doing, surely.
But make() implies - both because of its use with channels/slices and
its name - that it does some kind of additional processing to ensure
that the returned object makes sense. I would like make() be
'overloadable' in a way, so that make(*T,...) called make_T (...) if
it existed, and otherwise new(T). (And then new() would probably fade
out by convention.)
--
Scott Lawrence