should we Go now?

I

Ivan Shmakov

[Cross-posting also to and
as Go seem to be positioned as a successor to
both the "system programming languages" (C) and the "Web
programming languages," of which Perl is one I'm familiar with.
Setting Followup-To: news:comp.lang.misc only, though.]


Introduction

Becoming interested in this relatively new "Go" programming
language [1] (now that I've found that its libraries' collection
may even outshine CPAN, depending on the task at hand; I'm yet
to check the quality of the code, though), I've decided to check
its FAQ [2], and some of the related documents. There, I've
found a few design choices described which I'd like to comment
on. Certainly, there may be some misunderstanding on my part,
hopefully to be resolved in the discussion.

TIA.

[1] https://en.wikipedia.org/wiki/Go_(programming_language)
[2] http://golang.org/doc/faq


An unusual control flow feature

For me, one of the most notable features of Go seems to be its
"defer" statement. To quote [3]:
A defer statement pushes a function call onto a list. The list of
saved calls is executed after the surrounding function returns.
Defer is commonly used to simplify functions that perform various
clean-up actions.

And the code example follows:
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()

Certainly, this feature could simplify such clean-ups in the
code; the only other way for such simplification I know of is to
introduce additional functions all along the way.

Somehow, "defer" looks quite Perl-ish to me! (Such nice control
features are characteristic of the Scheme programming language,
too, but there "defer" would be too "imperative" to implement.)
I wonder if there's a clever way to bring "defer" to, say, Perl?

[3] http://golang.org/doc/articles/defer_panic_recover.html


Exceptions and assertions

The stance of the language designers on exceptions does (for the
most part) match my experience with them: which is little. To
quote the FAQ [2]:
Why does Go not have exceptions?
We believe that coupling exceptions to a control structure, as in the
try-catch-finally idiom, results in convoluted code. It also tends
to encourage programmers to label too many ordinary errors, such as
failing to open a file, as exceptional.

And then [3]:
The convention in the Go libraries is that even when a package uses
panic internally, its external API still presents explicit error
return values.

Also per my experience with CPAN packages, exceptions are rarely
used there, with most of the code I've seen returning "undef"
instead (which is known in Go as "nil", AIUI.)

The assertions go next:
Why does Go not have assertions?
Go doesn't provide assertions. They are undeniably convenient, but
our experience has been that programmers use them as a crutch to
avoid thinking about proper error handling and reporting. Proper
error handling means that servers continue operation after non-fatal
errors instead of crashing.

Which is contrary to my experience, which says that for a
prototype (which may or may not become a "real" project later),
it's far better to have an "assert ()"-, or "or die"-ridden,
code, than to waste time thinking about "proper" error
reporting. The latter may even be impossible at a given stage
of development, "thanks" to the relative unfamiliarity of the
coder with the new libraries, protocols, file formats, etc.

Unfortunately, C-style assertions in Go would likely require a
separate syntactic construct. Perhaps a simple panic () may fit
the majority of cases, however.


Expressions and statements

Being familiar with the languages of the Lisp family (mostly
Scheme and Emacs Lisp), I don't believe in the necessity of firm
separation of the "statement" and "expression" contexts. C and
Perl are rather close to Lisp in this respect, given that, for
instance, the assignment operator forms an "expression" in both
(while in other languages it may be a "statement" instead.)

At least one modern C implementation (GCC) goes a bit further
and allows statements almost anywhere in expressions. Also,
both C and Perl provide three conditional operators for use
within expressions: binary &&, ||, and the ternary ? :. (Perl
provides a few more, but it isn't all that relevant here.)

Unfortunately, Go seem to take a step back there. Consider [2]:
Does Go have the ?: operator?
There is no ternary form in Go. You may use the following to achieve
the same result:
if expr {
n = trueVal
} else {
n = falseVal
}

In my opinion, this latter form is more error-prone, just as the
"a = a + b" form is more error-prone than "a += b", -- thanks to
the duplication of the identifier.

Also, with several choices being handled, this can easily expand
the code quite a bit. Consider, e. g.:

a = (foo_p ? foo
: bar_p ? bar
: baz_p ? baz
: qux);

which has to be transformed to, say:

switch {
case foo_p:
a = foo;
case bar_p:
a = bar;
case baz_p:
a = baz;
case true:
a = qux;
}

There's one more notable difference in having two particular
operators form statements, and not expressions.
Why are ++ and -- statements and not expressions? And why postfix,
not prefix?
Without pointer arithmetic, the convenience value of pre- and postfix
increment operators drops. By removing them from the expression
hierarchy altogether, expression syntax is simplified and the messy
issues around order of evaluation of ++ and -- (consider f(i++) and
p = q[++i]) are eliminated as well. The simplification is
significant.


I have some doubt regarding whether this decision is justified
or not. For instance, even though the pointer arithmetic is
gone (the inconveniece of which is partly remedied by an easier
to use "slicing" feature), wouldn't it still be convenient to
write, say, a[i++] = b[j++]? Go doesn't allow for that.


The matters of style

One more notable decision made for Go is that it allows for a
newline to end a statement (unless it leads to an error), thus
eliminating most of the semicolons one'd find in a similar C
code, and is (AIUI) similar to the AWK approach. The price to
pay for that is that there can't be a newline before the
function definition's opening brace:

func () foo() {
// ...
}

... Frankly, I don't seem to recall for a major programming
language designer /not/ to advocate for a particular coding
style. (And so for the major users.)

The all-time low record in this respect is probably set by the
Python language, which goes as far as to hardwire a particular
coding style into the language syntax itself (which made Python
a no-go for me.) Go seem to take a gentler approach here [2]:
Since Go code is meant to be formatted automatically by gofmt, some
style must be chosen. That style may differ from what you've used in
C or Java, but Go is a new language and gofmt's style is as good as
any other. More important -- much more important -- the advantages of a
single, programmatically mandated format for all Go programs greatly
outweigh any perceived disadvantages of the particular style.

Instantly, I began to think if there could be a person brave
enough to fork gofmt (or write a similar tool anew) to provide
for a set of parameters to adjust comparable to that of
GNU Indent [4]!

A particular point is that there's an old typographic convention
of spacing the parenthesis from the outside (as in "a (b) c"),
which Go seem to forgo at times, and which I'd like to get back.

[4] http://www.gnu.org/software/indent/manual/
 
R

Rainer Weikusat

Ivan Shmakov said:
[Cross-posting also to and
as Go seem to be positioned as a successor to
both the "system programming languages" (C) and the "Web
programming languages," of which Perl is one I'm familiar
with.

I'm using Perl for 'system programming' and 'Go' won't replace it (it
also won't replace C) for the simple reason that it uses another "I
swallowed the Kool Aid and it was sure tasty!" runtime environment:
Deterministic object finalization based on lexical scopes is not only
a very powerful and easy-to-use way to manage all kinds of resources,
not just memory, it is also a useful programming paradigm beyond
resource management. As an example, in a certain program, I'm using
some classes to facilitate construction of (Linux) kernel IPset
objects. These provide interfaces for adding data to such an object
(either 'CIDR addresses' or port numbers) and construct the actual
kernel object during execution of DESTROY[*]. That's the sensible way
to make this happen automatically because if the object is going away,
new members won't be added to the set anymore.

Tracing garbage collectors, OTOH, are an exercise in academic
masturbation 'hard-coded' around a couple of assumptions about the
execution environment of the original Lisp implementation (AFAIK, an
IBM 704 machine) such as 'no multiprogramming, no networking, single
userspace application in total control of the environment, pointers
are not data' which essentially haven't been true anywhere since (it
was demonstrated that general purpose computers can run Lisp code
faster than 'Lisp machines' 20 years ago) and offer no real advantages
except that they can solve toy problems specifically invented to
demonstrate the usefulness of these memory management algorithms
(something people who presently can't use some important application
written in Java because it is leaking file descriptors very likely
don't care about).

I spent the last 1.5 weeks mostly with writing Java code and having to
write code which explicitly release a 'lock objects' managed outside
of the JVM instead of creating a class instances of which will release
their locks 'at the right time' during object destructions felt pretty
much like 'stone age coding' to me (NB: I didn't research how
finalization works in Java and may assumption that it is coupled to
the garbage collector may be wrong). perl can do better than that.

[*] I'm aware that this is another feature in the process of becomeing
'documented away' because it is considerd to be politically incorrect
to provide it.
 
I

Ivan Shmakov

Rainer Weikusat said:
[...]
as Go seem to be positioned as a successor to both the "system
programming languages" (C) and the "Web programming languages," of
which Perl is one I'm familiar with.
I'm using Perl for 'system programming' and 'Go' won't replace it (it
also won't replace C) for the simple reason that it uses another "I
swallowed the Kool Aid and it was sure tasty!" runtime environment:
[...]

a couple of assumptions about the execution environment of the
original Lisp implementation (AFAIK, an IBM 704 machine) such as 'no
multiprogramming, no networking, single userspace application in
total control of the environment, pointers are not data' which
essentially haven't been true anywhere since

Doesn't seem to be all that applicable to Go. Consider that:

* Perl uses GC;

* so does Emacs;

* Go provides multiprogramming facilities "out of box";

* Go seem to have a decent networking library;

* Go has pointers.

Somehow, Go seem to lack destructors, although I don't quite
understand how this goes well along with having a GC. But
perhaps it doesn't.

Other than that, my most major objection to Go's design
currently is its syntax.

[...]
 
R

Rainer Weikusat

Ivan Shmakov said:
Ivan Shmakov <[email protected]> writes:
[...]
as Go seem to be positioned as a successor to both the "system
programming languages" (C) and the "Web programming languages," of
which Perl is one I'm familiar with.
I'm using Perl for 'system programming' and 'Go' won't replace it (it
also won't replace C) for the simple reason that it uses another "I
swallowed the Kool Aid and it was sure tasty!" runtime environment:
[...]

a couple of assumptions about the execution environment of the
original Lisp implementation (AFAIK, an IBM 704 machine) such as 'no
multiprogramming, no networking, single userspace application in
total control of the environment, pointers are not data' which
essentially haven't been true anywhere since

Doesn't seem to be all that applicable to Go.

Since you deleted the core part of my statement, the remainder doesn't
make sense anymore and is hence not 'applicable' to anything.
Consider that:

* Perl uses GC;

perl implements automatic memory management by keeping track of
'object references' where each reference is supposed to represent 'a
user of the object', not based on the inherently wrong assumption
that future use of an object will be impossible if no pointer to it
can be found within the address space of a single process at an
essentially arbitrary time. This implies (as I wrote in the part of my
text you've chosen to ignore) that the lifetime of perl objects
created in a particular block will usually end when the corresponding
block is exited which is very useful manageing resources other than
memory (such as database locks in the case of the example I gave for
that) and can be utilized in other ways to cause certain code to be
executed automatically 'when the time is right for that' (I also gave
an example for that).

The same is not true for the Go runtime environment because it uses
'some kind of mark & sweep' garbage collector.

[...]
* Go provides multiprogramming facilities "out of box";

The 'usual' meaning of the term 'multiprogramming' (I'm aware of)
refers to running more than one program on a given computer
concurrently, exploiting (nowadays) a combination of 'multiple
execution units' (processor cores or processers), preemtive
task-switching and hardware facilities to separate different processes
from each other. This implies that pointers to a certain object could
exist in the address space of another process (the garbage collector
cannot inspect) or in the kernel (the garbage collector also cannot
inspect), an example of that would be the Linux 'epoll' interface
which is capable of keeping track of a 'caller supplied pointer'
associated with a file descriptor which will be provided in case of an
I/O event on this descriptor.
 
R

Rainer Weikusat

Ben Morrow said:
Perl uses refcounting. One of Perl's flaws is that it doesn't use proper
GC.

What you believe to be 'proper automatic memory management' is based
on a set of flawed assumptions about the execution environment of a
program[*] (and really dated flawed assumptions, as they go back to the
original Lisp implementation in the 1950s) and has other practical
drawbacks. The memory management approach used by perl 5 is the same
approach which is commonly used in OS kernels. By your reasoning, the
people writing such kernel code have no clue about 'proper memory
management' (This is an appeal to authority and logically not a sound
argument. But simply asserting that what works is 'flawed' and what
doesn't is 'proper' also isn't).

[*] In particular, a tracing collector tries to guess if an object can
be used again in future based on whether there's currently reachable
pointer to it in the sole 'process address space' it can examine
(which was no problem on an IBM 704 because it didn't have virtual
memory) instead of tracking 'object users'. This implies that pointers
must be treated as 'especially magic quantum state' existing outside
of the laws of physics in some magic way which inherentely ties them
to this particular address space. And this is wrong. A pointer is just
a piece of data and it can be written to files, communicated to other
processes on the same machine, communicated to other process on
different machines on a network, be stored in the kernel,
aritmetically transformed to a different value and transformed back to
the original one etc and all of these operation have 'really existing'
practical uses.
 
R

Rainer Weikusat

Ben Morrow said:
Perl uses refcounting. One of Perl's flaws is that it doesn't use proper
GC.

After consuming three beer, I feel like writing something positive:
Depending on what angle you come from, perl 5, despite all his warts,
is either 'C++ done right' or 'Lisp done right' and even after two
decades of 'high-end engineering' of the technical excellence company
which went broke while trying to force this camel through the eye of
needle, it is easily two order of magnitude faster for real problems
than Java.

Do you actually understand what you're trying to kill because it
shouldn't exist in the proper universe of whishful thinking? Or are
you just a hardcore terrorist who enjoys blowing up bridges because
they're useful?

I disclaim any responsibility for this because I'm drugged and didn't
start the character assassination :).
 
B

BartC

Other than that, my most major objection to Go's design
currently is its syntax.

I'm not a fan of C-style syntax. But I think Go has done a good job of
tidying it up, and hasn't been afraid of some radical departures (for
example, getting rid of that mandatory 'break' in switch statements).

I use a similar rule, and at one time it was very simple: semicolons are
inserted unless the last token is a comma, or line-continuation is used.
(Some tolerance of superfluous semicolons is also needed.) It works well
enough that you can look at a thousand lines of my code and not see a single
semicolon.

That's as it should be; it's just something you never need to worry about
again (well, until you post some code and line-breaks get added in the wrong
places...)
Thus, my earlier "? :" example as it is is impossible here, and
so is my preferred style of lining the operators up like:

a = (b
+ c
+ d * e);

Does Go have line-continuation? It's not pretty, but will allow your style
of coding.
 
N

Nils M Holm

In comp.lang.misc Rainer Weikusat said:
[...] not based on the inherently wrong assumption
that future use of an object will be impossible if no pointer to it
can be found within the address space of a single process at an
essentially arbitrary time. [...]

Can you elaborate on this?

F'up-To limited to c.l.m.
 
I

Ivan Shmakov

[Dropping news:comp.lang.perl.misc from Followup-To:.]

[...]
I use a similar rule, and at one time it was very simple: semicolons
are inserted unless the last token is a comma, or line-continuation
is used. (Some tolerance of superfluous semicolons is also needed.)

The idea isn't new. Consider, e. g.:

--cut: http://www.gnu.org/software/gawk/manual/html_node/Statements_002fLines.html --
However, gawk ignores newlines after any of the following symbols
and keywords:

, { ? : || && do else

A newline at any other point is considered the end of the
statement.^1
--cut: http://www.gnu.org/software/gawk/manual/html_node/Statements_002fLines.html --

[...]
Does Go have line-continuation?

I believe it doesn't.

Patching the lexer to count parenthesis and brackets doesn't
seem like a particularly bright idea, but it might worth trying
doing it that way.

Alternatively, I may try to teach the all-mighty Emacs to show
the code formatted in my style, while it would be stored in the
one chosen by the Go designers. (I need a similar trick for
XML, anyway, as otherwise indentation level changes tend to
waste a plenty of diff-space.)
It's not pretty, but will allow your style of coding.

Thus, my Go programs are likely to end up having more
line-continuations than my similar C (or Perl, for that matter)
ones would have semicolons...
 
I

Ivan Shmakov

Perl uses refcounting. One of Perl's flaws is that it doesn't use
proper GC.

I know about the deficiencies of refcounting. However, it was
my understanding that it works quite well for Perl in practice.

[...]
Real pointers, or Perl-style safe references? Real pointers are a
major disadvantage, in my book, for any language which isn't C.

Given that Go has no pointer arithmetic, I'd say that these are
"safe references."
True GC and destructors don't play very well together, because
objects get destroyed at arbitrary times. Mechanisms like the
'defer' you mentioned originally are more appropriate for cleaning up
non-memory resources.

I may be using a wrong term, but the GC's I've worked with
typically allowed for a procedure to be run when an object
is about to be removed.

How would, say, Perl's IO handles be possible without the
availability of a mechanism to properly give the resources back
to the underlying system when such a handle is to be removed?
 
R

Rainer Weikusat

Nils M Holm said:
In comp.lang.misc Rainer Weikusat said:
[...] not based on the inherently wrong assumption
that future use of an object will be impossible if no pointer to it
can be found within the address space of a single process at an
essentially arbitrary time. [...]

Can you elaborate on this?

F'up-To limited to c.l.m.

Don't try to play this kind of tricks on me. I have no interest in
this discussion beyond that it involved features perl 5 has and Go
hasn't which happen to be among the more prominent reasons why I'm
using perl 5 for certain tasks and won't be using Go. The answer to
your 'mock question' you've attached to the the random fabrication
you've created by picking a coupld of half sentences out of a text I
wrote was contained in the parts of the text you've chosen to delete
and in some other postings as well. Go read them if you're curious.
 
R

Rainer Weikusat

Ben Morrow said:
Yes and no. It works well in simple situations, and has the advantage of
providing prompt destruction (see below), but as soon as a cyclic data
structure is created the programmer has to be very careful to use
weakrefs in all the right places, or the structure will never be
collected.

'Cyclic data structures' are a relative fringe case and the advantage
that code like this doesn't have to be written in Perl:

public ImportInformationDTO importUsers(long domainId)
throws LoginException, NamingException, IOException {
ImportInformationDTO dto;

if (!trylockADImport(domainId)) {
log.warn("AD import for #0 already running", domainId);
return null;
}

try {
dto = doImportUsers(domainId);
} finally {
unlockADImport(domainId);
}

return dto;
}

by far outweighs the 'inconvenience' of having to use weak
references: This whole method has no other purpose than work around
the deficiencies of the Java garbage collector and if something like
this isn't meticolously distributed throughout a possibly 'large' body
of Java code (48,585 LOC in this case), resource leaks or resource
'hogging' will occur and this has caused numerous user-visible
problems for the people using this code (most often because the JVM
process ran out of file descriptors but it also ran out of memory(!),
out of database sessions and generally 'out of' whatever the tracing
collector cannot manage at all or cannot manage well enough to work
reliably in practice).
 
J

johannes falcone

After consuming three beer, I feel like writing something positive:

Depending on what angle you come from, perl 5, despite all his warts,

is either 'C++ done right' or 'Lisp done right' and even after two

decades of 'high-end engineering' of the technical excellence company

which went broke while trying to force this camel through the eye of

needle, it is easily two order of magnitude faster for real problems

than Java.



Do you actually understand what you're trying to kill because it

shouldn't exist in the proper universe of whishful thinking? Or are

you just a hardcore terrorist who enjoys blowing up bridges because

they're useful?



I disclaim any responsibility for this because I'm drugged and didn't

start the character assassination :).

I love this guy! www.cat-v.org approved post!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top