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]:
And the code example follows:
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]:
And then [3]:
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:
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]:
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.
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]:
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/
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/