It's a long post that proposes adding a lot of things to the language.
I think the author picked a good title; he wants a language that is
not Go.
Go is a (very) small language, and IMO that is one of its major
strengths. Features are only introduced after they are deemed
essential.
Andrew
Yeah, like "I want to stick in a bunch of features to the Go", but as
Andrew has pointed out, that won't be the Go anymore. IMHO the author
of that article doesn't really realizes that if he will stick in
templates _and_ operator overloading in any language it will become
a mess. It is inevitable. I mean really, we have a language that has
(almost) all these "nice" features and it is called D. And no one uses
D, because it is overcomplicated and bloated.. But you know, it
compiles faster than Go, but that's no the only reason why we love Go.
Being simple is a very hard property to achieve, and Go is the closest
thing we have today.
Of course Go has its own issues, like recent proposal of a new built-in
"append" function showed us, which btw removes the need for a "vector"
package mentioned in article. But let's be honest. Go is good enough and
adding new features now should be a strictly "de facto" thing. I mean
when we want to add something we must have at the same time enough
cases which this new feature solves and extra steps should be
considered to make sure that the solution is generic enough to be
included in the language. Let's take me for example. Currently I have
one major Go app (> 5k loc) - gocode, and many small ones. And the
only case where I had to use very evil copy&paste technique was exactly
the vector-like append functions.
Experience in other programming languages tells me that all you need
most of the time is a vector and a hash table, because these two has
the most interesting properties (vector is a contiguous chunk of memory
and linear/streaming memory access patterns are preferred on modern
CPUs, and map has insertion/lookup complexity close to O(1)). Take a
look at most of the scripting languages, almost all of them has two
types of built-in containers, it is a vector and a hash table. What I
mean here is that maybe (really, maybe) we don't need generics at all.
On the other hand there are few cases where having an optimal sort
function is a big win. And current "sort.Interface" is not a solution
to that, it will always be slower than C's qsort which is slower than
C++'s std::sort. Also we don't have any kind of generic binary search,
which is sad. I don't think that adding built-in function for each of
those cases are a good idea, but for some.. maybe.
That's what I think regarding most painful topic of the Go - generics..
Let's see what else in that article:
- Named/keyword args - I think author proposes some kind of a name
mangling and compile-time code generation features. Crap.
- Block arguments - they're nice. But it's pure syntax sugar for
calling functions where the last argument has a function type. Syntax
sugar needs a lot of arguments to be considered for adding to the
language, IMHO.
- Operator overloading - makes language unreadable, especially if mixed
with templates. Also requires other considerations like "reference"
types (see C++). In theory is simple, in practice complicates a
language a _lot_.
- Tuples - maybe, but python experience tells me that they are a very
bad duplication for structs. Once you want to have a name for each of
the tuple's components, you're screwed. Better leave them out.
- Unions - I miss them, at least for my go lemon parser generator port.
C's and D's versions have no problems with lemon, because they both
have unions. In Go I have to use interface{} magic, which has bad
performance properties.
- Constructors - my experience shows me that they are useless. First of
all they don't work if you don't have function overloading, because
obviously for some types there is more than one way to initialize
them. And function overloading adds another layer of complexity and
ambiguity. Secondly, the problem with error handling. People will
want to write myFunc(MyObject(1, 2, 3)) and then they will complain
that it's not possible, because constructor returns multiple values
(we need to handle errors, right.. ugh.. exceptions I forgot). Crappy
features, please don't even consider it.
- Eliminating nil - I'm a C guy, nulls are ok for me. Go solves the
most nasty problem btw, it doesn't allow programmers to use 0 as nil.
The problem is addressed in C++0x as well, but it's too late for that
language.
- Exceptions - some people keep telling me that they are a superior way
of handling errors. Other people (including few very famous
programmers like John Carmack) tend to avoid them. I don't understand
them as well. Having panic/recover is fine for me as long as they
don't introduce additional language statements (hello to
try/catch/finally, the most horrible thing I've ever knew about
programming languages) and as long as Go's standard library doesn't
force me to use them.
- Generics - I've already said enough about them in the beginning.
- Uniform Access - who? Bertrand Meyer? OOP guy? I think he is nuts. In
fact every person who seriously considers OOP as a valid term is
nuts.
Summary: I dunno... A long answer to the long article.
There was a very long discussion about this last year
on this mailing list. No one had any proposals that
worked and left the language intact.
> Exceptions
http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx
Having to write and then maintain codereview.py
has only made me more aware of how right he is.
Russ
It's nice that they've tried the language and seen what they thought,
but I can't say I found the post particularly illuminating. The central
problem that, in my view, makes it an unsatisfactory review is that a
number of the issues they raise are handled in Go by common idioms, and
they haven't reviewed their suggestions against these idioms, at least
not as explictly and in as much detail as I think would be required to
be able to tell if their ideas actually provide benefit.
The alternative for "scoped behaviour" in their example is defer, which
is not discussed (well), and the provision of New* functions replaces
constructors with a more explicit form; their request for constructors
needs to be directly compared against this. Their section on error
handling is very sparse on discussion of whether the explicitness of
the handling is good or bad, which is the primary change exceptions
make, and also lacks a discussion of the value or lack thereof of
banning, by convention, panic()/exception-like behaviour across package
boundaries, another important idiom.
This might just be my opinion, but I don't think future-proofing, as
they describe it, is an idiom in Go, either. They should directly
compare the uniform access feature against a lack of any such
future-proofing, not against speculatively wrapping everything. I feel
this would be hard to prove reliably either way. My personal opinion is
one of dubiousness that the cost of modifying every call site in the
event changes happen in one place beat the costs of mitigating it
*everywhere* pre-emptively.
Another of their points seem not quite thought through; their point
about "type safety without coupling" would only apply if the error from
their function was the only error the function it was passed to
returned, and their function only ever returned one type of error.
It seems unlikely to be valid in real code, where os.Error is common.
Finally, their suggestion for multiple types for a return value is just
bad; it appears to create both additional syntax and be longer, and they
suggest it would be more efficient for "avoiding creating a variable"
without actually suggesting an implementation so the other performance
effects could be examined; I'd think this would require returning
something like an interface to make the type switch possible, which
seems like it would have significant effects of its own.
All in all... I think it would be a better review if the author had
spent more time with the language, possibly working with other people's
code, before judging the language, and perhaps thought a little harder
in some cases. It is hard to begrudge them this, although I would have
suggested not writing the review if they weren't willing to seriously
consider what is idiomatically used as alternatives to the desired
features, and be quiet about performance when they haven't analysed
what their idea would cost to implement. Perhaps my standards for blogs
are too high, though.
It does seem, though, that the language they want is not Go.
Personally I think syntactic sugar for this would be a huge win. It certainly is in Ruby, particularly for blocks which make use of yield to iterate generated results in a traditional coroutine style. In Go this could be integrated nicely with range to improve orthogonality between slice types and user-defined types.
> - Unions - I miss them, at least for my go lemon parser generator port.
> C's and D's versions have no problems with lemon, because they both
> have unions. In Go I have to use interface{} magic, which has bad
> performance properties.
Unions are another feature I could leverage a lot if it were added to Go. A lot of the dirty hackery I currently do with unsafe would disappear and the type safety of my code would be improved.
> - Exceptions - some people keep telling me that they are a superior way
> of handling errors. Other people (including few very famous
> programmers like John Carmack) tend to avoid them. I don't understand
> them as well. Having panic/recover is fine for me as long as they
> don't introduce additional language statements (hello to
> try/catch/finally, the most horrible thing I've ever knew about
> programming languages) and as long as Go's standard library doesn't
> force me to use them.
It's trivially easy to roll your own exceptions on to of panic/recover (see the code I posted a couple of months back for an example of the Ruby exception idiom in Go) and whilst I'm quite keen on them I don't see any reason they should become a core language feature.
Overall I understand the perspective the article's coming from because there are all kinds of aspects of Go that I'd like to see tweaked to suit my personal needs or biases, but the point is that Go's a young language that needs to be given space to be itself. Just last night I was being asked in an online interview how I could go from Ruby to this supposed 1970s bare-metal language, but that's not really a good characterisation of Go. Sure it's not the bastard child of C and Simula that's C++ or the half-arsed Smalltalk clone we call Java: I prefer to think of it as the best bits of C + CLU without any of the 90s Enterprise OOP nonsense that's made commercial development such a nightmare.
Would I like to see a few features from Ruby or other elegant languages? Certainly. Does that mean Go isn't fit for purpose? Far from it. It's the most productive systems language I've used in thirty years of coding and I don't think that's accidental...
Ellie
Eleanor McHugh
Games With Brains
http://feyeleanor.tel
----
raise ArgumentError unless @reality.responds_to? :reason
> Hi, I'm the author. Before I reply, I should probably set some
> context.
Hi.
Don't get me wrong too, these all are just my opinions, and who am I?
I'm no one. :)
> > IMHO the author
> > of that article doesn't really realizes that if he will stick in
> > templates _and_ operator overloading in any language it will become
> > a mess. It is inevitable.
>
> C# pulled it off. There's nothing magical at all about operator
> overloading, with the exception of the assignment operation, which
> *is* special. I probably wouldn't encourage the ability to overload
> that. Other operators are just functions. They don't cause problems
> with templates any more than regular prefix functions do, as far as I
> know.
I think C# is a mess. I've tried writing code using it's standard
library, it was one of the most horrible experiences in my programming
practice.
>
> > I mean really, we have a language that has
> > (almost) all these "nice" features and it is called D.
>
> Java and C# have most of these features too.
Then why people tend to use C and C++ for most of the systems
programming? On my linux machine I have tons of software and 0 is
written in C#/Java.
>
> > Being simple is a very hard property to achieve, and Go is the closest
> > thing we have today.
>
> Being simple is easy. Forth is simple. R4RS Scheme is simple. What's
> very hard is being simple without just pushing the complexity onto the
> users. I think simplicity is only one term in the equation and
> focusing on it can miss the bigger goal. The goal is to maximize
> *power*: what the language can express / its complexity. Reducing
> complexity will increase power but only to the degree that it
> *doesn't* reduce expressiveness.
I'm still not really sure that expressiveness is a some kind of magical
property that makes people's programs shine. Like Ruby is expressive,
Perl is expressive and I have very hard time reading programs written
in these languages. D goes to that category as well. On the other hand
things written in C and Go are very easy to read and understand.
>
> > - Named/keyword args - I think author proposes some kind of a name
> > mangling and compile-time code generation features. Crap.
>
> Don't hold back. Tell us how you really feel.
You're proposing some kind of magic transformations behind the scenes
of a call expression. substring(from: start, to: end) becomes
substring__from__to(start, end). That's how languages like C++ and D do
function overloading. They simply encode argument types as a part of a
function name (also the process is knowsn as name mangling). It can't
be good. Have you ever tried reading crappy undocumented C++ code? I
literally spent hours trying to grep things in C++ apps like that.
That's why I call this a "crap". Overloading is a very bad idea and the
fact that some form of overloading is required for generic functions
implementation makes all this stuff even worse.
> > - Operator overloading - makes language unreadable, especially if mixed
> > with templates. Also requires other considerations like "reference"
> > types (see C++). In theory is simple, in practice complicates a
> > language a _lot_.
>
> Eliminate assignment from your list of operators. Do your complaints
> still stand?
Let's be honest. When do you need operator overloading? The most
common area for that is games and 3d/2d graphics in general. These apps
tend to have a lot of math, and operator overloading makes someone's
life simpler. But the thing is, that operator overloading adds a lot of
indirections in a language. It is possible to hide complex operations
under simply looking expressions. It is bad. Don't get me wrong. It's
much better to write code in a language that has operator overloading,
but it's much harder to read it and especially debug it.
Reading/debugging has a higher priority over writing.
>
> > - Tuples - maybe, but python experience tells me that they are a very
> > bad duplication for structs. Once you want to have a name for each of
> > the tuple's components, you're screwed. Better leave them out.
>
> Couldn't you make that same argument about multiple returns? Shouldn't
> you be returning a struct instead?
Multiple return values are ok, because they don't introduce persistent
storage type as tuples do. It's like passing multiple arguments to a
function. Function form in general comes from math and in math
functions do return only one value, but computing world is different,
it makes sense to have multiple return values the same way as it makes
sense to be able to pass multiple arguments to a function.
>
> > And function overloading adds another layer of complexity and ambiguity.
>
> Overloading is a separate issue, but Go should be in a much happier
> place than C++ if it added it because it already shies away from
> implicit conversions.
It's not a separate issue, overloading is a very annoying issue,
because in a lot of places it struggles to get in. Want constructors?
But there are sometimes multiple ways to initialize a value, solution -
function overloading. Want generics? But is it possible to have multiple
functions with the same name but with different types as arguments,
solution - function overloading.
Yes, absence of implicit conversions helps us in a way that we don't
need to use function overloading for operator overloading as well. But
is it help, really?
Plus argument above, function overloading makes it harder to read the
code.
>
> > - Exceptions - some people keep telling me that they are a superior way
> > of handling errors. Other people (including few very famous
> > programmers like John Carmack) tend to avoid them.
>
> Carmack's perspective is pretty deeply tied to the needs of games.
> Things like GC and closures tend to be pretty rare there too.
Well, exceptions are used in game industry. Of course most people tend
to avoid them, especially if their game is a very computing resources
greedy. But I believe his (John Carmack) opinion has nothing to do with
that. Writing exception safe code is much more painful than good error
checking code. Maybe I'm an idiot, I don't know, but I don't get how
exceptions make my code better.
>
> > - Uniform Access - who? Bertrand Meyer? OOP guy?
>
> Yeah, what does that guy know, right?
> http://en.wikipedia.org/wiki/Eiffel_%28programming_language%29
> http://www.amazon.com/dp/0136291554
I'm sorry, it tells me nothing. I've read a lot of OOP books and I've
used a lot of OOP (I mean especially those, when an author emphasise
that fact) languages, their authors are nuts. And you're pointing
to the guy that started all this OOP mess? That's just my opinion.
>
> - bob
To beat this to death, let's say the linked list was implemented within
an array, where all 'pointers' were indices into the array. In this
case, 'nil' or zero is clearly a valid value. Which means the end of
the list (EOL) would need to be identified some other way. Would you
choose -1 for this implementation?
Using a 'special' value of the Next field to indicate EOL is encoding
more data into the field than just a 'next' value: It is also encoding
the existence of a next item in the list. This is more popularly known
as 'in-band signaling', where a special value in the domain of a field
is reserved to encode a meaning different from all other values in the
domain. The 'out-of-band' version of the list structure would have a
separate boolean field called EOL, and only when False would the Next
field have any meaning or relevance.
Merging two separate data items into a single field in this manner is
nothing less than a non-type-safe risky hack. This use of nil/null/zero
should be eliminated, if only to greatly reduce the use of problem-prone
ad-hoc in-band signaling. There are times when in-band signaling is
inevitable (such as over bit-serial links), but this specific use of nil
for in-band signaling within a pointer field isn't one of them.
The use of nil pointers in this manner is so entrenched in C and its
progeny that its elimination may seem an impossibility. I don't
recommend eliminating the 'existence' of nil pointers (doing so would
complicate operating system implementations), but their intentional use
should require jumping through some linguistic hurdles to make it
blindly clear what's happening.
Bottom line, the language should make it very difficult (but not quite
impossible) for a user to either intentionally or accidentally create or
dereference a nil pointer. At compile time, not just run time.
-BobC
What value should the Next pointer of the last element in a linked
list have, then?
--
Gordon Tisher
17
-rob
It doesn't matter: Use any value you want!
The 'safe' implementation is to have a boolean 'Last' field, and the
Next field is accessed only when Last is False. It is up to the
programmer to ensure that Next has been correctly set before Last is set
to False.
We've fought this particular battle at great length on the mailing list
and wound up right back where we started.
E.g.:
http://groups.google.com/group/golang-nuts/browse_thread/thread/aef193652154f2c6/f498a152f025b147
Ian
Ya know, the Unreal game engine's scripting language has the best of both worlds -- null pointers and no crashes. The compiler inserts null checks for dereferences that print an error message and then jump to the next source line :-)
On 2010-10-21 4:59 PM, "Ian Lance Taylor" <ia...@google.com> wrote:
munificent <munifi...@gmail.com> writes:
>> What value should the Next pointer of the last elem...
I choose nil.
> The 'safe' implementation is to have a boolean 'Last' field, and the Next
> field is accessed only when Last is False. It is up to the programmer to
> ensure that Next has been correctly set before Last is set to False.
The 'safe' implementation is to have a pointer, which can be nil,
which is only _dereferenced_ when it is not nil. It is up to the
programmer to ensure that the pointer is non-nil before using it.
--Benny.
actually, Go doesn't shy away from implicit conversions entirely.
it has implicit conversions from constant values, implicit
conversions to interface values, and implicit conversions from nil.
any or all of these would make overloading a pain in Go.
tuples can fit just fine in a statically typed language.
Limbo, one of Go's predecessors, has tuples and they work well.
i miss them in Go - it's annoying having to think of a name for
a type when the only reason for the type is to combine two values.
and it's a shame that the return value of a function isn't first class.
but it would be awkward retrofitting them into Go, because
of the way that Go overloads on number of arguments assigned to.
even in languages with no nil, such as Haskell, you can still get
exceptions from using things that have an unexpected form.
e.g. head []
that's not too different from a nil pointer exception.
so even if you go the non-nil route, you may still end
up paying a good proportion of the "billion dollar"
price.