About two days ago the net started to fill with information about a new programming language, created by people at Google. The language is called Go, and is something between a low-level language (like C/C++) and a high-level language (like Python, Java or C#), combining the features of the first (compiling to native code, execution speed, etc) and the later (garbage-collector, native thread support, etc). Yesterday evening I found some time to test the language, and I've managed to port one of my raytracers to Go. So, after the first sight, I decided to write a little about what I think of Go, and to show You my raytracer of course (source code at the end of the post). By the way, the opinion is made after only 5 hours of coding in Go, so I reserve the right to change my opinion at will, nd also, the things I write might not always be accurate ;>

I'll start with saying that imho writing in Go goes quite comfortably, and in most cases surpasses the comfortability of C/C++. However, I have to say that in some cases, the guys at Google created a few strange syntax 'monsters' ;>

Let's begin with what I like about Go:

1. Declaring and initializing variables using the symbol :=
In Go you can declare and initialize variables using two different ways. The first way goes as follow:
var name type = value;
var a int = 5;

So, it's basically same as C/C++/etc - we have explicitly stated the type, name and the value (I'll get back to this later). Now, the second way:
name := value;
a := 5;

As one can see (or see not), the type is nowhere explicitly stated, instead, the type is decided based on the type of the initializing value. This may seem strange at first (especially in a language that has no implicit casts), but it proved to be extremely useful, especially if I wanted to create some variable that received the return value of a function (ret := my_func(12,34);). Hmm, I guess you could do that in C/C++ using some kind of a macro, like #define VAR(a,b) typeof(b) a = b, but of course, it would work when the r-value is a variable (hmm, maybe in case of constants too).

2. If and for without the brackets
Too tell you the truth, at first the missing ( ) brackets seemed at least strange to me (especially in case of 'for'), but I've managed to switch to it quite fast, and I've found it actually pretty intuitive. Of course, it's just a matter of taste, so I guess in your case you might hate it at the first sight ;>
I'll add that the 'for' loop goes quite well with the := symbol, for example.:
for i := 0; i < 10; i++
{
...
}


3. Goroutines
Goroutines are separate threads of execution (not to be confused with a normal system-type thread), that work in a set of system-type threads (it reminds me of fibers, but it seems that the runtime library handles the fiber switching) - so, we have native multithreding!  And also, it's very easy to use! Just take a look below:
go my_any_type_function(any, arguments any_type);
And thats it. You don't have to create wrappers that get rid of strange argument passing that very likely would require you to create some structure/class and pass it by a void* pointer, then do some casts etc. And, you don't have to open the header/documentation to check how many unused/reserver arguments does the function take (yes CreateThread, I'm talking about you). You just add the go verb in front of a function call, and thats it.
Whats interesting, the function can be defined just after the go verb, so you don't always have to create a separate external function. Take a look at an example from the official documentation:
go func(ch chan<- bool) { for { sleep(10); ch <- true; }} (c)

4. Channels (chan)
A channel appeared already in the above example. Well, a channel seems to be a lightweight pipe, that transmits packets of a given type, and thats use reminds me of C++ streams (it's because of the arrows ;>). So, we don't have to call any CreatePipe, WriteFile or ReadFile (with tenths and tenths of parameters), nor check if the received data is all we are waiting for. You just create a variable with the word chan and you have a pipe! Now, you can write to it, and read from it, like this:
my_channel <- data; // write
variable := <-my_channel; // read (synchronic)

Additionally, thanks the fact that it's synchronic, it can also be used as a simple wait mechanism - you just run a goroutine, and wait until it passes something through the channel (I assume it writes to the channel the return code or something similar).

There are also some other things ;>

But now, a few things that I didn't like.

1. The lack of explicit stating which interface a class implements
So, we have an interface (an abstract class), and we want to implement it. It appears that it's enough to create a class with methods named the same way as the methods from the interface. From one point of view, it's pretty handy. From another, you have to spend some time digging through the documentation/code to find out which class implements which interface. In C++ you would just read the class name after the ": public" tokens (or similar), but here you have to compare the function names for interfaces and classes, since it's nowhere explicitly stated what was implemented by the class.

2. Strange way to define a class method
An example!
func MNormalFunc(arguments type) return_type { }
func (p *Class) Method(arguments type) return_type { }

This might be a matter of taste, but I think Class:Method() from C++ was way more readable than (p *Class) Method from Go - too many "decorations" imo. An interesting choice however is letting the programmer decide how to call the object pointer (in C++ it was 'this', here you can decide - in the above example, it's called 'p').

3. Initializing many variables at declaration time
An example!
var a,b,c,d int = 1,2,3,4;
Well... I can see the point - you don't have to task-switch your brain so much ;>. First you write the names, and then you think about the values. You don't have to write name, value, name, value, etc etc and switch from brain-task 'create some name' to 'think about the initial value' and back. But... Well, it seems to remind me of Perl, it's easy to write, but hard to read. You have to actually count the number of commas before the name of the variable your interested in knowing the value of, and then, count the commas again on the other side of the equation mark. Talk about hard work ;>
However I must admit, that this point might someday get transfered to the 'what I like' section ;>

And thats that. Even if I don't like a few things about Go, it is very possible I'll stick with it for a longer period of time.

I'll just add that Go works quite well with the GCC preprocessor (cpp), but you have to remove the lines starting with # before feeding the code to the Go compiler. And, I look forward to the Windows version, since it's currently available only on *nix platforms.


And now, the raytracer code (it's my second program in Go, after Hello World, so don't expect well written code or using all of the Go features):

t2.go (9.6 KB, go, src)

And thats it! Go go go ;>

UPDATE: I looks like I wasn't the first one to write a raytracer in Go ;> Greetings to Grammerjack ;>

Comments:

2009-11-13 21:31:06 = vashpan
{
Yap...

But why not D language ? :) It have more features needed for "normal" programmer, links with C code, syntax basically the same as C/C++, but not so complicated and more clean, powerfull templates and compile-time code generating, no headers, really fast compile time to machine code (comparable with Java and C#), inline assembler, could be low-level, integrated documentation generation and syntax, "modern" object model (interfaces, "real" modules and packages, delegates, all methods virtual from the beginning, override keyword, exceptions with 'finally') REAL structures ( no more POD's, struct is a structure in a C-way, passed by value ) but with regular methods. More... built-in dynamic arrays, associative arrays, arrays operations, string operations. Garbage Collector - but you can disable it and free memory by 'delete' and even 'free' functions, and at least but not last: contracts: built-in assertions, unit tests,function 'in' and 'out' assertions - all of this compiled into native code 40 times faster than C++ code, and this code is running at the same speed as C++ :)

Why Go could be better ? The only notable feature is goroutines I think, everything else is rather disappointing...
}
2009-11-14 08:28:06 = Gynvael Coldwind
{
@vashpan
Because this post is about Go ;>
It would be strange to write about D in a post about Go, right ?
Please note that I'm not comparing the 'betterness' of languages here, I'm just noting the existence of a new language that I find Interesting ;>

I don't think you can say which language is better. You can just say which language is better for a given task/project, or which language suits you most ;>
}
2009-11-18 15:16:32 = vashpan
{
@gynvael coldwind

Yes, yes, I know, but I wanted to show that there is another language with similar concepts, with better features :) And of course you have right - language is only a tool, we have to choose it carefully ;) And, btw. Go seems to be quite specialized as for "systems" language.
}

Add a comment:

Nick:
URL (optional):
Math captcha: 6 ∗ 10 + 1 =