Without access to list/dict/set comprehensions, or libraries like itertools, or a lightweight lambda syntax or something like Ruby's blocks, transformations on collections will always be considerably more tedious and verbose.
Various functions have "bring-your-own-buffer" calling conventions, which will often double or triple the number of lines required for those function calls.
No tuple unpacking, no ability for functional programming (Python's isn't that amazing but with functools and some of the builtins, you can get pretty far), no operators for string formatting or anything but absolute barebones operations.
Combine that with what Go lacks even compared to languages like Java (no inheritance [which is usually an anti-pattern but does decrease verbosity when used properly], no generics, no deep type/class reflection) and it's hard to say that Go isn't a verbose language.
No statically typed language is going to be as terse or expressive as Python can be.
You would need to know the nature of the tasks to pick out those that need longer answers but all the Go tasks are here: http://rosettacode.org/wiki/Category:Go - most of which should have Python solutions.
we decided to transliterate our 90k lines of Python directly to Go, line by line
The 90K number includes our tests, but without tests, the Python codebase is 36,784 lines of code. Those same lines of code became 41,717 lines of Go code. So, not that much of an increase. When you consider that's just 4,933 more lines, it's not crazy to assume most of those are closing braces.
I'd say closing braces and trivial expansions of list comprehensions into 3-5 line loops.
func foo() (string, int, bool) {
return "one", 2, true
}
func bar(s string, i int, b bool) {}
bar(foo())But how about things like:
g = some_input() # A list with 3 elements
a, b, c = g
Or in Python 3+: lst = [1, 2, 3, 4]
head, *tail = lst # 1, [2, 3, 4]
*init, last = lst # [1, 2, 3], 4
a, *b, c = lst # 1, [2, 3], 4 g := some_input() // A list with 3 elements
a, b, c := g[0], g[1], g[2]
I'll grant you, having to specify the indices is slightly more verbose, but it's also a lot more clear... because what happens when g has more or less than 3 elements? In Go it's clear, more is ok, less will get you a panic. lst := []int{1, 2, 3, 4}
head, tail := lst[0], lst[1:]
init, last := lst[:len(lst)-1], lst[len(lst)-1]
a, b, c := lst[0], lst[1:3], lst[4]
Again, I don't think the magic unpacking is really helping that much... doing it the same way you'd do anything else in Go is slightly more verbose, but it's also not some new syntax you have to figure out either.