Learning some Go: the Rosetta Stone

18 Apr 2020

The Rosetta Stone is a black granite slab that was carved with the same text in 3 different languages, around the year 196 BC. These languages were Ancient Greek, Demotic Egyptian and Egyptian hieroglyphs. The latter was never understood until the stone was discovered for the western civilisation.

It seemed the Greek and Demotic texts were telling the same story, so scholars studying these writings had the brilliant assumption the undecipherable hieroglyphs on the top of the stone would also convey the same meaning. They used the known languages as a guide to decipher the hieroglyphs, first finding some easy names such Cleopatra and Ptolemy, later the full text. Being able to translate hieroglyphs opened the gates to understand all the old Egyptian writings out there that were a complete mystery for the modern world.

Nowadays, the Rosetta Stone is displayed at the British Museum. If you wonder how it looks like, I took these pictures in December 2012 (during my first visit to London).

I remember when Golang was released and made available on Google App Engine, in 2011. I already had a Python website running on GAE, and the new language piqued my interest. However, it looked raw and primitive compared to Python, and the lack of HTML templates among other web application goodies that I considered essential… made me discard the novelty. I must say Go provides concurrency features out-of-the-box which is something very attractive, but those weren’t tools my website would have benefited from.

Finally, last month I faced some sluggishness at work on some Ruby code we use to read and parse big XML files. It was one of those Ruby limitations: the language is slow, no big surprises here. It’s the price we pay for the productivity.

I thought there could be a chance to write that part in a different, faster language. Motivated by these benchmarks and my past experience with C (too raw), Java (memory hungry), PHP (ugly) and Python (slow) I decided to jump into the unknown and do something exciting such as learning a new language: Go!

Golang is fast, produces very small binaries and does not require lots of memory to run. In terms of language design philosophy, feels like Python to me because its simplicity. Sometimes, even too simple and bare. Feels like C too, but does not let you blow up your foot. In any case, a 100% orthogonal depart from Ruby.

I’ve done 2 toy projects so far, an HTML to PDF conversion server and a Markdown wiki, and I can list some features any Rubyist will surely find odd:

I’m a firm believer in learning by example, and I found very useful the official reference guide, and this nice site. Another convenient way to quickly get the feeling of a language is using something like the Rosetta Stone so your brain can infer new meanings based on prior knowledge.

Here it is such stone, for Ruby, Python and Go:

Go Python Ruby
true
false
nil
True
False
None
true
false
nil
Conditional operators
x == nil
x is None
x.nil?
if condition {
  
} else if condition {
  
} else {
  
}
if condition:
  
elif condition:
  
else:
  
if condition
  
elsif condition
  
else
  
end
Loops
for condition {
  
}
while condition:
  
while condition
  
end
for {
  
  if condition {break}
}
while True:
  
  if condition:
    break
loop do
  
  break if condition
end
for key, value := range myMap {
  
}
for key, value in my_dictionary:
  
my_hash.each do |key, value|
  
end
Lists
import "sort"
my_slice := []int{} // also: var my_slice []int
my_slice = append(my_slice, 79)
my_slice = append(my_slice, 19)
b := append(my_slice[:0:0], my_slice...) // duplicate slice
sort.Ints(b) // sort is always in-place
sort.Ints(my_slice)
import "reflect"
reflect.DeepEqual(my_slice, b)
my_array = []
my_array.append(79)
my_array.append(19)
b = sorted(my_array)
my_array.sort()
my_array == b
len(my_array) == 2
my_array = []
my_array << 79
my_array.append 19
b = my_array.sort
my_array.sort!
my_array == b
my_array.length == 2
Associative arrays
import "strconv"
var myMap = map[string]string{}
for i, value := range [3]int{5,6,7} {
  myMap[strconv.Itoa(i)] = strconv.Itoa(value)
}
my_dictionary = {}
for i, value in enumerate([5,6,7]):
  my_dictionary[str(i)] = str()
my_hash = {}
[5,6,7].each_with_index do |value, i|
  my_hash[i.to_s] = value.to_s
end
import "strconv"
var myMap = map[string]string{}
for i, value := range [3]int{5,6,7} {
  myMap[strconv.Itoa(i)] = "a" + strconv.Itoa(value)
}
values := make([]string, 0, len(myMap))
for _, val := range myMap {
  values = append(values, val)
}
if _, ok := myMap["1"]; ok {
  
}
my_dictionary = {}
for i, value in enumerate([5,6,7]):
  my_dictionary[str(i)] = "a" + str(value)
values = values(my_dictionary)
if "1" in my_dictionary: 
my_hash = {}
[5,6,7].each_with_index do |value, i|
  my_hash[i.to_s] = 'a' + value.to_s
end
values = my_hash.values
if my_hash.include?('1') then  end
Regular expressions
import "regexp"
if match, _ := regexp.MatchString("ab", "ab ac ab ad") {
  
}
r := regexp.MustCompile("ab")
r.FindAllString("ab ac ab ad", -1) // []string{"ab", "ab"}
r.FindString("ab ac ab ad") // "ab"
import re
if re.match(r'ab', 'ab ac ab ad') is not None: 
re.findall(r'ab', 'ab ac ab ad') # ['ab', 'ab']
m = re.match(r'ab', 'ab ac ab ad')
m.group(0) # 'ab'
if 'ab ac ab ad' =~ /ab/ then 
'ab ac ab ad'.scan /ab/ # ["ab", "ab"]
m = 'ab ac ab ad'.match /ab/; m[0] # 'ab'

Check in often, it may suffer updates from time to time.

If you are interested into other tables for other languages, see:

Examples: