Hacker News new | ask | show | jobs
by MoOmer 3143 days ago
Often, you don’t need concurrent types and their overhead. The sync package makes it easy to bolt on if you truly do.

It’s not as if every block is sending out each line to run in a separate goroutine. Go encourages users to share data by communicating, not communicating by sharing data [0].

On phone, sorry for syntax

    struct CMap {
        sync.RWMutex
        i int 
    }

    func (m *CMap) Inc() {
        m.RWLock()
        defer m.Unlock()
        m.i++
    }
[0]: https://blog.golang.org/share-memory-by-communicating
1 comments

i understand the patterns and design intentions of the language, despite not having written very much of it. (i just checked my work stats and i'm surprised to find that i've written/changed nearly 40k lines of go - of course that counts for less given how verbose the language is). one of my complaints is that a language designed to be simple has made (to me) incomprehensible design choices in the name of efficiency.

for instance, in a for loop ranging over a slice:

  for _, i := range some_slice {
    go func() {
      something(i)
    }()
  }
i is re-used for every iteration of the loop, so the asynchronous function does not close over i as a particular value, but rather i as whatever value the loop has iterated to by the time it is referenced. i see experienced go programmers make this mistake sometimes, even when they've run into it before. my take is that the golang designers chose to implement this behaviour to either simplify their work or in the name of efficiency. if it's the former, it's indefensible. if it's the latter, why not offer user-friendly semantics (let closing over i as in the example capture the iterated value) and simplify the generated code at compile time when possible?

for your example, you can look to the newly added sync/map which uses interface{} everywhere as an example of how the lack of generics have left users with no _sensible_ tools to remedy these kinds of language problems. how wonderful would an efficient thread-safe and type-safe concurrent map be? it's possible in many other modern and not so modern programming languages, but it is intentionally not possible in go.

Java gets this right.

  for (int i : someCollection) {
      pool.execute(() -> something(i));
  }
Because "i" is effectively final, each Runnable closes over the correct value for that iteration. If "i" were being reassigned, the closure wouldn't be allowed to use it, you'd have to decide whether to close over a final copy or a reference to a mutable object that holds the latest value over time (a one-element array is the idiom).