In praise of Go or : "Why I moved from Python and C++ to Go"

1,889 views
Skip to first unread message

Serge Hulne

unread,
Nov 16, 2010, 9:03:11 PM11/16/10
to golang-nuts
Dear members of the golang commmunity,


I wanted to take the opportunity of the first anniversary of the Go
programming language to recapitulate why I decided to move from Pyhon
and C++ to Go.

Here is an excerpt from the list of reasons I moved fro Python to Go:

- It is a small language: it fits in memory (even mine).

- It is only superficially object-oriented (no classes, no
inheritance, just interfaces) so it is more transparent.

- It is statically typed, so it is more explicit and the code of a
third party is more readable. Also it eliminates the risk of unwanted
automatic type conversions (unlike Python).

- It is compiled into machine code (no interpreter, unlike Python).

- The documentation is very good and can be consulted instantly form
the command line or from a browser, indifferently.

- There is an excellent plugin for the Vi editor, so that coding Go
with Vi is extremely easy (there is even a plugin which performs code
auto-completion).

- The language uses C-like curly braces: So the danger to paste stuff
at the wrong indent level is far less than in Python.

- It compiles extremely fast (much faster than C++ or Java or C,
actually it compiles virtually instantly).

- Concurrent programming is done with coroutines which communicate
trough channels (far easier to code than threads).

- Unlike C or C++, it is truly modular like Python or Java : No header
files.

- It has built-in support for mutli-core architectures (not yet very
useful, in practice).

- It is "garbage-collected" (which is uncommon for a compiled
language: The only other example is "D") : So there is no room for
memory leaks.

- Go uses pointers (as in C, using the same operators : & and *). So
there is no unwanted duplication of variables (in function calls,
assignments etc ...) and you can do all the usual indirection you
would also be able to do in C.

- The developers and the community are very helpful.

- It is easy to install from source (unlike "D").

- It is very actively maintained (unlike "D").

- The support is very dynamic.


Serge.

Geoff

unread,
Nov 16, 2010, 10:41:49 PM11/16/10
to golang-nuts
Objective-C on mac is another garbage collected language actually.

Also I'm surprised you say that you don't get much utility from 'go'
and channels

dipen chaudhary

unread,
Nov 16, 2010, 11:11:15 PM11/16/10
to golang-nuts
Hi Serge,

Thanks for your detailed post on what you like about go, would be
great if you can extend your post with what kind of application
development is more suited in go? I am an enthusiast of the principles
go is built on but have not used it. Would also be great if you can
share a little on what have you been building with go to augment my
above question.

Cheers

ch

unread,
Nov 16, 2010, 11:40:46 PM11/16/10
to golang-nuts

Serge Hulne wrote:

> - It is "garbage-collected" (which is uncommon for a compiled
> language: The only other example is "D") :

You are misinformed; There have been many (very many) language
implementations with native compilation and garbage collection.

> So there is no room for memory leaks.

I'm a big fan of garbage collected languages, but note that you can
unintentionally keep stuff you shouldn't even if you have a GC.

-ch

David Leimbach

unread,
Nov 17, 2010, 1:36:54 AM11/17/10
to golang-nuts
It is very easy and common to have space leaks in Haskell for example.

>
> -ch

Johann Höchtl

unread,
Nov 17, 2010, 2:37:08 AM11/17/10
to golang-nuts
W
This is rather an effect of lazy evaluation rather than an weak
garbage collector. The dons have plenty of advice how to avoid those.
But it's OT in here.
>
>
> > -ch

Serge Hulne

unread,
Nov 17, 2010, 3:45:59 AM11/17/10
to golang-nuts
My mistake,

I should have pinpointed more precisely situtations where channels and
goroutines proved less helpful than I naively thought initially,
namely:

- In relation to algorithmics, some expressions can be coded more
elegantly using goroutines than without goroutines.
- However, even if goroutines provide a more elegant formulation for,
say Eratosthene's sieve or the sequence of Fibonacci, the performance
of the algorithm with goroutine is equal or less than the perfomrance
of the corresponding algorithms without gouroutine (unless one uses
machines with a very large amount of cores and ones uses very large
numbers as arguments in said algorithms).
- Of course, one could point out that this is not the initial aim of
gouroutines and that they are great at handling concurrent requests in
a web server application, for instance.

- "Objective-C is garbage-collected": OK, I didn't know that (I had a
look at objective C a long time ago and it put me off for subjective
reasons: I thought it was a rather odd construct).

Serge.

Serge Hulne

unread,
Nov 17, 2010, 3:49:48 AM11/17/10
to golang-nuts
Excepted for Objective-C, did they ever make it to the top 10 of the
Tiobe index (or into the Tiobe index at all ) ?

Serge

> You are misinformed;  There have been many (very many) language
> implementations with native compilation and garbage collection.
> -ch

Paulo Pinto

unread,
Nov 17, 2010, 3:58:22 AM11/17/10
to golang-nuts
Objective-C -> place 8
Go -> place 25

http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

As for having GC and being compiled. Almost every language with GC
have a compiler version of it.

People should have by now learned that compilers/interpreters !=
language.

Serge Hulne

unread,
Nov 17, 2010, 4:08:31 AM11/17/10
to golang-nuts
Hi Dipen,

1. Go appears well suited for web developpement (see web.go on
github).
Since goroutines are easier to handle than Pythons' approach to
threads, I think that it is easier to build a web application or a web
development framework in Go than in Python.

2. Another example is NLP : natural language processing.
I made simple tests of Go vs Python for basic tasks like :
- wordcount (wc)
- Extracting bigrams (or more generally N-grams) from large corpora
(some examples can be found in my earlier posts).

I find the corresponding code clearer in Go than in Python (more
explicit, less magic, explicit use of pointers instead of implicit use
of references, etc ...).
Besides theres is also a performance improvement:
- Less memory use thanks to pointers (no unnecessary duplication of
data via "copy constructors")
- Better runtime performance.

Cheers,
Serge.

Serge Hulne

unread,
Nov 17, 2010, 6:46:42 AM11/17/10
to golang-nuts
By compiled, I mean compiled into a binary executable (not compiled
into bytecode, since bytecode still needs an interpreter, resp. a
"virtual machine" to run on).

With said definition of "compiled", there are not so many languages in
which the source code is both actually compiled into a binary
executable *and* garbage collected.

Granted, there are toy compilers for Python (shedskin) and for Java
("gcj", under Linux), yet, however laudable the efforts on the part of
the respective developers, these compilers can only ever handle a
small subset of an old version of the language (with a lot of
restrictions compared to the interpreted version), besides their
performance is atrocious.

Conversely, there are toy interpreters for languages which were
initially conceived to be compiled, like "tinyc" for C, or
"root" (from the CERN) for C++, but again these are mere academic
experiments and are of little use for building an industrial grade
application.

As far as Objective-C is concerned, the very recent regain in interest
for that language merely reflects the recent success of Apple, the
iPhone, iPad, etc... Developers are not really convinced that it is a
handy language, but it is the language of choice to develop gadgets
for the Mac platform.
In other words, "Objective-C" is to Mac what "Visual C++" (or Visual
Basic) is to Windows.
As a matter of fact, Objective-C was totally ignored for the last 20
years and only took off in the middle of last year (Objective-C is not
a new language, it is rather an artificially resurrected dinosaur).
Before 2010, the amount of code written in objective C was next to
naught, yet it had been around for about 20 years (see the green curve
on the web page of the TIOBE index).

Serge

Sebastien Binet

unread,
Nov 17, 2010, 7:10:35 AM11/17/10
to Serge Hulne, golang-nuts
On Wed, 17 Nov 2010 03:46:42 -0800 (PST), Serge Hulne <serge...@gmail.com> wrote:
> Conversely, there are toy interpreters for languages which were
> initially conceived to be compiled, like "tinyc" for C, or
> "root" (from the CERN) for C++, but again these are mere academic
> experiments and are of little use for building an industrial grade
> application.

OT, but "ROOT" isn't a C++ interpreter. CINT (which is indeed part of
ROOT) is - or at least it is meant to be.
mind you, it started as a C -not C++- interpreter on top of which
C++ was bolted on. I let you imagine the "interesting" side effects of
such an approach...

cheers,
sebastien.

Geoffrey Teale

unread,
Nov 17, 2010, 7:23:33 AM11/17/10
to Serge Hulne, golang-nuts

Lisp sits at 13 in the index today and their are definitely compiled
versions of that language with GC - if you can think of a way to
implement a programming language someone has probably implemented a
Lisp that way ;-)

--
Geoff Teale

Paulo Pinto

unread,
Nov 17, 2010, 9:20:34 AM11/17/10
to golang-nuts
Ok, lets than list some languages with both compiler and interpreters
available:

Compiled Pascal - GNU Pascal, Delphi, Turbo Pascal, Free Pascal
Interpreted Pascal - Pascal Script

Compiled Java - GCJ, Excelsior JET
Interpreted/JIT Java- JDK

Compiled .Net - Mono AOT, NGEN
JIT .NET (.Net never interprets code) - Mono, .Net

Compiled Oberon - Oberon (it has GC)
Interpreted Oberon - Thesis from Günter Obiltschnig

Compiled D - DMD, GDC, LLVM D
Interpreted D - DMD Script

Lisp, Scheme, Haskell, ML all have GC and provide compiled and
interpreter versions.

It is always an implementation issue, and not having a mainstream
compiler
does not mean a language is not compiled into native code, just that
the community
probably sees little value in having one. And vice-versa.

It is always a case of return on investment, as you can see from the
available list at Wikipedia
http://en.wikipedia.org/wiki/List_of_programming_languages_by_category

--
Paulo

Serge Hulne

unread,
Nov 17, 2010, 10:12:03 AM11/17/10
to golang-nuts
:)

S.


On Nov 17, 3:20 pm, Paulo Pinto <paulo.jpi...@gmail.com> wrote:
> Ok, lets than list some languages with both compiler and interpreters
> available:
>
> Compiled Pascal - GNU Pascal, Delphi, Turbo Pascal, Free Pascal
> Interpreted Pascal - Pascal Script
>
> Compiled Java - GCJ, Excelsior JET
> Interpreted/JIT Java- JDK
>
> Compiled .Net - Mono AOT, NGEN
> JIT .NET (.Net never interprets code) - Mono, .Net
>
> Compiled Oberon - Oberon (it has GC)
> Interpreted Oberon - Thesis from Günter Obiltschnig
>
> Compiled D - DMD, GDC, LLVM D
> Interpreted D - DMD Script
>
> Lisp, Scheme, Haskell, ML all have GC and provide compiled and
> interpreter versions.
>
> It is always an implementation issue, and not having a mainstream
> compiler
> does not mean a language is not compiled into native code, just that
> the community
> probably sees little value in having one. And vice-versa.
>
> It is always a case of return on investment, as you can see from the
> available list at Wikipediahttp://en.wikipedia.org/wiki/List_of_programming_languages_by_category

Andrew Francis

unread,
Nov 17, 2010, 11:54:21 PM11/17/10
to golang-nuts
Hi Serge:

On Nov 17, 4:08 am, Serge Hulne <serge.hu...@gmail.com> wrote:

> Since goroutines are easier to handle than Pythons' approach to
> threads, I think that it is easier to build a web application or a web
> development framework in Go than in Python.

Stackless Python uses a very similar concurrency model to Go
(tasklets, synchronous channels based on rendez-vous semantics). Eve-
online uses Stackless Python. It is because of the shared heritage
between Go and Stackles (Limbo) that I read the Golang nuts mailing
list and dabble with Go. That said, Stackless Python lacked a select
statement and a friend and I added that a few months ago. Now I am
experimenting with join patterns. Chances are if I can implement join
patterns in Stackless, it can be done in Go.

Cheers,
Andrew

Renz

unread,
Nov 18, 2010, 12:02:43 AM11/18/10
to golang-nuts
Hi Serge,

I've been lurking in this mailing list for several months now but your
statement below caught my attention.

My last play with pointers is my college days 11 years ago, and been
programming business apps with Java/Python/Ruby since I graduated. I
love the philosophy behind golang and waiting for it to go mainstream
and be adopted in my company. But it's baffling to me that Golang is
not designed with implicit use of references as other languages are. I
admire Golang and the people behind it and it follows that I should
also appreciate the use of pointers versus the pass-by-reference that
I'm used to.

My experience and knowledge with languages is VERY limited so what's
baffling to me is surely explainable by someone knowledgeable and
enlightened. And since you mentioned this, maybe you could give me
some insights and educate me why you said "explicit use of pointers
instead of implicit use of references" is clearer.

Thanks.


On Nov 17, 5:08 pm, Serge Hulne <serge.hu...@gmail.com> wrote:
> ...
> I find the corresponding code clearer in Go than in Python (more
> explicit, less magic, explicit use of pointers instead of implicit use
> of references, etc ...).
> ...

Steven

unread,
Nov 18, 2010, 12:26:14 AM11/18/10
to Renz, golang-nuts
On Thu, Nov 18, 2010 at 12:02 AM, Renz <ghostf...@gmail.com> wrote:
Hi Serge,

I've been lurking in this mailing list for several months now but your
statement below caught my attention.

My last play with pointers is my college days 11 years ago, and been
programming business apps with Java/Python/Ruby since I graduated. I
love the philosophy behind golang and waiting for it to go mainstream
and be adopted in my company. But it's baffling to me that Golang is
not designed with implicit use of references as other languages are. I
admire Golang and the people behind it and it follows that I should
also appreciate the use of pointers versus the pass-by-reference that
I'm used to.

My experience and knowledge with languages is VERY limited so what's
baffling to me is surely explainable by someone knowledgeable and
enlightened. And since you mentioned this, maybe you could give me
some insights and educate me why you said "explicit use of pointers
instead of implicit use of references" is clearer.

Thanks.

For me, pointers allow you to specify whether you're manipulating the reference or the value it references. Implicit references don't give you this ability. There is a clear semantic distinction to be made which pointers allow, while implicit references do not. Also, it allows for uninitialized references and a simple way to check for this case (ptr == nil), which is very useful in many cases. Otherwise, Go pointers aren't all that different from references in other languages (no pointer arithmetic or pointer polymorphism).

Serge Hulne

unread,
Nov 18, 2010, 2:30:36 AM11/18/10
to golang-nuts
Hi Renz,

1. Perhaps the most straightford way to look at the difference between
the implicit use of references and the explicit use of addresses or
pointers is to look at a few examples.

2. One thing to consider is that the difference is not a theoretical
one, but a practical one.

3. To give a first example: high-level, object-oriented languages like
Python or C++ use, among other standard data structures, containers.
Let's focus on the C++ maps for instance (or the Python dictionaries):

Since, by essence, these two "containers" are meant to store (key,
value) pairs and since the source from which the corresponding data is
obtained can be temporary (network, user input, ...), these containers
will automatically (by design) store *copies* and not *adresses* (or
pointers or references) of the original data which is fed to said
containers.

More precisely in Python:

a = "hello"
b = a

does not lead to a duplication of the data stored in a, b merely
points to the same string. a and b are "alias"

This is the same as :
char a[] = "hello";
char * b = &a;
or
char * b = &a[0];
i.e. b is a pointer which holds the address of (the fisrt element of)
array a

So far, so good: There is a rule in Pyhon which says that affectation
between "simple" (non composite, as opposed to classes) variables will
result in passing by address and therefore avoid an unnecessary copy.

There are however two situations where this all goes horribly wrong,
namely : When affecting composite variables or when storing (even
"simple" variables in containers):

- First situation:
===========
A is a Python classe, a and b two instances:
a = A()
b = a

a = b will result in a *copy* of the data of a into b (same story as
the "copy constructor" in C++)

- Second situation:
==============
D is a Python dictionary
D[key] = value
results in the pair (key, value) being copied (and not referenced)
into D.

All that does not seem too bad at first glance, but if you realise
that the two scenarios above might happen in the analysis of a very
large corpora (body of text), as is regularly the case in NLP, then
you might end up with very large lists or maps or "maps of maps" or
"lists or maps" holding *copies* of the (very voluminous) original
data contained in the original text.

In other words:

- With Go and C, you can tell by simply looking at the code, how much
ram will be needed to analyse a large body of text (or a large set of
data more genetally).
- With The STL maps and other containers of C++ it is a bit more
tricky because you must be sure of how exacty the container (on one
hand) and the copy contructor of the object to be stored will
interact: Will storage resutlt in a copy or not ?.
- With Python, it is next to impossible to guess by a mere look at the
code, how much ram will be needed to process a large amount of data
when lists, dictionaries, etc ... are involved.

Of course the same applied to Java, for the same basic reason:
Containers store copies, not references.
This is one of the reasons why Java programs consume much more ram
than C or C++ or Go programs.

Sincerely,
Serge.

Florian

unread,
Nov 18, 2010, 2:38:26 PM11/18/10
to golang-nuts
> - First situation:
> ===========
> A is a Python classe, a and b two instances:
> a = A()
> b = a
>
> a = b will result in a *copy* of the data of a into b (same story as
> the "copy constructor" in C++)

Not in my version of Python. Or any other... (provided the second line
in your example is meant to read "b = A()"; otherwise it's incomplete)

>>> class A(object):
... pass
...
>>> a = A()
>>> b = A()
>>> print a
<__main__.A object at 0x7fc7e7df7090>
>>> print b
<__main__.A object at 0x7fc7e7df70d0>
>>> b = a
>>> print b
<__main__.A object at 0x7fc7e7df7090>

> - Second situation:
> ==============
> D is a Python dictionary
> D[key] = value
> results in the pair (key, value) being copied (and not referenced)
> into D.

>>> class A(object):
... pass
...
>>> a = A()
>>> b = A()
>>> d = {}
>>> d[a] = b
>>> print d
{<__main__.A object at 0x7f95f4014090>: <__main__.A object at
0x7f95f40140d0>}
>>> print a
<__main__.A object at 0x7f95f4014090>
>>> print b
<__main__.A object at 0x7f95f40140d0>

No copies to be seen there.

Variables are names referencing objects and the assignment operator
changes which object is reference by the name. That is true for any
type of object in Python. Copies need to be created explicitly.

Gustavo Niemeyer

unread,
Nov 18, 2010, 3:30:46 PM11/18/10
to Serge Hulne, golang-nuts
> 1. Perhaps the most straightford way to look at the difference between
> the implicit use of references and the explicit use of addresses or
> pointers is to look at a few examples.
(...)

> So far, so good: There is a rule in Pyhon which says that affectation
> between "simple" (non composite, as opposed to classes) variables will
> result in passing by address and therefore avoid an unnecessary copy.
(...)

> a = b will result in a *copy* of the data of a into b (same story as
> the "copy constructor" in C++)
(...)

> D[key] = value
> results in the pair (key, value) being copied (and not referenced)
> into D.

An honest suggestion: stop talking publicly about language comparisons
and offering recommendations while you have no idea about how they
work whatsoever. Instead, spend that time actually using and learning
about them.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter

Serge Hulne

unread,
Nov 18, 2010, 5:15:59 PM11/18/10
to golang-nuts
OK, my mistake !

Serge.

Renz

unread,
Nov 19, 2010, 5:48:23 AM11/19/10
to golang-nuts
Hi,

Irregardless of whether your view of how other languages references
works is correct or not, it seems you didn't get my question.

My understanding (not sure if correct) is that Go's pointer is no
different than how references in Java works. The only difference is
that in Java, non-native types are always *implicitly* passed by
reference (you're not passing to a method or function a copy/value of
the object but only the reference).

So my question revolves around the usability of Go in a programmer's
perspective, not in the Golang's perspective. Why do you think it is
clearer, or as you say practical, that Golang does not have this
*implicit* pass-by-pointer as Java's implicit pass-by-reference for
non-native types? Anyway, isn't it discouraged in Go to pass object or
struct value to a function especially if the struct is big, and that
programmers and API designers would most likely require a pointer
argument or return a pointer? So, bottom line, my question is actually
about the *implicitness* for usability's sake.

I know there must be a justification for this Golang way but I'm not
sure what that is. I'm not a straightforward speaker but I hope I'm
clear. :-D

Inputs from others are welcome. Cheers!

Renz

unread,
Nov 19, 2010, 5:48:55 AM11/19/10
to golang-nuts
Hi, thanks for input. Appreciated.

On Nov 18, 1:26 pm, Steven <steven...@gmail.com> wrote:

Cory Mainwaring

unread,
Nov 19, 2010, 6:06:48 AM11/19/10
to Renz, golang-nuts
Go doesn't have anything like that. Go has "pass by definition". If a
variable is defined by a value, then Go sends the value around. If
that variable is a pointer, then Go sends the pointer around. The
default pass-by is whatever the data is. You can make a value send a
reference or a pointer send a value if you want to via the "&" and "*"
syntactic markings, but the default is to send the data, plain and
simple.

This of course makes your life as a programmer far easier. You don't
have to remember what types are passed by reference, and what types
are passed by value. You don't have to find performance bottlenecks
that don't make sense (such as a user-defined integer being passed
around by reference, and constantly getting dereferenced). You don't
have to remember to indicate to send it by value, rather than
reference. You just send it. If you want to change the variable
directly, you send a reference. If you want to send it into the black
box, and get it transformed on it's way out, you send a value.

Also, all of this "pass-by" stuff is a bad paradigm. Programs have
pointers and values. The data is a pointer or value. If I have a
value, and I'm sending that value to a function, I want the value to
get there. If I have a pointer, and I'm sending it to a function, then
I still want the pointer to get there. I don't want anything getting
sullied up by the language between my variables and my functions,
except me.

In short: Go sends data, Java throws poop

chris dollin

unread,
Nov 19, 2010, 6:08:30 AM11/19/10
to Renz, golang-nuts
On 19 November 2010 10:48, Renz <ghostf...@gmail.com> wrote:
> Hi,
>
> Irregardless of whether your view of how other languages references
> works is correct or not, it seems you didn't get my question.
>
> My understanding (not sure if correct) is that Go's pointer is no
> different than how references in Java works. The only difference is
> that in Java, non-native types are always *implicitly* passed by
> reference (you're not passing to a method or function a copy/value of
> the object but only the reference).

In Java, non-native types are only represented by references, but
those references are always passed around by value. (There is a
specific computer-science meaning for "passed by reference" and
neither Java nor Go has it -- but C++ does.)

Go's pointers are rather like Java's implicit references. But Go
/also/ allows you to pass the actual values of structs (etc) around
as well as pointers to them. Once you can do that, trying to make
get-reference-to and de-reference implicit is tricky and probably
not worth it. (It can be done -- Algol 68 does it -- but the cost is that
it can be hard to keep in your head where theings are pointers and
where they're not. I Have Been Bitten.)

> Anyway, isn't it discouraged in Go to pass object or
> struct value to a function

No.

> especially if the struct is big,

If the struct is "big enough" there may be a performance optimisation
by passing a reference to it instead. Or it may be a performance
/pessimisation/ -- eg passing a reference means that it's pretty
certain (with today's Go compilers) that the struct will be heap-
allocated, and hence produce more GC load, while if it were passed
by value it might get away with stack allocation.

Measurement beats speculation.

> and that
> programmers and API designers would most likely require a pointer
> argument or return a pointer?

They might, or they might not. It depends.

> So, bottom line, my question is actually
> about the *implicitness* for usability's sake.

I don't see the explicitness of Go's pointers as presenting a
usability problem.

Chris

--
Chris "allusive" Dollin

chris dollin

unread,
Nov 19, 2010, 6:18:31 AM11/19/10
to Cory Mainwaring, Renz, golang-nuts
On 19 November 2010 11:06, Cory Mainwaring <olr...@gmail.com> wrote:

> In short: Go sends data, Java throws poop

Omit needless words.

Bill Burdick

unread,
Nov 19, 2010, 9:33:10 AM11/19/10
to Renz, golang-nuts
My understanding is that the Go authors were shooting for a good system language, which would allow precise control of memory layout, like C does.  By including implicit conversion between structs and pointers, they made Go able to behave a lot like languages that only support reference types (LISP, Java, etc.)

So my advice is that if you want a simple way to make Go behave roughly like Java, LISP, et al, when you make a new type, you can make it a struct and always use a pointer to it; i.e. base your methods on pointers to the type and make sure that methods and functions which return values of the new type always return pointers.  here are finer points of initialization when embedding structs vs pointers, etc. but you can just use pointers, initialize them explicitly, and worry about all that later.

One interesting way that Go departs from Java is that you can call methods on a nil receiver -- a method can check its own receiver for nil.  I think this can save a lot of code in the long run, in cases when nil is considered a legal value, because the nil check can be localized in the method instead of required at every call site.


Bill

bflm

unread,
Nov 19, 2010, 10:37:27 AM11/19/10
to golang-nuts
On Nov 19, 3:33 pm, Bill Burdick <bill.burd...@gmail.com> wrote:
>  By including implicit conversion between structs and pointers, they made Go
> able to behave a lot like languages that only support reference types (LISP,
> Java, etc.)
I'm not sure if the above is a typo or misunderstanding. There's no
implicit conversion between structs and pointers in Go. Did you
mean(?):

"The method set of any other named type T consists of all methods with
receiver type T. The method set of the corresponding pointer type *T
is the set of all methods with receiver *T or T (that is, it also
contains the method set of T)."

http://golang.org/doc/go_spec.html#Types

or:

"Selectors automatically dereference pointers to structs."

http://golang.org/doc/go_spec.html#Selectors

or???

Bill Burdick

unread,
Nov 19, 2010, 12:10:25 PM11/19/10
to bflm, golang-nuts
This is getting way out of the scope of helping Renz, but I'll offer my opinion on this issue.

Yes, I'm referring to how selectors work, like in the example below.  The "." operator may pass the receiver as-is, dereference it, or take its address, depending on the actual type of the receiver and the declared type of the receiver.  I call that implicit conversion.  I don't have a problem with that, either -- I think I like it.  Here's that example:

package main
type S struct {i int}
func (s *S) hello() {println("i =", s.i)}
func (s S) duh() {println("duh")}
func main() {
        var s *S = &S{}
        s.hello()    // pass s as-is to hello
        s.duh()      // pass s dereferenced to duh
        (*s).hello() // pass *s's address to hello
        (*s).duh()   // pass *s as-is to duh
}

Bill

Bill Burdick

unread,
Nov 19, 2010, 12:14:56 PM11/19/10
to bflm, golang-nuts
In fact, the receiver is really the zeroth argument to the method's function; why not convert the rest of the arguments that way instead of just the zeroth?  Keeping track of whether to dereference or take an address is a pain sometimes.


Bill

chris dollin

unread,
Nov 19, 2010, 12:43:06 PM11/19/10
to Bill Burdick, bflm, golang-nuts
On 19 November 2010 17:14, Bill Burdick <bill.b...@gmail.com> wrote:
> In fact, the receiver is really the zeroth argument to the method's
> function; why not convert the rest of the arguments that way instead of just
> the zeroth?  Keeping track of whether to dereference or take an address is a
> pain sometimes.

?

I've never had a problem with it (except in a language which
did it implicitly ...).

Steven

unread,
Nov 19, 2010, 12:52:22 PM11/19/10
to Bill Burdick, bflm, golang-nuts
On Friday, November 19, 2010, Bill Burdick <bill.b...@gmail.com> wrote:
> In fact, the receiver is really the zeroth argument to the method's function; why not convert the rest of the arguments that way instead of just the zeroth?  Keeping track of whether to dereference or take an address is a pain sometimes.

To me this sounds like "keeping track of whether pants go on the top
part or the bottom part is a pain sometimes" as a justification for
making pants that automatically transform into a shirt when pulled
over your head.

Remembering you have/need a pointer is no more difficult than
remembering you have/need a slice. The only confusion can come from
intentionally ignoring this piece of information and instead thinking
in terms of the "pass by reference" vs "pass by value" dichotomy in
other languages.

David Leimbach

unread,
Nov 19, 2010, 2:05:39 PM11/19/10
to golang-nuts


On Nov 19, 2:48 am, Renz <ghostfigh...@gmail.com> wrote:
> Hi,
>
> Irregardless of whether your view of how other languages references
> works is correct or not, it seems you didn't get my question.
>
> My understanding (not sure if correct) is that Go's pointer is no
> different than how references in Java works. The only difference is
> that in Java, non-native types are always *implicitly* passed by
> reference (you're not passing to a method or function a copy/value of
> the object but only the reference).
>

That's not quite right, Java always passes everything by value.

You just never have anything but references to objects to work with,
so the reference is passed by value and it's a copy of that reference
received at the method being called.

This is why it's impossible to write a utility function that swaps two
objects in Java.

Try it:

public class testswap {
public static void swap(Integer o1, Integer o2) {
System.out.printf("arg 1: %d arg 2: %d\n", o1, o2);
Integer t = o2;
o2 = o1;
o1 = t;
System.out.printf("arg 1: %d arg 2: %d\n", o1, o2); // here it's
swapped.
}

public static void main (String [] args) {
Integer i = 999;
Integer j = 1000;
swap(i, j);

// The swap fails because we got copies of the references to swap, not
the original reference.
System.out.println("i is " + i);
System.out.println("j is " + j);

}
}

> So my question revolves around the usability of Go in a programmer's
> perspective, not in the Golang's perspective. Why do you think it is
> clearer, or as you say practical, that Golang does not have this
> *implicit* pass-by-pointer as Java's implicit pass-by-reference for
> non-native types? Anyway, isn't it discouraged in Go to pass object or
> struct value to a function especially if the struct is big, and that
> programmers and API designers would most likely require a pointer
> argument or return a pointer? So, bottom line, my question is actually
> about the *implicitness* for usability's sake.
>

Implicit stuff better be for usability's sake, or it's not worth
doing, and will just lead to confusion. I do not understand the
premise of your argument.

In Java you always have a reference to an object, never a value. new
returns references, end of story.

In Java, methods get value copies of the thing being passed as an
argument. Since, for objects, that's a reference, you get a copy of a
reference at the method.

In Go, you can have functions in ADTs (or methods) that require a
pointer to a type, or a copy of the type, and they can be called via
objects that are either pointers to values of those types or values of
those types.

package main

import (
"fmt"
)

type mine struct {
i int
name string
}

func (m * mine) foo () {
fmt.Printf("In foo for: %v\n", m)
m.i++
fmt.Printf("I incremented the value in : %v\n", m)
}

func (m mine) bar () {
fmt.Printf("In bar for: %v\n", m)
m.i++
fmt.Printf("I incremented the value in : %v (which was a copy)\n", m)
}

func main () {
m1 := &mine{3, "m1"}
m2 := mine{4, "m2"}

fmt.Printf("m1 is a %v\n", m1)
fmt.Printf("m2 is a %v\n", m2)

m1.foo()
m1.bar()
m2.foo()
m2.bar()

fmt.Printf("m1 is now: %v\n", m1)
fmt.Printf("m2 is now: %v\n", m2)

}

All you have to remember as the programmer is that receiver method
gets to say if it's a pointer or not.

I'm not confused by this at all, but I think it's easy to be confused
by Java with respect to objects, references and value passing.


> I know there must be a justification for this Golang way but I'm not
> sure what that is. I'm not a straightforward speaker but I hope I'm
> clear.  :-D
>

This is in line with the philosophy of Go to make maintenance
programming less of a pain.

Say you change a method one day, and wanted it to do some modification
to the state of an object, do you *really* want to go back and correct
all the calls to that method to take a pointer, or do you want to
document, in one place, that now that method can modify the object
being passed, because it's no longer a copy of the object, but a copy
of the address.

It seems Go was interested in helping people maintain methods without
breaking all the consumers of an ADTs interface. Big win from my
perspective.

It also forces the users of an ADTs interface to keep in mind that any
method taking a pointer to a type COULD be messing with their data, so
if they want to give it a copy they better send one explicitly. There
is one consistent way to know that a method could mess with the
caller's data, and I really like that.

Dave

David Leimbach

unread,
Nov 19, 2010, 2:12:37 PM11/19/10
to golang-nuts


On Nov 19, 3:06 am, Cory Mainwaring <olre...@gmail.com> wrote:
> Go doesn't have anything like that. Go has "pass by definition". If a
> variable is defined by a value, then Go sends the value around. If
> that variable is a pointer, then Go sends the pointer around. The
> default pass-by is whatever the data is. You can make a value send a
> reference or a pointer send a value if you want to via the "&" and "*"
> syntactic markings, but the default is to send the data, plain and
> simple.

It's different for methods of ADTs. See my example from before.

I've updated it to include your point that general functions do behave
as you describe, but there is an implicit nature to methods on ADTs.

package main

import (
"fmt"
)

type mine struct {
i int
name string
}

func (m * mine) foo () {
fmt.Printf("In foo for: %v\n", m)
m.i++
fmt.Printf("I incremented the value in : %v\n", m)
}

func (m mine) bar () {
fmt.Printf("In bar for: %v\n", m)
m.i++
fmt.Printf("I incremented the value in : %v (which was a copy)\n", m)
}

func baz (m * mine) {
fmt.Printf("In baz for: %v\n", m)
m.i++
fmt.Printf("I incremented: %v\n", m)
}

func oopsy (m mine) {
fmt.Printf("In oopsy for: %v\n", m)
m.i++
fmt.Printf("I incremented: %v\n", m)
}

func main () {
m1 := &mine{3, "m1"}
m2 := mine{4, "m2"}

fmt.Printf("m1 is a %v\n", m1)
fmt.Printf("m2 is a %v\n", m2)

m1.foo()
m1.bar()
m2.foo()
m2.bar()

baz(m1)
baz(&m2)

oopsy(*m1)
oopsy(m2)

fmt.Printf("m1 is now: %v\n", m1)
fmt.Printf("m2 is now: %v\n", m2)

}


>
> This of course makes your life as a programmer far easier. You don't
> have to remember what types are passed by reference, and what types
> are passed by value. You don't have to find performance bottlenecks
> that don't make sense (such as a user-defined integer being passed
> around by reference, and constantly getting dereferenced). You don't
> have to remember to indicate to send it by value, rather than
> reference. You just send it. If you want to change the variable
> directly, you send a reference. If you want to send it into the black
> box, and get it transformed on it's way out, you send a value.

except in the case of ADT methods.

>
> Also, all of this "pass-by" stuff is a bad paradigm. Programs have
> pointers and values. The data is a pointer or value. If I have a
> value, and I'm sending that value to a function, I want the value to
> get there. If I have a pointer, and I'm sending it to a function, then
> I still want the pointer to get there. I don't want anything getting
> sullied up by the language between my variables and my functions,
> except me.

I think people need to understand the calling conventions of a
language, of which pass-by is an important concept.

>
> In short: Go sends data, Java throws poop
>

chris dollin

unread,
Nov 19, 2010, 2:15:35 PM11/19/10
to David Leimbach, golang-nuts
On 19 November 2010 19:05, David Leimbach <lei...@gmail.com> wrote:

> That's not quite right, Java always passes everything by value.
>
> You just never have anything but references to objects to work with,
> so the reference is passed by value and it's a copy of that reference
> received at the method being called.

Boxing.

--
Chris "allusive" Dollin

David Leimbach

unread,
Nov 19, 2010, 2:23:25 PM11/19/10
to golang-nuts


On Nov 19, 11:15 am, chris dollin <ehog.he...@googlemail.com> wrote:
> On 19 November 2010 19:05, David  Leimbach <leim...@gmail.com> wrote:
>
> > That's not quite right, Java always passes everything by value.
>
> > You just never have anything but references to objects to work with,
> > so the reference is passed by value and it's a copy of that reference
> > received at the method being called.
>
> Boxing.

If boxing occurs, you still get the same effect of passing a copy of a
reference to an object. Swap still can not be written :-)

Boxing of a sort occurs in C++ too, sometimes when you don't want it
to, hence the "explicit" keyword on single argument constructors.

Dave

>
> --
> Chris "allusive" Dollin

Bill Burdick

unread,
Nov 19, 2010, 4:33:35 PM11/19/10
to Steven, bflm, golang-nuts
It's the unnecessary punctuation that's really the painful part, not the keeping track.  Apparently the Go authors felt similarly about unnecessary punctuation for message sends.  Why not do it for all of the arguments instead of just the receiver?


Bill

Ian Lance Taylor

unread,
Nov 19, 2010, 4:45:37 PM11/19/10
to Bill Burdick, Steven, bflm, golang-nuts
Bill Burdick <bill.b...@gmail.com> writes:

> It's the unnecessary punctuation that's really the painful part, not the
> keeping track. Apparently the Go authors felt similarly about unnecessary
> punctuation for message sends. Why not do it for all of the arguments
> instead of just the receiver?

It's particularly bad for the receiver. Methods on pointer types are
very common. For a while we were writing
(&v).M()
for almost every method call. It was awkward. A method call is already
distinctive enough that it seemed OK for the language to take the
address for you, so we could just write
v.M()

Passing arguments to functions seems different, at least to me. The
difference is that if you pass v to a function, you know that it will
not change. If you pass &v, you know that it might change. With a
method call, whether the value changes or not is a property of the
variable's type. With a function, it's being decided in some other
package far away.

This is not a conclusive argument, it's just a tradeoff, like all other
language decisions.

Ian

Steven

unread,
Nov 19, 2010, 4:55:02 PM11/19/10
to Bill Burdick, bflm, golang-nuts

With the receiver, the added punctiation actually Is significant
because you have to use brackets. For the other arguments, all you
need is a single extra character at the beginning. It's not
comparable. With arguments, you have to be careful when it comes to
passing to an interface value. It's better to always need to be
explicit than to sometimes need to be explicit, especially when the
difference is one character. This problem doesn't exist for the
receiver.

Bill Burdick

unread,
Nov 20, 2010, 2:05:14 AM11/20/10
to Ian Lance Taylor, Steven, bflm, golang-nuts
I respect that languages have tradeoffs and these really get down to matters of taste.  If you say v.foo(), though, you don't know whether v will change, even if v is a struct.  Maybe that's easier to swallow with a method, since changing the receiver is "normal" for a method.  For me, this behavior is fine.  Maybe it would help if syntax highlighters rendered the "autoconversion" (when the language takes the address for you or dereferences for you) using italics or something like that.  This would mean that the use wouldn't have to refer back to the API to see whether the declared receiver's type is different from the actual receiver.

Along the same lines, though, I personally think that the "*" and "&" operators are a bit ritualistic -- there are no pointer or struct-specific operations; as far as I can tell, the "*" and "&" operators are only needed for assignment, parameter passing, and returning values (but not for calling a method).  "*" for declarations is fine, but can we move beyond the "*" and "&" operators in languages?  Go has already taken the first step with receivers.  I think you could use the same syntax highlighting to indicate when a * or & conversion happens in these cases.


Bill

bflm

unread,
Nov 20, 2010, 2:57:19 AM11/20/10
to golang-nuts
On Nov 20, 8:05 am, Bill Burdick <bill.burd...@gmail.com> wrote:
> I respect that languages have tradeoffs and these really get down to matters
> of taste.  If you say v.foo(), though, you don't know whether v will change,
> even if v is a struct.  Maybe that's easier to swallow with a method, since
> changing the receiver is "normal" for a method.  For me, this behavior is
> fine.  Maybe it would help if syntax highlighters rendered the
> "autoconversion" (when the language takes the address for you or
You're only doing it harder for yourself when you insist on thinking
in terms "autoconversion" or "implicit conversion between structs and
pointers" as these things really don't exists in Go or their perceived
existence is a result of semantically different constructs (as
mentioned yesterday).

> dereferences for you) using italics or something like that.  This would mean
> that the use wouldn't have to refer back to the API to see whether the
> declared receiver's type is different from the actual receiver.
>
> Along the same lines, though, I personally think that the "*" and "&"
> operators are a bit ritualistic -- there are no pointer or struct-specific
> operations; as far as I can tell, the "*" and "&" operators are only needed
> for assignment, parameter passing, and returning values (but not for calling
> a method).  "*" for declarations is fine, but can we move beyond the "*" and
> "&" operators in languages?
You can't write to much programs in a language with pointers without
the pointer operators.

> Go has already taken the first step with
> receivers.  I think you could use the same syntax highlighting to indicate
> when a * or & conversion happens in these cases.
This is an another example of you confusing yourself with the
"autoconversion". As soon as you stop convincing yourself of the
"autoconversion" and get used to pointers vs. values (it has nothing
to do with structs, any type can have a related pointer) then you will
see not only more, but the view of the Go language should be much
clearer.

My 2c.

Bill Burdick

unread,
Nov 20, 2010, 3:28:50 AM11/20/10
to bflm, golang-nuts
Please study my example above.  You may notice that the s variables in hello() and duh() are not always of the same type as the receiver at the call site.  Please teach me the difference between this an an implicit type conversion.

bflm

unread,
Nov 20, 2010, 3:56:39 AM11/20/10
to golang-nuts
On Nov 20, 9:28 am, Bill Burdick <bill.burd...@gmail.com> wrote:
> Please study my example above.  You may notice that the s variables in
> hello() and duh() are not always of the same type as the receiver at the
> call site.  Please teach me the difference between this an an implicit type
> conversion.
Implicit type conversion is this:
type T1 = X
type T2 = Y

func f(arg T1) {}

func main() {
var v T2
f(v)
}

When this would compile by means of the compiler figured how to
convert v (being T2) in main to arg (being T1) in f. Some languages do
this for e.g. X == float and Y == int and vice versa. Go doesn't allow
this.

More on type conversion in Go (completely unrelated to your "automatic
conversion"):

http://golang.org/doc/go_spec.html#Conversions

Note it's explicit only.

Bill Burdick

unread,
Nov 20, 2010, 4:56:24 AM11/20/10
to bflm, golang-nuts
So, is there explicit conversion in this case?

type T1 = X

func (arg *T1) f() {}

func main() {
   var w *T1 = &T1{}

   w.f()
}

T1 and *T1 are not type compatible.  You cannot say this, for instance:

var v T1
var w *T1 = &T1{}

v = w

Note that f can change the contents of arg, just like it could if it were declared as f(arg *T1).

Bill Burdick

unread,
Nov 20, 2010, 5:01:12 AM11/20/10
to bflm, golang-nuts
Sorry, I messed up that example.  Main should be:

func main() {
   var w T1

   w.f()

bflm

unread,
Nov 20, 2010, 6:07:43 AM11/20/10
to golang-nuts
On Nov 20, 10:56 am, Bill Burdick <bill.burd...@gmail.com> wrote:
> So, is there explicit conversion in this case?
Applying your next post patch, hope this is what it should be:

=========
type T1 X // not '= X', sorry, that was Pascal slipping through my
fingers accidentally

func (arg *T1) f() {}

func main() {
var w T1
w.f()
}
=========

There is no conversion of 'w' involved (using conversion as defined in
the specs: http://golang.org/doc/go_spec.html#Conversions ). The
validity of the code stems not from a conversion but from:

"A type may have a method set associated with it (§Interface types,
§Method declarations). The method set of an interface type is its
interface. The method set of any other named type T consists of all
methods with receiver type T. The method set of the corresponding
pointer type *T is the set of all methods with receiver *T or T (that
is, it also contains the method set of T)."

http://golang.org/doc/go_spec.html#Types

This is semantically distinct/disjoint from the mechanism of a
conversion. It can be mistakenly perceived as interchangeable though
(i.e. as a conversion from T to *T). But there is no such conversion
in Go. Neither implicit nor explicit. To get an expression of type *T
from an expression of type T one must use the address operator ('&')
of a T instance or a type literal ('&T{}') or 'new(T)', not a
conversion.

The above specifications of the method sets just says that the
language/compiler will conveniently "insert" the address operator for
you in the same manner as it will done here, just in the opposite
direction:

type S struct{
f int
}

var v = &S{1}

println((*v).f) // manually
println(v.f) // here the '*' operator is "inserted" by the language/
compiler for convenience.

Bill Burdick

unread,
Nov 20, 2010, 11:54:11 AM11/20/10
to bflm, golang-nuts
On Sat, Nov 20, 2010 at 1:07 PM, bflm <befeleme...@gmail.com> wrote:
This is semantically distinct/disjoint from the mechanism of a
conversion. It can be mistakenly perceived as interchangeable though
(i.e. as a conversion from T to *T). But there is no such conversion
in Go. Neither implicit nor explicit. To get an expression of type *T
from an expression of type T one must use the address operator ('&')
of a T instance or a type literal ('&T{}') or 'new(T)', not a
conversion.

The above specifications of the method sets just says that the
language/compiler will conveniently "insert" the address operator for
you in the same manner as it will done here, just in the opposite
direction:

type S struct{
   f int
}

var v = &S{1}

println((*v).f) // manually
println(v.f)    // here the '*' operator is "inserted" by the language/
compiler for convenience.

So you think conveniently inserting an address of operator in this case is not actually implicit type conversion between types and pointers to those types:

type T struct {i int}
func (t *T) foo() {t.i = 3}
func main() {
   t1 := T{}
   t1.foo()
}

In this case, I'd like to see the same conveniently inserted address of and dereference operators for assignment, parameter passing, and return values that are also not implicit type conversion.

Bill Burdick

unread,
Nov 20, 2010, 11:58:18 AM11/20/10
to bflm, golang-nuts
In the case of a = b, you already know the type of a and b.  Having to add a * or a &, in my opinion, is just needless ritual -- as far as I can tell, they are only needed for assignment, parameter passing, and returning values.  Are the * and & operators really necessary?  I think * is great for specifying precise memory layout, but what is the point of the operators?  Why not get rid of them and make assignment, parameter passing, and return values follow the pattern of "."?

bflm

unread,
Nov 20, 2010, 12:48:27 PM11/20/10
to golang-nuts
On Nov 20, 5:54 pm, Bill Burdick <bill.burd...@gmail.com> wrote:
> So you think conveniently inserting an address of operator in this case is
> not actually implicit type conversion between types and pointers to those
> types:
What a conversion in Go does mean is written in the specs. In Go there
is no conversion (let's ignore the unsafe package) between T and *T
possible, same for the other direction. Taking an address of an entity
E is not converting N in any way. The only way to take an entity
address (or dereference a pointer) is via the pointer operators. For
anybody's convenience, those address operators are inserted by the
language semantics/compiler in well defined places. As Ian pointed out
previously, it's just awkward to write '(&v).M' when there is some
'func (p *type_of_v) M()'. The other way of this convenience fills a
semantic gap of a field of a pointer. 'v.f' has (very strictly) no
meaning if 'v' is a pointer, so the most obvious thing it can be is
'(*v).f' and that's what the specs do for us.
> In this case, I'd like to see the same conveniently inserted address of and
> dereference operators for assignment, parameter passing, and return values
> that are also not implicit type conversion.
That's IMHO impossible, the reasons have been explained several times
before. In one sentence - the programmer will lose most of the control
about what and how things are going to happen. Note that the above
conveniences do not have this unwelcome property. I can easily imagine
that a person experienced in languages which do such "autoconversions"
can miss it in Go and that Go could then be not a good language for
such person. But that's not a reason to hurt Go.

I wonder what the e.g. Java community would say about a hypothetical
effort of someone to introduce pointer and pointer operators to the
language, but I know it doesn't make really any sense.

bflm

unread,
Nov 20, 2010, 1:01:42 PM11/20/10
to golang-nuts
On Nov 20, 5:58 pm, Bill Burdick <bill.burd...@gmail.com> wrote:
> In the case of a = b, you already know the type of a and b.  Having to add a
> * or a &, in my opinion, is just needless ritual -- as far as I can tell,
> they are only needed for assignment, parameter passing, and returning
> values.  Are the * and & operators really necessary?  I think * is great for
> specifying precise memory layout, but what is the point of the operators?
>  Why not get rid of them and make assignment, parameter passing, and return
> values follow the pattern of "."?

Examples:
a = b
a = &b
a = *b
*a = b
*a = *b

Take the above assignment statements as independent, whole programs,
line per line, so the types of a and b on line 1 are not assumed to be
same as on line 2 etc.

I wonder, how one can write them with your proposal. I guess some of
them became impossible. Or not? I haven't tried, will you?

Rob 'Commander' Pike

unread,
Nov 20, 2010, 1:14:56 PM11/20/10
to golang-nuts
If * were eliminated, given

type Number *Number
var n Number

how would one distinguish assigning to n from assigning to the contents of n?

If that seems a silly example, it's just a form of

var p *int
x := p

Is x a copy of p, or a copy of what p points to?

* is an operator. It operates. It can't operate if it's not there.

There are a couple of places in the language where it's invisible, for
programmer convenience, but it's still there. Just ask the compiler.

-rob

Bill Burdick

unread,
Nov 22, 2010, 9:30:19 PM11/22/10
to Rob 'Commander' Pike, golang-nuts
On Sat, Nov 20, 2010 at 8:14 PM, Rob 'Commander' Pike <r...@golang.org> wrote:
If * were eliminated, given

   type Number *Number
   var n Number

how would one distinguish assigning to n from assigning to the contents of n?

If that seems a silly example, it's just a form of

   var p *int
   x := p

Is x a copy of p, or a copy of what p points to?

In this case, it seems like x ought to be the same type as p and contain a copy of its value.

* is an operator. It operates. It can't operate if it's not there.

There are a couple of places in the language where it's invisible, for
programmer convenience, but it's still there. Just ask the compiler.

-rob

What I'm saying is that it seems like Go can generalize the dot operator's boxing and unboxing to get rid of a lot of uses of * and & in code where the address of and dereference operators are obvious (like the dot operator does).  If a case is ambiguous, the compiler can generate an error.  But there are common cases, like

func f(pi *int) {
  ...
  *pi = (int-value)
  ...
}


Why not this?

func f(pi *int) {
  ...
  pi = (int-value)
  ...
}

It seems pretty similar to what the dot operator accomplishes, to me.  In Go, you have to say *pi = 3, but isn't the "*" really boilerplate?  You have to put * on pi in order to assign to the contents of it, but 3 is known to be an int and you can't convert an int to a pointer; there aren't a lot of options there -- why require the '*' if the language can put it in for you?  If not, the programmer is worrying about the language itself instead of the program -- something you addressed you a few of the Google tech talks I've seen on the net.  Isn't it one of the central points of Go to remove boilerplate code and help programmers work more in the problem domain and less in the language domain?

I know that people can deal with pointers -- programmers have been doing it for years and years, and, having programmed for many years in C, C++, LISP, Smalltalk, Java, etc., I can definitely see the value of being able to specify memory layout and choose between pointers and structures for fields, etc., but I think there is also value in the way LISP, Smalltalk, etc. make pointers disappear for the programmer, even though the language still uses it in the implementation.

I dunno, maybe like Ian was saying, it's too dangerous to have address-of declared in a parameter but not given at the call site, making pointer parameters behave like var parameters in Pascal (pass-by-reference).


Bill

bflm

unread,
Nov 23, 2010, 12:54:15 AM11/23/10
to golang-nuts
On Nov 23, 3:30 am, Bill Burdick <bill.burd...@gmail.com> wrote:
> On Sat, Nov 20, 2010 at 8:14 PM, Rob 'Commander' Pike <r...@golang.org> wrote:
>
> > If * were eliminated, given
>
> >    type Number *Number
> >    var n Number
>
> > how would one distinguish assigning to n from assigning to the contents of
> > n?
>
> > If that seems a silly example, it's just a form of
>
> >    var p *int
> >    x := p
>
> > Is x a copy of p, or a copy of what p points to?
>
> In this case, it seems like x ought to be the same type as p and contain a
> copy of its value.
>
I believe Rob's rhetoric question was not about *what* has to be
chosen, but *how* can one here make a choice without the pointer
operators.
> * is an operator. It operates. It can't operate if it's not there.
>
>
>
> > There are a couple of places in the language where it's invisible, for
> > programmer convenience, but it's still there. Just ask the compiler.
>
> > -rob
>
> What I'm saying is that it seems like Go can generalize the dot operator's
> boxing and unboxing
No boxing, no unboxing is done by the selector (dot) operator. The
automatic dereferencing of a pointer, when it appears with a selector,
is only a convenience providing the only sane semantic value of some
'p.field' whilst not aliasing/shading any other reasonable and/or well
defined meaning of it.
> having programmed for many years in C, C++, LISP,
> Smalltalk, Java, etc.,
That surprises me.

chris dollin

unread,
Nov 23, 2010, 1:25:47 AM11/23/10
to Bill Burdick, Rob 'Commander' Pike, golang-nuts
On 23 November 2010 02:30, Bill Burdick <bill.b...@gmail.com> wrote:
> but I think there is also value in the way LISP, Smalltalk, etc.
> make pointers disappear for the programmer, even though
> the language still uses it in the implementation.

They make "pointers disappear" by not having a choice
between pointers and composite values; all (assignable)
structured values are dealt with using pointers to memory
which is (almost always) allocated from some heap.

It's not surprising that this difference shows up in the syntax
for accessing values.

The test case for me is, given that a and b are both *T,
what does

a = b

(or, equivalently, f(b) where f = func(a *T) {...})

mean? Whichever of "assign b to a" or "assign the
value b refers to to the place a refers to" it means,
how do you express the other one? The only answers
I've seen -- this is not the first time we've discussed
this issue -- either throw up their hands and say, well,
in that case we need special syntax/use * after all,
or try and break up the assignment/parameter passing
into smaller, disambiguated (, clumsy) chunks.

Given that you can't get the bump out of the carpet,
sticking with the existing, well-understood techniques,
for something that doesn't seem to be an actual problem
(the gains in expressiveness are IMAO small), seems
like a reasonable approach.

Chris

--
Chris "is the sentence finished yet? has it a VERB?" Dollin

Bill Burdick

unread,
Nov 23, 2010, 11:05:34 AM11/23/10
to bflm, golang-nuts
On Tue, Nov 23, 2010 at 7:54 AM, bflm <befeleme...@gmail.com> wrote:
> What I'm saying is that it seems like Go can generalize the dot operator's
> boxing and unboxing
No boxing, no unboxing is done by the selector (dot) operator. The
automatic dereferencing of a pointer, when it appears with a selector,
is only a convenience providing the only sane semantic value of some
'p.field' whilst not aliasing/shading any other reasonable and/or well
defined meaning of it.

Go essentially allows pass-by-reference semantics for receivers only.  For example, if you say p.meth(), where p is a struct and meth is defined on pointers to that type of struct, you haven't explicitly passed a pointer to p, but the method gets a pointer to it, which it can store somewhere else if it wants to, causing a change to the p struct from some other code at some other time (which is what you would expect from pass-by-reference, anyway).  To me, this makes the dot operator fairly magical in a language that only has pass-by-value most other places.  I'm just saying, "hey, has anyone thought about making the other value-transfer facilities more magical as well?"  According to Chris, there's already been discussion about this, so I'm happy.
 
> having programmed for many years in C, C++, LISP,
> Smalltalk, Java, etc.,
That surprises me.

It does not surprise me that you're surprised.


Bill

chris dollin

unread,
Nov 23, 2010, 11:16:54 AM11/23/10
to Bill Burdick, bflm, golang-nuts
On 23 November 2010 16:05, Bill Burdick <bill.b...@gmail.com> wrote:

>
> Go essentially allows pass-by-reference semantics for receivers only.  For
> example, if you say p.meth(), where p is a struct

Nitpick (but an important one): where p is a /variable/ of some type T ...

> and meth is defined on pointers to that type

T

> of struct,

which need not be a struct.

> you haven't explicitly passed a pointer to
> p, but the method gets a pointer to it, which it can store somewhere else if
> it wants to, causing a change to the p struct from some other code at some
> other time (which is what you would expect from pass-by-reference, anyway).
>  To me, this makes the dot operator fairly magical in a language that only
> has pass-by-value most other places.

Other nitpick: it's not the dot operator, it's the dot operator /used in a
method call/.

Bill Burdick

unread,
Nov 23, 2010, 11:51:06 AM11/23/10
to chris dollin, bflm, golang-nuts
On Tue, Nov 23, 2010 at 6:16 PM, chris dollin <ehog....@googlemail.com> wrote:
On 23 November 2010 16:05, Bill Burdick <bill.b...@gmail.com> wrote:

>
> Go essentially allows pass-by-reference semantics for receivers only.  For
> example, if you say p.meth(), where p is a struct

Nitpick (but an important one): where p is a /variable/ of some type T ...

I really did mean "p.meth()", not <p>.<meth>() -- my example wasn't supposed to be a rule, just one case that illustrates that dot allows pass-by-reference.  Maybe it would have helped if I just gave a code example instead of describing it:
package main
type t struct {v int}
func (i *t) meth() *t {return i}
func main() {
var p t
var pp *t
println(p.v)
pp = p.meth()
pp.v = 5
println(p.v)

}


> and meth is defined on pointers to that type

T

> of struct,

which need not be a struct.

> you haven't explicitly passed a pointer to
> p, but the method gets a pointer to it, which it can store somewhere else if
> it wants to, causing a change to the p struct from some other code at some
> other time (which is what you would expect from pass-by-reference, anyway).
>  To me, this makes the dot operator fairly magical in a language that only
> has pass-by-value most other places.

Other nitpick: it's not the dot operator, it's the dot operator /used in a
method call/.

When I said, "for receivers only" above, I thought that was clear that I was talking about message sends (method calls); you need a message in order to have a receiver.

To be clear, I'm not saying that Go's dot is bad, it's just more magical than parameter passing, assignment, and return.  Since you mentioned that this was already all discussed before I brought it up, I think this is beating a dead horse.


Bill

bflm

unread,
Nov 23, 2010, 12:26:51 PM11/23/10
to golang-nuts
On Nov 23, 5:05 pm, Bill Burdick <bill.burd...@gmail.com> wrote:
No, there's no conversion involved and there's nothing magical
happening. Consider the following program:

=================
package main

type T int

func (t T) f() {
println("f1")
}

func (t *T) f() {
println("f2")
}

func main() {
var v T
v.f()
}
===============
Would you try to compile it, you should see an error:

prog.go:9: method redeclared: T.f
method(t T)func()
method(t *T)func()

What this means is - there is always one and only one possible
interpretation of the v.f() in a valid program above (i.e. by means of
getting rid of one of the conflicting functions f()). For your
convenience the language specs and the compiler kindly allows you not
to write that awkward (&v).f() because, as can be seen above, it is
not ambiguous with only one method f() existing for type T which v
happens to by instance off.

Introducing your "magic" would lead to not being anymore able to
control what should happen and how as has been explained and shown too
many times before to no avail.

In one sentence one more time - The selector (you call it the dot
operator) only selects and does no magic, no conversion, no pass-by
mode modification and the whole mistaken perceiving of it doing
something special is an unambiguous convenience of the language saving
you few keystrokes which *are semantically still there* and that's why
nothing magical is happening.

Perhaps it could be helpful to try out rewriting the assignment
statements from the earlier post using your proposal of removing the
pointer operator(s) and see for yourself what happens.

Bill Burdick

unread,
Nov 23, 2010, 12:56:39 PM11/23/10
to bflm, golang-nuts
On Tue, Nov 23, 2010 at 7:26 PM, bflm <befeleme...@gmail.com> wrote:
What this means is - there is always one and only one possible
interpretation of the v.f() in a valid program above (i.e. by means of
getting rid of one of the conflicting functions f()). For your
convenience the language specs and the compiler kindly allows you not
to write that awkward (&v).f() because, as can be seen above, it is
not ambiguous with only one method f() existing for type T which v
happens to by instance off.

You're just replacing the idea of "pass-by-reference" with the idea of "kindness."
 
In one sentence one more time - The selector (you call it the dot
operator) only selects and does no magic, no conversion, no pass-by
mode modification and the whole mistaken perceiving of it doing
something special is an unambiguous convenience of the language saving
you few keystrokes which *are semantically still there* and that's why
nothing magical is happening.

By the way, you should probably know that it's the *name* after the dot that's the selector, not the operator itself.  Check it out: http://golang.org/doc/go_spec.html#Selectors "The identifier f is called the (field or method) selector"  This concept probably comes from Smalltalk (that's where I learned it, anyway).  Maybe it's from Simula, though, I'm not sure.

It really seems like you think that saying "the compiler kindly allows you not to write that awkward (&v).f()" changes the fact that v.f() passes v by reference and not value.  It doesn't.  You didn't put a & in front of v, but somehow, f() ended up with a pointer to it.  Just because "the language is saving you a few keystrokes" means that it's not pass-by-reference?  Really?  Run this code and it will print 3, not 4.  Looks like pass-by-reference to me:

package main

type t int

func (i *t) f() {*i = 3}

func main() {
var x t = 4
x.f()
println(x)
}

Bill Burdick

unread,
Nov 23, 2010, 12:59:45 PM11/23/10
to golang-nuts
By the way, Chris, Ian, Rob, I really do appreciate your courtesy and patience!


On Tue, Nov 23, 2010 at 7:26 PM, bflm <befeleme...@gmail.com> wrote:

bflm

unread,
Nov 23, 2010, 4:24:32 PM11/23/10
to golang-nuts
Being not even wrong actually is enough to not to be proven wrong.

HTH
Reply all
Reply to author
Forward
0 new messages