Before I get into Go Routines and Channels (which are both really cool), I thought I'd do some more direct C# to Go translation. So, I took a FizzBuzz project that I had written in C#(
here) and converted it to Go(
here). Here's a play by play over the main differences. Some of it is obvious with only slight syntax differences. But other parts are pretty different from C#. That's what I'll focus on in this post. Again, if I get something wrong please let me know in the comments or send me an email.
First up is the .go file(below) that contains the package
main and the function
main. I'm creating an executable (.exe) with this file so I have to use the main package and have a function called main. Very similar to a console application in .Net. The import keyword denotes the packages I'm importing, "fmt" for formatting and writing text to the console and the line to my package that contains code for my Go implementation of FizzBuzz.
The program gets started with the
func main on line 9. The first thing I do is declare a variable and assign it a value. The value is returned from a function called GetItems which is local to this file. One thing you'll notice is the := syntax for variables. This is similar to implicit typing that you can do in .Net. In go, I'm saying create a variable called
fbp and assign it a value of whatever is returned from the GetItems function.
If I wanted to declare an
int and give it a value of 40, I would write
agesoon :=40. In C# I would write
var agesoon = 40; and in VB it would be
Dim agesoon = 40. In all cases, the type of the variable is not stated explicitely but it is inferred based upon the value assigned to it. In Go, if I wanted to change the value later I'd just write
agesoon=41. If I tried to use
agesoon:=41 I would get an error because agesoon has already been declared once.
Line 11 starts a simple for loop. The syntax is almost exactly what one would expect in C#. The fmt.PrintF on line 13 is doing basically the same thing as Console.Writeline. That's our main function. The program begins and ends here, with a few detours of course.
Let's take a look at line 19. I'm creating a variable of type FizzBuzzNumberOnly. Line 20 looks a little strange.
var _ = fbpno is odd. Why is that here? In Go, if you declare a variable and don't use it, you get a compile time error. Not a warning. An error. Same thing happens if you import a package in a .go file and you don't use anything from that package. You get a compile time error, not a warning. This is by design. One of the reasons for this is that compile time speed was important to Go right from the start. Files containing un-referenced variables and packages slow down compile time and provide...nothing. As you can expect, there are some
differing opinions on this. The _ character allows me to use the variable by assigning it the
blank identifier. The blank identifier does not create a binding. If I want to keep this variable in my program, I have to assign it to something or Go won't let my project compile.
Let's move on to the type FizzBuzzProcessor.
This file contains some of our code for the FizzBuzz library. This isn't an executable, so there's no package main. It's designed to be used by other Go programs.
The only package we import is the built in
strconv so that we can convert things to strings. First look at lines 11 through 13. We are creating an interface called IFizzBuzzItem. I used I for the first letter of the interface to keep with the convention that is widely used in .Net. After the name we specify that this interface contains one method called GetResult. It takes in a single int parameter called
num and returns a string.
What's cool about interfaces in Go is this: Any type that has a method called GetResult with an int parameter called num that returns a string
automatically implements the interface IFizzBuzzItem. We don't have to manually tell our other types to use the interface. How cool is that? It works kinda sorta like the poor man's interface I described in
this post.
On line 15 we make a type called Items that is a slice of types that implement IFizzBuzzItem. See the article
arrays, slices and maps for more info.
See, I didn't name it anything. Often times in a struct you have a name and then a variable type like, maxnum int or lastname string. But you can also just create a field and call it a type. So, my FizzBuzzProcessor has one field, that's a slice of IFizzBuzz items and one method called GetResult. You'll notice that the method GetResult is declared separately from the struct type. It's a part of the FizzBuzzProcessor type because it accepts a pointer to a FizzBuzzProcessor variable. So any FizzBuzzProcessor type will have access to this method. This is one of the things that will throw a .Net developer. We're used to having fields and methods that belong to a class all in one place (even if it's split across files using partial classes). Not so in Go. However, if you are familiar with
Extension Methods in .Net, you'll see some parallels. If you're not familiar with extension methods in .Net, now we be a good time to read up on that. If you use Linq you're using them all the time.
GetResult is going to accept a number, loop through all the instances in Items and see if the num parameter meets any of the conditions. Let's look at the conditions themselves to see how this works.
There are several types that can be used, but I'm just going to cover the FizzBuzzDivisor type since it meets the requirements for the classic FizzBuzz problem.
I define three instances (covering Fizz, Buzz and FizzBuzz) like this:
fbdiv15:=sfb.FizzBuzzDivisor{15,"FizzBuzz"}
fbdiv5:=sfb.FizzBuzzDivisor{5,"Buzz"}
fbdiv3:=sfb.FizzBuzzDivisor{3,"Fizz"}
Here, I'm declaring and assigning three variables. I'm also setting the properties after it variable is instantiated. This isn't a constructor, it's assigning property values after the fact. C# sharp allows the same thing. For example, you can do:
var person = New Persion(){LastName = "Smith"}
Go doesn't need me to name the properties (although it's allowed) since I'm matching the order and type of how the are declared in the type.
Again, the GetResult method that meets the requirements for the IFizzBuzzItem interface resides outside of the type definition. In this case, I'm passing a value to the instance and not a pointer. I don't know enough yet about pointers to know why I'm doing it one way here and another way with the other type, but I'll figure that out. I just know I had to do it this way to get the code to compile. The main thing is, methods are declared outside of the types they belong to. You get used to it pretty quick.
So that's about it. I have a few other types that make use of the IFizzBuzzItem interface so that I can test for the ultimate answer, palindromes and perfect squares. I make sure to add them in the order that I want them to be checked and that's that. If a number meets more than one condition, only the resulting string of the first match will be returned.
So, that's my FizzBuzz in Go. I learned a lot about the Go as I wrote it. I like how it's teaching me new things about software development. I still like C# a lot, but I hope to get better with Go, get up to speed on Postgresql and be able to write complete applications that aren't on Windows or Azure (yes, I know about Mono/Xaramin, it's great stuff).
So there you have it. I have a few more programs in mind that I'll write in Go, one of which will help me check code as I write it. It's fun stuff.
If you want to get started with Go yourself, read this and get...uh...going.
http://golang.org/doc/install