Live data from Hacker News

Go is my hammer, and everything is a nail

maragu.dev

371–380 of 816 posts

Re: Go is my hammer, and everything is a nail

#371
post #105

Go is everything I don’t want in a language for my personal projects. It’s verbose, every simple task feels like a lot to write. It’s not expressive, what would be a one-liner in Python makes you write three for loops in Go. I constantly need to find workarounds for the lack of proper enums, lack of sum types, no null safety etc. I’m sure these are the exact reasons why Go is good for enterprise software, but for per…

I feel like autocomplete has reduced the problem of verbosity for all languages. And if I am writing something that is going to have to be supported a lot I want something that is very explicit and easy to read. For me that is the "verbose" languages.

I feel the opposite way about this, and find this kind of verbosity reduces the signal to noise of the intention behind the code

Re: Go is my hammer, and everything is a nail

#372

Earlier quoted context omitted.

Gamedev in Go will require cgo (and not just the easy parts), which ups the complexity quite a bit, unless you're already very familiar with C. I think it's pretty viable nonetheless, but more for the experienced developer with specific goals outside of the nice parts of common engines, or for a hobbyist who knows the language and wants to tinker and learn.

Sorry, this comment is so incorrect that I have to ask, what are you basing it on? You can create games today using Go without cgo, and there are numerous examples of shipped games of varying complexity and quality. I do this to ship the bgammon.org client to Windows, Linux and WebAssembly users, all compiled using a Linux system without any cgo. https://ebitengine.org https://github.com/sedyh/awesome-ebitengine#game…

It was based on my own experience (with e.g. sdl2) and, clearly, some ignorance.

I didn't mean to imply that cgo was an insurmountable barrier. But apparently it was a big enough deal for the authors of this engine that they copied over large parts of major API surface to Go to avoid it. Impressive.

However, AFAICT avoiding cgo means using unsafe tricks and trusting that struct layout will stay compatible. Nevertheless, it's a proven solution and as you say used by many already.

Re: Go is my hammer, and everything is a nail

#373

Earlier quoted context omitted.

The point of using a mockable interface, even if there's only one real implementation, is to test the behavior of the caller in isolation without also testing the behavior of the callee. This can be overdone of course, not everything needs this level of separation, but if it makes testing one or both sides easier, then it's usually worth it. It's especially useful for testing nontrivial interactions with other people…

Did you miss "just one implementation"? A mock is literally defined by being another implementation. If the 'mock' is your sole implementation, we don't call it a mock, that's just a plain old regular implementation.

[deleted]

Re: Go is my hammer, and everything is a nail

#374
post #105

Go is everything I don’t want in a language for my personal projects. It’s verbose, every simple task feels like a lot to write. It’s not expressive, what would be a one-liner in Python makes you write three for loops in Go. I constantly need to find workarounds for the lack of proper enums, lack of sum types, no null safety etc. I’m sure these are the exact reasons why Go is good for enterprise software, but for per…

I would rather go had real enums, and I would _prefer_ if there were sum types. I agree it's more verbose, but I don't find that that verbosity really bothers me most of the time. Is res= [x for x in foo if "banned" in x] really actually more readable than var result []string for _, x := range foo { if strings.Contains(x, "banned") { result = append(result, x) } } ? I know it's 6 lines vs 1, but in practice I look at…

Filtering a container by a predicate is 50 year old technology and a very common thing. It's unbelievable that a "modern" language has no shorter or clearer idiom than that convoluted boilerplate filled Ministry of Silly Walks blob. Python had filter() and then got list comprehensions from Haskell list comprehensions. PowerShell has Where-Object taking from C# LINQ .Where() which takes from SQL's WHERE. Prolog has include/3 which goes back to sublist(Test, Longer, Shorter) and LISP-Machine LISP had sublist in the 1970s[1]. APL has single character / compressing an array from a bitmask result of a Boolean test in the original APL/360 in 1968 and it was described in the book in 1962[2].

Brian Kernighan gave a talk on the readability of code and not getting too clever[3] "Elements of Programming Style" where he talks about languages having one way to write one thing so that you can spot mistakes easily. I am aware he's one of the Go designers and I will mention that again in a moment. In Python the non-list-comprehension version might be:

    result = []
    for x in foo:
        if "banned" in x:
            result.append(x)
Which is still clearer than the Go, simply by having less going on. I usually argue that "readable" merely means "familiar" and Python is familiar to me and Go isn't. Your Go code makes me wonder:

- "var" in C# does type inference. You declare []string but don't declare types for _ x or the tuple _,x what's up with the partial type inference? What is "var" adding to the code over classic "int x" style variable declarations?

- What is "range" doing? From _ I guess it does enumeration and that's a throwaway for the index (if so it has an unclear name). If you have to enumerate and throw away the index into _ because there isn't another way to iterate then why does keyword "range" need to exist? Conversely if there are other ways to iterate and the keyword "range" is optional, why do it this way with a variable taking up visual space only to be thrown away? (The code equivalent of saying "for some reason" and drawing the audience's attention to ... nothing). And why is range a keyword instead of a function call, e.g. Python's "for (idx, elem) in enumerate(foo):" ?

- Why is there assignment using both := and = ?

- Why string.Contains() with a module name and capital letter but append() with no module and all lowercase? Is there an unwritten import for "strings"?

- The order of arguments to Contains() and append(); Prof. Kernighan calls this out at 10m10s in that talk. C# has named arguments[3] or the object.Method() style haystack.Contains(needle) is clear, but the Go code has neither. It would be Bad Prolog(tm) to make a predicate Contains(String1, StringA) because it's not clear which way round it works, but "string_a in string_1" is clear in Python because it reads like English. AFAIK a compiler/type system can't help here as both arguments are strings, so it's more important that the style helps a reader notice if the arguments are accidentally the wrong way around, and this doesn't. We could ask the same about the _, x as well.

- "result =" looks like it's overwriting the variable each time through the loop (which would be a common beginner mistake in other languages). If append is not modifying in place and instead returning a new array, is that a terrible performance hit like it is in C#? Python list comprehensions are explicitly making a completely new list, but if the Go code said "result2 = append(result, x)" is it valid to keep variable "result" from before the append, or invalid, or a subtle bug? The reader has to think of it, and know the answer, the Python code avoids that completely.

- And of course the forever curly brace / indent question - are the closing } really ending the indented block that they look like they are ending judging from the dedent? I hear Go has mandatory formatting which might make that a non-issue, but this specific Python line has no block indentation at all so it's less than a non-issue.

- The Python has five symbols =[""] to mentally parse, pair and deal with, compared to twenty []_,:={.(,""){=(,)}} in the Go.

Step back from those details to ask "what is the code trying to achieve, and is this code achieving the goal?" in the Go the core test ".Contains()" is hiding in the middle of the six lines. I'm not going to say you need to be able to read a language without learning it, but in the long-form Python what is there even to wonder about? B. Kernighan calls that out about 12:50 in the talk "you get the sense the person who is writing the code doesn't really understand the language properly". You say code is meant to be read more than written, and I claim it's more likely that a reader won't understand details, than will. Which means code with fewer details and which "just works" the way it looks like (Pythonic) is more readable. As BWK said in the talk "It's not that you can't understand [the Go], it's that you have to work at it, and you shouldn't have to work at it for a task this simple".

[1] https://old.reddit.com/r/ProgrammingLanguages/comments/14tvu...

[2] https://keiapl.org/archive/APL360_UsersMan_Aug1968.pdf 3.38 or PDF page 94, dating back to the original A Programming Language (APL) book in 1962, source https://aplwiki.com/wiki/Replicate#History

[3] https://www.youtube.com/watch?v=8SUkrR7ZfTA

[4] https://learn.microsoft.com/en-us/dotnet/csharp/programming-...

Re: Go is my hammer, and everything is a nail

#375

Earlier quoted context omitted.

The point of using a mockable interface, even if there's only one real implementation, is to test the behavior of the caller in isolation without also testing the behavior of the callee. This can be overdone of course, not everything needs this level of separation, but if it makes testing one or both sides easier, then it's usually worth it. It's especially useful for testing nontrivial interactions with other people…

Did you miss "just one implementation"? A mock is literally defined by being another implementation. If the 'mock' is your sole implementation, we don't call it a mock, that's just a plain old regular implementation.

I think my comment was clear on the distinction between real and mock implementations. If the code was testable with no need for mocks then certainly remove the interface and devirtualize the method calls.

Re: Go is my hammer, and everything is a nail

#376
post #129

Earlier quoted context omitted.

IME, there are two main differences between go and java: 1) go is more "batteries included". Modules, linting, testing, and much more are all part of the standard cli. Also, the go stdlib has a ton of stuff; in java, there is almost always a well-built third party library, but that requires you to find and learn more things instead of just reaching for stdlib every time. 2) golang is "newer" and "more refined". this…

Eh, imo the go libraries still aren't up to par with out of the box java libraries. Like there's still no Set class, nor the equivalent of Map.keys. yeah they're easy to write but that's still not an included battery. Also, while the cli to add stuff is useful, there's still nothing to the level of maven or gradle for dependency management, and I usually find myself doing some fun stuff with `find -execdir` for modul…

maps.Keys is coming in a few days with 1.23.

I generally agree with what you are saying. Although I wouldn’t hold out Maven as a paragon. I used to make my living untangling pom.xml files. I don’t think anybody is feeding their family helping people with go.mod messes — although I wish somebody would do that for kubernetes.

Re: Go is my hammer, and everything is a nail

#377
post #364
post #94

People always under-estimate the cost of properly learning a language. At any given time I tend to have a "main go-to language". I typically spend 2-4 years getting to the point where I can say I "know" a language. Then I try to stick to it long enough for the investment to pay off. Usually 8-10 years. A surprising number of people think this is a very long time. It isn't. This is typically the time it takes to under…

> It is the time it takes to where you can start to meaningfully contribute to evolving how the language is used and meaningfully coach architects, programmers and system designers. It is also what you need to absorb novices into the organization and train them fast. I think these criteria in particular are much more than a lot of people mean when they say "learn a language", which would explain why their estimates a…

2012: Python is Awesome!

2014: Python is a great language, but there are a few pitfalls

2016: Python is a good language with the right IDE, tooling, and process. The people are pretty cool though.

2018: I like python, but I wish more people used type annotations.

2020: You know, metaclasses are freaking awesome! They saved me so much work!

2022: Why can't people code the most obvious solution in python?

2024: Celery! Jesus H. Christ! What were you thinking?!

Re: Go is my hammer, and everything is a nail

#378
post #105

Go is everything I don’t want in a language for my personal projects. It’s verbose, every simple task feels like a lot to write. It’s not expressive, what would be a one-liner in Python makes you write three for loops in Go. I constantly need to find workarounds for the lack of proper enums, lack of sum types, no null safety etc. I’m sure these are the exact reasons why Go is good for enterprise software, but for per…

That's why I love Go so much. You want to write a very clever library using elegant abstraction and generics to have a cool innovative interface to solve your problem? Tough luck, you can't. So instead, you will just have to write a bog standard implementation with for-loops and good old functions which you will have to copy and tweak as needed where you really need something more complicated. It will work perfectly…

"Simple" is a cop-out word. Things can be simple along a lot of vectors. The vector you've chosen seems to be "does less for you" which taken ad absurdum would have you using assembly. Go does have elegant abstractions, and they aren't the simplest along this vector, nor would anyone want them to be. Coroutines, for example, are actually quite conceptually complicated in some ways.

I prefer "understandable"--it appears this is what you're trying to get when you say "simple", but I think you're drastically overselling the understandability of go code. Sure, you understand any given line easily (what you described as "readable"), but you're not usually trying to understand one line of code. Since go's sparse feature set provides few effective tools for chunking[1] your mental model of the program, complex functionality ends up being in too large of chunks to be understood easily. This problem gets worse as programs grow in size.

Another poster mentioned that they start running into problems and wishing they had explicit types with Python programs over 10K LOC, which approximately matches my experience. But comparing to go, you've got to realize that 10K LOC of Python does a whole lot more than 10K LOC of Go; you'd have to write a lot more Go to achieve the same functionality because of all the boilerplate. That's not necessarily a downside because that boilerplate is giving you benefits, and I don't think entering your code into the computer is the limiting factor in development speed. But it does mean that a fair comparison of equally-complex programs is going to be a lot more lines of Go than Python, i.e. a fair comparison of might be 10K LOC of Python vs 50K LOC of Go. I say "might be" because I don't know what the numbers would be exactly.

How many people have written or worked on projects in Go of that complexity? How many people have written or worked on programs of equivalent complexity in other languages to compare? I'm seeing people discuss how easy it is to start a project in Go, but nobody is talking about how easy it is to maintain 50K LOC of Go.

I've worked on projects of >200K LOC in Python, and the possibly-equivalent >500K LOC in C#. I think the C# was easier to work with, but that's largely because the 200K lines of Python made heavy use of monkey patching, and I've worked in smaller C# codebases that made heavy use of dependency injection to similar detriment. I'm honestly not sure which feels more maintainable to me, given a certain level of discipline to not use certain misfeatures.

I haven't written as much Go, and I wouldn't, because the features of C# which make it viable for projects of this complexity simply aren't present, and unlike Python, Go doesn't provide good alternatives. I suspect the reason we don't have many people talking about this is that not many projects have grown to this complexity, and when they do these problems will become apparent.

The real weak point is Go's type system--it's genuinely terrible, because the features that came standard in other modern statically-typed languages decades before Go was invented were bolted onto Go after the fact. Gophers initially claimed they didn't need generics for a few years. As a result you've got conflicting systems developed before `go generate` (using casts), after `go generate` but before generics (using go generate), and after generics (using generics). It's telling that you seemingly reject generics ("clever library using elegant abstraction and generics") even though go has them now.

Attacking Haskell is sort of a straw man--so far I haven't seen anyone in this thread propose Haskell as a go alternative. I think we agree Haskell is far too dogmatic about its abstractions when it's impractical to be used as a general-purpose language (because I don't think it's intended as a general-purpose language).

[1] https://en.wikipedia.org/wiki/Chunking_(psychology)

Re: Go is my hammer, and everything is a nail

#379
post #217

Earlier quoted context omitted.

Take a look at a JSON parser or ORM written in Go. It's god awful the things they have to do to work around Go's type system. The average developer won't see these things, they're typically just writing glue code between Go's great stdlib (which also contains wild things if you take a look) and other 3rd party dependencies.

> The average developer won't see these things, they're typically just writing glue code between Go's great stdlib (which also contains wild things if you take a look) and other 3rd party dependencies. This is what most of us are doing every day, and exactly what Go excels at.

If you want to progress your career you'll need to take on hard problems at some point.

Go isn't particularly unique in excelling at easy problems.

Post reply on HN