[HN Gopher] Go is not an easy language
       ___________________________________________________________________
        
       Go is not an easy language
        
       Author : jen20
       Score  : 439 points
       Date   : 2021-02-22 03:57 UTC (19 hours ago)
        
 (HTM) web link (www.arp242.net)
 (TXT) w3m dump (www.arp242.net)
        
       | unscaled wrote:
       | I think Go can only be defined as "simple" in the New Jersey
       | (worse is better) sense of simple, that is: "it is more important
       | for the implementation to be simple than the interface."
       | 
       | A lot of things about Go are not simple. In my opinion a simple
       | language is not about how simple it is to write a parser or a
       | compiler for that language, but it's about how many different
       | non-trivial and arbitrary pieces of information the developer has
       | to memorize.
       | 
       | This is highly tied to the Principle of Least Astonishment[1] in
       | language design: how many unexpected surprises does the
       | programmer have to deal with?
       | 
       | With Go, you get quite a lot:
       | 
       | 1. Go already _has_ generic types. These are the magical maps,
       | slices and channels. Everything else is not.
       | 
       | 2. Even if you think #1 was also true for Arrays in Java 1.4 and
       | no one was complaining, Go goes further: it already has generic
       | functions like 'copy', 'len', 'min' and 'append'. Since you
       | cannot properly describe the interface of a magic built-in
       | function like 'append' using the Go language itself, this is not
       | a standard library function, but should be viewed as an entirely
       | new piece of custom syntax, like the print statement in Python
       | 2.x.
       | 
       | 3. Nil interfaces and interfaces with a nil pointer are not
       | equal.
       | 
       | 4. Multiple return values are a magical beast - they are not
       | tuples and you cannot manipulate them in any useful way.
       | 
       | 5. Channel axioms[2]. Possibly one of the more astonishing and
       | painful aspects of Go.
       | 
       | 5. Slices are mutable, unless you copy them. This can lead to
       | some very surprising cases where a slice is passed down many
       | layers below and then modified, breaking the caller.
       | 
       | 6. Continuing the topic above, Go has neither clear data
       | ownership rules (like Rust), clear documentation tradition on who
       | owns the data passed to functions (like C/C++) nor a way to
       | enforce immutability/constness (like C++, Rust or FP languages).
       | This really pushes a lot of the cognitive overload to the
       | developer.
       | 
       | 7. Go modules are a lot better than what we had before, but are
       | quite hard to deal with. The moment you need to move to v2 and
       | above and start creating subdirectories they becomes rather
       | confusing compared to what you would do in other package
       | management system.
       | 
       | 8. If a simple language is a language that allows you to write
       | _simple programs_, and you follow Rich Hickey's classic
       | definition of Simple[3], then Go is probably one of the LEAST
       | simple languages available today.
       | 
       | tl;dr: I'm not saying other languages often compared to Go (like
       | Rust or Java) don't have their own share of complexities, but I
       | don't think Go should be viewed as a simple language in the
       | broadest sense. It is a language with a rather simple
       | implementation for our day and age (though Pascal was much
       | simpler if we stretch this definition backwards).
       | 
       | [1] https://wiki.c2.com/?PrincipleOfLeastAstonishment [2]
       | https://dave.cheney.net/2014/03/19/channel-axioms [3]
       | https://www.infoq.com/presentations/Simple-Made-Easy/
        
       | harikb wrote:
       | Going by their first example, the fact that I couldn't write
       | list.delete(value)
       | 
       | without realizing it involves a linear search is considered one
       | of the benefits of Go.
       | 
       | That said, I agree Go could use a bit for ergonomic and handy
       | methods, while still remaining efficient.
       | 
       | The example on Concurrency also has a different answer. Go gives
       | you all the tools, but there are many different ways their
       | problem can be solved. By explicitly avoiding a "join" method and
       | having any default 'channels', Go makes all these possible. What
       | is missing is some of the popular patterns that emerged in the
       | last few years need to make it to into stdlib or some cookbooks.
       | 
       | 1. If you want a worker model, I would start n=3 go-routines and
       | they all receive from a single channel. They don't need to much
       | around with a channel of buffer 3, as in the example.
       | 
       | 2. If the workers already return data from a compute, the read
       | from driver serves as the wait.
       | 
       | 3. In other cases, there is sync.Waitgroup available to
       | synchronize completion status.
       | 
       | 4. End of work from the driver can be indicated via a channel
       | close. Closed channels can still be read from until they are
       | empty and the reader can even detect end-of-channel.
       | 
       | Designing concurrent system is a bit complicated. Some tutorials
       | do make it sound like a `go` keyword is all you need. All of
       | these can fixed by improving std-lib or cookbooks.
        
         | prepperdev wrote:
         | Yes. That very example shows why itis hard to write fast
         | program in Ruby. It's not even the interpreter (which is slow),
         | but it's that Ruby is pure magic.
         | 
         | I was never able to understand what happens, when I looked at a
         | particular snippet of Ruby code, if it was not me who wrote it.
         | With Go, understanding others people code is a trivial task.
        
           | d3nj4l wrote:
           | What part of                   list.delete(x)
           | 
           | Is hard to understand if you didn't write it?
        
             | philosopher1234 wrote:
             | I don't think that example tells the whole story. You have
             | to zoom out and consider what code based look like when
             | this trade off is made repeatedly by devs. When "easyness"
             | or DRYness becomes king you get ravioli code, and it
             | becomes unintelligible. Keep it simple
        
               | majewsky wrote:
               | My persistent impression of the Ruby ecosystem is that
               | everyone optimizes for clever one-liners so hard that all
               | other priorities go out the window.
        
           | nobleach wrote:
           | As much as I don't personally enjoy writing Go. This is a
           | huge benefit. There's a decent amount of conformity around
           | the "right way" of doing things. I'll admit, it has
           | definitely made me consider my approach to solving problems
           | in other languages much simpler. I'm not back to writing
           | loops in JavaScript when I use it. It really bugs the
           | functional programming folks but... no one can argue that
           | it's readable, and it's pretty fast.
        
         | chinmaythosar wrote:
         | Linear search if it's not sorted. If it's sorted binary search
         | will be much faster. So you need a flag for the function to
         | tell it to use one of the two. This is why writing generic
         | functions can be tricky. Programmers may use something
         | inefficient just because it makes their lives easier.
        
           | jtsiskin wrote:
           | Binary search won't be any faster (in big O) because it's
           | backed by an array: you'll still need to copy every element
           | after the removed one from slot n to n-1.
        
           | harikb wrote:
           | Does Ruby track that a list is already sorted? If not, you
           | are still adding cognitive overhead for the programmer to
           | track it
        
             | vinger wrote:
             | You can sort by many things. Knowing where you sort
             | (usually as far up the chain as possible) is important
        
           | z0r wrote:
           | linear search can be faster than binary search for small
           | arrays, depending on your processor architecture.
           | 
           | writing generic functions is difficult, so it's nice if a
           | language allows people to do so, otherwise you get N
           | inefficient and/or buggy reimplementations of the function in
           | every project needing it. (not sure if that is your point)
        
             | hderms wrote:
             | obligatory link: https://www.evanjones.ca/linear-
             | search.html
        
             | chinmaythosar wrote:
             | my point was that the author uses an example like search
             | and called go not an easy language. so i was trying to give
             | an example where it's not necessary a linear search or
             | search is trivial ...
        
               | anonymoushn wrote:
               | If your developers hand write a binary search to delete
               | an element you are extremely likely to end up with bugs.
               | So it would be nice if there was a generic binary search
               | too.
        
               | BenFrantzDale wrote:
               | That Go doesn't have the equivalent of `std::lower_bound`
               | is pretty ridiculous.
        
             | arp242 wrote:
             | Linear search is also quite a bit faster than maps for
             | small arrays (in Go at least, last time I tested it).
        
         | d3nj4l wrote:
         | The advantage here isn't that it's ergonomic, it's that if I
         | get an "unknown" array I have no special knowledge about, I can
         | simply use list.delete instead of having to write a linear
         | delete and sleep soundly in the knowledge that it'll have well-
         | known performance characteristics for that case. If I know
         | something special about an array, I'm still free to write my
         | own deletion algorithm, and nobody would fault you for that.
        
         | joelfolksy wrote:
         | Since simpler languages are easier to work with, let's just
         | remove goroutines and channels from Go. Then all these problems
         | will just go away.
        
         | [deleted]
        
         | technics256 wrote:
         | I'm just learning Go myself, where can I learn or reference
         | these popular patterns that have emerged over the past few
         | years?
        
           | harikb wrote:
           | What comes handy is this [1] link, but I will update if I get
           | any better links or others might chime in.
           | 
           | [1] https://blog.golang.org/pipelines
           | 
           | It is a very long doc, but that also shows that concurrency
           | has so many patterns one might like.
           | 
           | My own pattern is typically
           | 
           | 1. Decide level of parallelism ahead of time and start
           | workers (that many `go X()` invocations 2. Setup
           | sync.Waitgroup for the same count 3. Create two channels, one
           | for each direction. 4. Job itself needs some sort of struct
           | to hold details
           | 
           | Most of my jobs don't support mid-work cancelation, so I
           | don't bother with anything else.
        
             | rakoo wrote:
             | Your 1. and 2. are merged in the almost-stdlib package
             | errgroup: https://pkg.go.dev/golang.org/x/sync/errgroup.
             | 
             | It also uses context (useful for long running, concurrent
             | jobs) and handles mid-work cancellation
        
           | majewsky wrote:
           | Study the generic reader/writer implementations in the io
           | module. (On my system, those sources are in
           | /usr/lib/go/src/io.) The io.Reader and io.Writer interfaces
           | are very simple, but very powerful because of how they allow
           | composition. A shell pipeline like `cat somefile.dat | base64
           | -d | gzip -d | jq .` can be quite directly translated into
           | chained io.Readers and io.Writers.
           | 
           | Another example of this is how HTTP middlewares chain
           | together, see for example all the middlewares in
           | https://github.com/gorilla/handlers. All of these exhibit one
           | particular quality of idiomatic Go code: a preference for
           | composition over inheritance.
           | 
           | Another quality of idiomatic Go code is that concurrent
           | algorithms prefer channels over locking mechanisms (unless
           | the performance penalty of using channels is too severe). I
           | don't have immediate examples coming to mind on this one
           | though, since the use of channels and mutexes tends to be
           | quite intertwined with the algorithm in question.
        
           | wisswazz wrote:
           | I really like http://tmrts.com/go-patterns/
        
             | majewsky wrote:
             | This looks like some C++ or Java developer trying to
             | shoehorn OOP concepts into a language that is not OOP-
             | first. Just to pick out one particularly egregious example:
             | 
             | > Registry: Keep track of all subclasses of a given class
             | 
             | Go does not have classes.
        
         | arp242 wrote:
         | > Going by their first example, the fact that I couldn't write
         | list.delete(value) without realizing it involves a linear
         | search is considered one of the benefits of Go.
         | 
         | I think this is mostly okay. Most experienced programmers will
         | realize this is a linear search and that it may be slow on
         | large arrays. And turns out that in the overwhelming majority
         | of the cases that's just fine!
         | 
         | For other cases it's not-so-fine, but the mere presence of
         | "list.delete" doesn't really stand in the way of implementing
         | another, more efficient, solution.
         | 
         | Overall, I certainly think it's _better_ than implementing the
         | same linear searches all the time yourself!
        
           | [deleted]
        
           | alkonaut wrote:
           | And it's not exactly gatekeeping to think that anyone who
           | uses a method on any data structure on their platform should
           | know the (rough) complexity of it (At least in the family
           | constant, linear, or better/worse than linear). Removing a
           | value from an array-backed list that isn't at the end is
           | usually not a good idea.
           | 
           | My main problem with having "remove items by value" in
           | standard library apis is that it assumes a notion of value
           | equality. Having that notion inserted at the very "bottom"
           | (Like Equals/GetHashCode in .NET for example) is a mistake
           | that has caused an infinite amount of tears.
           | 
           | I much prefer this situation where the user must provide his
           | own implementation and think about equality. It's
           | boilerplate, but the boilerplate is useful here.
        
             | lstamour wrote:
             | > I much prefer this situation where the user must provide
             | his own implementation and think about equality.
             | 
             | But that doesn't scale beyond the first user. Every
             | subsequent developer now needs to read implementations to
             | understand what the code does thanks to a lack of
             | standardization for these functions. Consider if there was
             | a smaller standard library with fewer interfaces and
             | conventions, it would become a lot harder to understand a
             | number of concepts in Go, by design. That's fine, but
             | conventions are what made Ruby on Rails projects so
             | successful that they scaled up to being the monolith
             | monstrosities most of us with startup experience ended up
             | knowing them to be.
             | 
             | Note that I'm suggesting something akin to C++'s standard
             | library where algorithms are already written for you to use
             | and compose with. Yes, the drawbacks are a slower compile
             | time, and some conventions like constexpr can really
             | complicate things, but... I can't say that a larger
             | standard library or a larger set of conventions would make
             | Go harder to use assuming the implementations hide a
             | sufficient amount of complexity to outweigh the overhead of
             | learning of them in the first place.
             | 
             | What functions provide more value than the overhead
             | required to learn them? Delete is one such function,
             | mutable immutable data structures generally are likely
             | another. Yes, the documentation needs to specify big O
             | complexity, but it can still be easy to read. For example:
             | https://immutable-js.github.io/immutable-js/docs/#/List
             | 
             | The only way to get easier than that would be to use the
             | same syntax for immutable operations as mutable ones:
             | https://immerjs.github.io/immer/docs/introduction
             | 
             | I recognize that the Go community finds the built-in
             | standard library restrictive now, but that's no reason not
             | to support a versioned standard library that ships
             | separately but contains common functionality. I can only
             | point to TypeScript for how such a system might work, given
             | the large community project that exists to provide and
             | publish types for that language, without actually
             | publishing them with the language or compiler itself,
             | excluding browser dom etc.
        
               | alkonaut wrote:
               | > But that doesn't scale beyond the first user. Every
               | subsequent developer now needs to read implementations to
               | understand what the code does thanks to a lack of
               | standardization for these functions.
               | 
               | There is no standardization. It's a hidden piece of logic
               | that developers slowly and painfully understand.
               | 
               | If I you _have_ to pass an equality function when
               | removing an item from a list by value (or equivalently
               | when creating a set or dictionary) it would always be
               | explicit. That doesn't mean it can't be standardized. A
               | framework can provide typical implementations (and often
               | does!) such as "ReferenceEquals" or
               | "StringComparison.Ordinal" etc.
               | 
               | Another unfortunate side effect of the "equality as a
               | property of the type" is that you can only have one such
               | equality. And if you are passed a Set of dogs you still
               | can't know for sure whether the equality used is actually
               | the one declared in the Dog type of at Set creation (or
               | possibly in the Animal base class). It's a mess. And it's
               | so easy to avoid - the default virtual equality is simply
               | not necessary.
        
               | lstamour wrote:
               | This is why I tend to like the example shown by the
               | Immutable javascript libraries. They implement find-by-
               | value using a library-specific calculation of equality
               | built on primitives, but if you need something custom,
               | you can pass predicate functions to, for example, perform
               | your own equality check.
               | 
               | I think we're agreeing here, despite the initial
               | disagreement. Standards don't have to solve every edge
               | case, nor do they have to integrate with existing
               | language patterns such as "equality as a property of the
               | type" though a standard function could have multiple
               | variants. The Go way of doing this might be like how
               | regular expression function names are built.
               | 
               | Also, the library doesn't _have_ to implement delete that
               | way. If functional programming and predicate functions
               | are an encouraged design pattern, you could replace
               | delete-by-value functions with a filter function which
               | could clearly indicate what happens if more than one
               | value is found as well as how the equality check is
               | performed but glosses over using slices to update the
               | array. Some might say it 's slower
               | https://medium.com/@habibridho/here-is-why-no-one-write-
               | gene... but it doesn't have to be slower, it's just
               | tradeoffs in how the go compiler and language currently
               | works vs could work.
        
             | simiones wrote:
             | Why would introducing value equality of all things be a
             | problem? I think the opposite is true: many languages force
             | you to write error-prone boilerplate because they lack a
             | good definition of value equality built into the language
             | and ecosystem.
             | 
             | Go in particular is worse on this front than any language
             | more high-level than C. It defines equality for a very
             | limited subset of built-in types, with no way to extend
             | that notion of equality to anything that is not covered;
             | nor any way to override the default equality assumptions.
             | This makes it extremely painful whenever you want to do
             | something even slightly advanced, such as creating a map
             | with a struct as key when that struct has a pointer field .
             | 
             | And since pointers have lots of overloaded uses in Go, this
             | turns a potentially small optimization (remember this field
             | by pointer to avoid creating a copy) to a mammoth rewrite
             | (we need to touch all code which was storing these as map
             | keys).
        
           | kaba0 wrote:
           | I'm sorry for the "gatekeeping", but do you really want to
           | work together with someone that doesn't know the language's
           | standard library or won't even look at the documentation? And
           | instead it would be positive that he/she writes the very
           | implementation of something?
        
             | jholloway7 wrote:
             | This problem transcends documentation of any given
             | language's standard library. list.delete(value) in any
             | programming language is a degenerate without an ordering or
             | hash built-in to the underlying data structure.
             | 
             | If you ever need to delete based on value, it's a smell
             | that a list is the wrong data structure. I'm sure there are
             | cases in constrained environments where a linear search is
             | heuristically okay, but generally in application
             | development list.delete(value) is a hint that you're using
             | the wrong data structure.
        
               | mhaymo wrote:
               | Good point. I guess the reason the author finds removing
               | from lists to be relevant is because there is no Set
               | structure in the Go standard library. They should use a
               | `map[t]bool`, but that is non-obvious to many
               | programmers.
        
               | vlowther wrote:
               | `map[t]struct{}` saves you a few bytes per entry. Just
               | use the `_,found := foo[key]` form of lookup
        
               | nitrix wrote:
               | map[t]struct{} so that the values don't require any
               | space.
        
               | dullcrisp wrote:
               | My Google search suggests that maps in Go aren't ordered.
               | If so this doesn't work in place of an array.
        
               | simiones wrote:
               | Unfortunately, map[t]bool (or map[t]struct{}) only works
               | for special structs that happen to have equality defined
               | for them. It's not a general purpose solution, it's just
               | a hack.
        
           | citrin_ru wrote:
           | > Most experienced programmers will realize this is a linear
           | search and that it may be slow on large arrays.
           | 
           | Experienced programmer can think that values in a list with
           | such operation are indexed by a hash and delete by value is
           | O(1) unless there is a big warning in documentation. Novice
           | programmer probably haven't seen such combination of an array
           | and a hash table and will assume O(N) as taught in a college.
        
             | wizzwizz4 wrote:
             | Experienced programmers will know that such a thing is
             | possible, but I've never seen any (non-JavaScript, non-PHP)
             | language where primitive arrays involve a hash table.
        
               | Gaelan wrote:
               | And even in JS and PHP (and Lua) where arrays and hash
               | tables are technically the same data structure, a hash
               | table used as an array isn't actually indexed on
               | elements, just numbers, so you don't get O(1) search.
        
             | shakow wrote:
             | Experienced programmer will know whether the vanilla array
             | of the language they use feature this kind of convoluted
             | data structure.
             | 
             | Which, for virtually all languages, is "obviously not".
        
             | thezilch wrote:
             | Experienced programmers, if perf matters to their project,
             | should think about their data structures, memory size, and
             | access patterns. It's not clear why an experienced
             | programmer would think an array defaults to wasting memory
             | on a hash table. Why use an array, if they need lots of
             | values and to delete them at random?
        
         | eweise wrote:
         | Developers in other languages know the performance
         | characteristics of list.delete because there is only one
         | implementation throughout 99% of their codebase. Go you have to
         | look at the implementation every single time to make sure its
         | doing the right thing.
        
           | johnisgood wrote:
           | Haskell documentation uses the Big O notation everywhere
           | which I really love. I wish Go and many other languages did
           | the same.
           | 
           | An example would be https://hackage.haskell.org/package/conta
           | iners-0.4.0.0/docs/....                 size :: Map k a ->
           | IntSource       O(1). The number of elements in the map.
           | member :: Ord k => k -> Map k a -> BoolSource       O(log n).
           | Is the key a member of the map? See also notMember.
           | lookup :: Ord k => k -> Map k a -> Maybe aSource       O(log
           | n). Lookup the value at a key in the map.
           | 
           | And so forth...
        
             | johannes1234321 wrote:
             | In the C++ standard the complexity of algorithms is a
             | requirement for a compliant implementation. Many text books
             | skip it, but better documentation references it. Taking the
             | "delete element from list" example from this thread
             | https://en.cppreference.com/w/cpp/container/list/remove
             | states that there is a linear search.
        
             | mhh__ wrote:
             | Andrei Alexandrescu has a scheme to encode these in the D
             | typesystem on his website. It didn't get merged into the
             | standard library in the end but you can happily do it in
             | your own code
        
             | arp242 wrote:
             | Redis does the same; e.g. [1]:
             | 
             | > Time complexity: O(N) where N is the number of elements
             | to traverse to get to the element at index. This makes
             | asking for the first or the last element of the list O(1).
             | 
             | I agree this is something more documentations should do
             | when possible; it doesn't even have to be big-O notation as
             | far as I'm concerned, just a "this will iterate over all
             | keys" will be fine.
             | 
             | Ruby's delete() doesn't mention any of this, although you
             | can easily see the (C) implementation in the
             | documentation[2]. In principle at least, this doesn't
             | _have_ to be O(n) if the underlying implementation would be
             | a hash for example, which of course has its own downsides
             | but something like PHP may actually do this with their
             | arrays as they 're kind of a mixed data structure? Not
             | sure.
             | 
             | [1]: from https://redis.io/commands/lindex
             | 
             | [2]: https://ruby-doc.org/core-3.0.0/Array.html#method-i-
             | delete
        
               | Groxx wrote:
               | Redis's documentation is _wonderful_ , I wish every
               | system I use were documented even half as well.
        
           | btilly wrote:
           | My experience is that developers in other languages have no
           | idea of the performance characteristics of list.delete. It
           | seems low-level and therefore obviously fast.
        
             | mdemare wrote:
             | "Fancy algorithms are slow when n is small, and n is
             | usually small." Rob Pike, creator of Go.
        
               | saagarjha wrote:
               | Not at the scale of his employer.
        
             | andi999 wrote:
             | But isn't usually when using lists performance is dead
             | anyway (due to cache misses).
        
               | simiones wrote:
               | "List" is usually a generic term for any data structure
               | that can hold many items, and that has an API for adding
               | or removing items. This can be implemented using an
               | array, a linked-list, a hasthable, or a more advanced
               | data structure.
               | 
               | Also, in languages with GCs, linked lists do not
               | necessarily cause more cache misses than arrays, due to
               | the way allocation works (this is especially true if you
               | have a compacting GC, but bump-pointer allocation often
               | makes it work even when you don't).
        
               | pansa2 wrote:
               | Linked-lists, maybe, but `list`s in Python are just
               | resizable arrays.
        
               | kindall wrote:
               | resizable arrays of pointers to objects stored elsewhere,
               | which is going to blow through your cache anyway,
               | especially when you start accessing those objects'
               | attributes. so it's more a memory management strategy
               | than a performance optimization.
        
               | pansa2 wrote:
               | But when removing an item it's not necessary to visit
               | every element, just re-arrange the pointers (and in
               | CPython, modify the reference count of the item that was
               | removed).
        
               | sokoloff wrote:
               | When removing an item _by value_ (being the use case in
               | this thread), you do need to visit the elements to check
               | the value.
        
           | [deleted]
        
           | jholloway7 wrote:
           | You can write a list data structure with performant 'delete'
           | properties if you're willing to maintain a sort or a hash
           | table. There would be people here bitching about memory usage
           | if the stdlib did that natively. Here's a solution: don't use
           | list.delete. You're using the wrong data structure if that's
           | your solution to whatever collection you're maintaining.
        
             | jholloway7 wrote:
             | Downvotes on comp sci 102. Nice.
        
               | code_duck wrote:
               | You'd probably do better on HN to substitute 'decrying'
               | for 'bitching'.
        
               | jholloway7 wrote:
               | Noted, thanks. Didn't realize that was the issue.
        
               | mirekrusin wrote:
               | We have this saying in my country "if grandma had a
               | mustache she'd be a grandpa".
        
               | omaranto wrote:
               | I like my country's version better: "if grandma had
               | wheels she'd be a bicycle".
        
               | jholloway7 wrote:
               | If your aunt had nuts she'd be your uncle
        
               | omaranto wrote:
               | That has the same basic idea as the mustache version, but
               | is pithier. I still prefer the surrealism of the bicycle.
        
         | dmitriid wrote:
         | > What is missing is some of the popular patterns that emerged
         | in the last few years
         | 
         | Erlang has had these "popular patterns" for decades. And all
         | languages that ignore them, making devs reimplement them,
         | poorly, with third-party libs and ad-hoc solutions.
        
           | jnwatson wrote:
           | It has been said that Go4 software design patterns are signs
           | of language defects.
           | 
           | Most all of them can be implemented as single lines of code
           | in Python.
        
             | koenigdavidmj wrote:
             | Most languages codify common idioms from earlier ones; I
             | don't see how most of the patterns are any different.
             | 
             | I'm sure there were a lot of assembly programs that used
             | the "call stack pattern". I'm sure there are a lot of C
             | programs that abuse struct packing to do inheritance (the
             | "class pattern", sometimes with crappy vtables) or the
             | preprocessor to do "templates". And even in Python, you've
             | got to use the visitor pattern due to single dispatch.
        
         | moonchild wrote:
         | > without realizing it involves a linear search is considered
         | one of the benefits of Go.
         | 
         | The primary problem that I think the author was trying to get
         | at is that go _lacks tools for building abstractions_. In a
         | language with generic functions (for instance), it would be
         | possible to implement a delete_at function completely in user
         | code. The fact that you cannot reflects poorly on the language.
         | 
         | Of course it will always be possible to write functions that
         | have arbitrarily slow time complexities. That's something you
         | have to be aware of, with any function; it's not a reason to
         | avoid including a linear-time function to remove an element at
         | a given index of an array.
        
           | grey-area wrote:
           | That's a deliberate choice in many ways, and it means you
           | don't end up building castles in the air, or worse, working
           | within someone else's castle in the air. See the lack of
           | inheritance for a good example of a missing abstraction which
           | improves the language.
           | 
           | Abstractions are pretty and distracting but they are not
           | _why_ we write code and each one has a cost, which is often
           | not apparent when introduced.
           | 
           | That's not to say Go is perfect, there are a few little
           | abstractions it would IMO be improved by having and the slice
           | handling in particular could be more elegant and based on
           | extending slice types or a slices package rather than built
           | in generic functions.
        
             | stiray wrote:
             | > and it means you don't end up building castles in the
             | air, or worse, working within someone else's castle in the
             | air. See the lack of inheritance for a good example of a
             | missing abstraction which improves the language.
             | 
             | Very well said. If only more developers would understand
             | this.
             | 
             | Yes I know, creating interfaces, inheriting everything from
             | the void, adding as large amount of layers to create the
             | beautifully constructed reusable structure is a nice drive
             | but it might happen no one will reuse it as doesn't want to
             | dive itself trough infinite level lasagna with rolling
             | ravioli around it (and - thanks god, go doesn't have
             | try/catch - throwing exceptions trough all the layers to
             | force you to go down the guts of the construct to figure
             | out what it means). Or, as it often happens, will be forced
             | to reuse it, complicating his life and is code.
             | 
             | I do understand that inheritance, even operator overloading
             | has its meaning and is extremely useful. But it has another
             | side - everybody are overdoing it as "might come handy
             | later" and the code becomes a case for "How to write
             | unmaintainable code"[2].
             | 
             | When I am coding I am not doing it for philosophical
             | reasons and go has until now succeeding being a very
             | helpful tool without much of "meaning of life"
             | complications. And i would love to see it stay that way.
             | 
             | If you are forcing me to read the documentation / code
             | (presumably I know what I am trying to solve) to be able to
             | use the "beautiful oo construct" and forcing you to read
             | your beautiful design, you have failed making it and I have
             | seen it in java everywhere. I just hope the same people
             | making everything complicated more than it need to be wont
             | skip from java[1] train to go train. I really don't want
             | them anywhere close.
             | 
             | [1]https://github.com/EnterpriseQualityCoding/FizzBuzzEnter
             | pris...
             | 
             | [2]https://github.com/Droogans/unmaintainable-code (and 20
             | others)
        
               | marcus_holmes wrote:
               | Yeah, this. I always found myself creating beautiful OO
               | constructs that were invariably overfitted to my
               | understanding of the state of the problem at that time,
               | and were really hard work to change once I understood the
               | problem better (or it changed).
        
               | doctor_eval wrote:
               | Yeah I've fallen into this trap and worst of all, I've
               | had my team fall into it and not find their way out.
               | 
               | Abstractions are a great idea, and Go has plenty of these
               | (io.Writer is ubiquitous), but most business logic is
               | dead boring and doesn't need it.
        
             | kaba0 wrote:
             | I don't see why a well-built standard library would be in
             | the way. In the case of delete, well write your own linear
             | search if you are into, though it gets old imo fast. But
             | will you also write a sort algorithm by hand? Because I am
             | fairly sure it won't be competitive with a properly written
             | one. Also, what about function composition? Will you write
             | a complicated for loop with ifs and whatnot when a filter,
             | foldl and friends would be much more readable?
        
               | cube2222 wrote:
               | Go actually does contain a sort package in the stdlib:
               | https://golang.org/pkg/sort/ with the most ergonomic
               | function being sort.Slice
               | 
               | As for a complicated for loop instead of the functional
               | equivalent: I too am a fan of functional patterns in this
               | case, but the for loop equivalent in my opinion is just
               | as readable in 99% of cases. A little more verbose,
               | that's all. Matter of taste.
        
               | somehow1 wrote:
               | Here is an example, https://www.cryptopp.com/ , I love
               | this library. Beautifully designed, extremely capable,
               | incredible what you can do in two lines of code. I hate
               | to use it down to my guts. Try to use it and you will
               | understand.
        
               | Gibbon1 wrote:
               | Seems to me there are a lot of different types of
               | collections which all have a delete operation. Feels like
               | go forcing you to hand roll a delete forces you to
               | violate the principal that a bit of code should care as
               | little as possible about things it's not about.
               | 
               | If code breaks because list is now a hashmap, that seems
               | like an anti-feature.
        
               | hmage wrote:
               | > the principal that a bit of code should care as little
               | as possible about things it's not about
               | 
               | Can you find/quote where that principle comes from? I'm
               | genuinely curious.
        
               | kaba0 wrote:
               | I think it is mostly based on OOP paradigm, like
               | principle of least knowledge.
        
               | Gibbon1 wrote:
               | It's a motivation for things like OOP, Generics, and
               | macro's.
        
               | grey-area wrote:
               | I agree filtering and many generic operations on slices
               | would be nice, I think they have plans for it once
               | generics lands. I don't agree they are huge problems
               | though, they are much simpler than sorting algorithms for
               | example (which are included).
               | 
               | A range vs a series of filtering operations is an
               | interesting question in terms of which is easiest to
               | write or, crucially, for a beginner to understand after
               | writing - not convinced foldl is easy for beginners. If
               | you like abstractions like that, Go will be abhorrent to
               | you, and that's ok.
               | 
               | It is easy to build your own filter on a specific type,
               | I've only done it a couple of times, it is literally no
               | more than a for loop/range. Where I have written
               | extensions and would appreciate helpers in the std lib is
               | things like lists of ints and strings - contains, filter
               | and friends would be welcome there in the stdlib (I use
               | my own at present).
               | 
               | For filter etc I think I'd use them but am not convinced
               | they would change the code much - the complexity is in
               | what you do to filter not the filtering operation itself
               | which probably adds about 3 lines and is very simple (set
               | up new list, add to list in range, return list). For
               | example in existing code I have lots of operations on
               | lists of items which sit behind a function and for hardly
               | any of them would I delete the function and just use
               | filter in place at the call site, because I have the
               | function to hide the complexity of the filtering itself
               | (all those if conditions), which would not go away. I
               | wouldn't even bother rewriting the range to use filter
               | because it's not much clearer.
        
       | systemvoltage wrote:
       | I want compiled Python with strong types. All the batteries of
       | Python included. And runs at C speed. Go feels like C and Python
       | had a baby except the baby looks more like C and less like
       | Python.
        
         | mberning wrote:
         | Many people just don't understand the joy of working with
         | something like python or ruby. Maybe they haven't had an
         | immersive experience with an "a ha!" moment. Coming from basic
         | and similar languages to java, then later to jruby and ruby,
         | and now back to java I have to say it depresses me how much
         | more I enjoy some languages and technologies over others. I
         | wish I could feel equally excited and happy to work in them.
        
           | systemvoltage wrote:
           | 100%, being productive is addicting and fun. Laboring through
           | for-loops 80th time in a the day is something for sadist
           | purists, not me.
           | 
           | As a non-expert at Go, I would have loved classic OOP +
           | batteries included Python and remove the curly braces.
           | Perfect.
        
         | pid_0 wrote:
         | This might be wrong but can't you use cython and then compile
         | python to speed it up? Obviously won't be as fast as Go but
         | seems like it can have speed ups
        
           | cb321 wrote:
           | If you think of Cython as a "new but related" language rather
           | than just a Py compiler then you can sprinkle enough C-like
           | `cdef` code into your Cython so that, when compiled, your
           | code can run much faster than Go..as fast as any C, really.
           | (EDIT: Pypy is usually a better way to make unadorned Python
           | run faster.)
           | 
           | That said Nim [1] provides nicer metaprogramming features
           | than Cython and very nice overall ergonomics..I think more
           | ergonomic than Python with, e.g. UFCS. Nim has almost all the
           | safety/speed of Rust with better ergonomics than Python. What
           | is not to love?
           | 
           | [1] https://nim-lang.org/
        
             | pid_0 wrote:
             | I'll definitely check out nim. The best thing about python
             | is the ecosystem and vast knowledge available. It makes it
             | hard to move to something "better"
        
               | cb321 wrote:
               | The Nim Forum and IRC channel are pretty helpful/rapid
               | feedback. Maybe not as much as a canned StackOverflow
               | answer for Python or an existing library to do exactly
               | what you need, but the Nim ecosystem/userbase is growing
               | all the time.
        
         | statenjason wrote:
         | Have you looked at Nim? Python-like syntax, compiles to C, and
         | a considerably sized stdlib (http, parsing, channels)
         | 
         | https://nim-lang.org/
        
           | hombre_fatal wrote:
           | Nim is an impressive undertaking, though I was sheepishly
           | overwhelmed when I tried using it for a quick project and
           | realized I might have to spend 30 more minutes reading the
           | docs than writing cowboy code. It's quite a big language:
           | https://nim-lang.org/docs/manual.html
           | 
           | Would love to take a more serious look later.
        
             | peheje wrote:
             | I can recommend a look at Crystal also, although Crystal is
             | not even 1.0
             | 
             | Between the two I found I needed much less referencing with
             | Crystal, and when I did, I found it more intuitive, well
             | described and easier to remember and apply the same
             | concepts in other places.
             | 
             | https://crystal-lang.org/reference/index.html
        
         | j-james wrote:
         | You may enjoy working with Nim.
         | 
         | Its main drawback is the small number of native libraries
         | available for it, but wrapping and using existing libraries
         | from other languages is a walk in the park.
         | 
         | https://nim-lang.org/
        
           | systemvoltage wrote:
           | Thanks, Nim looks extremely interesting. Anyone here used Nim
           | in production?
        
             | cb321 wrote:
             | Also, I am not sure what counts as "in production" but on
             | literally a daily basis I use about 15 different command
             | line utilities I wrote in Nim using cligen [1] (EDIT:
             | Really I have written several dozen but only about 15..20
             | are useful every day). There are always compiler
             | bugs/things to workaround (as with any language), but Nim
             | has been pretty usable for years.
             | 
             | [1] https://github.com/c-blake/cligen
        
             | cb321 wrote:
             | The Nim Forum [1] is written in Nim and has been used in
             | prod for years. A commercial video game Reel Valley was
             | written in Nim. Status [2] is doing Ethereum 2.0 stuff with
             | it.
             | 
             | [1] https://forum.nim-lang.org/ [2] https://status.im/
        
         | zhobbs wrote:
         | Swift could be interesting for you. Modern syntax, easy to use,
         | performant.
        
         | d3nj4l wrote:
         | If you're ok with s/python/ruby Crystal is great, LLVM compiled
         | and has a couple of cool web frameworks coming up.
         | 
         | https://crystal-lang.org/
        
         | viraptor wrote:
         | It won't be ready for production use for a while, but
         | https://github.com/python/mypy/tree/master/mypyc is being
         | worked on.
        
         | lmm wrote:
         | I stopped missing Python once I found Scala. It may not quite
         | be C speed, but it's fast enough (orders of magnitude faster
         | than Python, good enough for doing casual linear algebra in).
         | Standard library is probably smaller than Python's, but you
         | don't need it because the JVM world has really good dependency
         | management (Maven).
        
           | jpgvm wrote:
           | Kotlin pretty much did this for me. I'm no longer interested
           | in writing Python or Go because Kotlin is better than both in
           | every dimension that matters to me personally. i.e
           | power/expressiveness, performance and runtime/ecosystem
           | quality.
        
             | lmm wrote:
             | Heh yeah fair enough. I find Kotlin infuriating to work in
             | (far more so than many objectively worse languages) because
             | it's such a step backwards from Scala, but if I didn't know
             | what I was missing then I'd probably feel the same way.
        
               | jpgvm wrote:
               | I actually used Scala before Kotlin. I get what you mean
               | in Kotlin being less powerful than Scala but for me this
               | has turned out to be a good thing in bigger projects.
               | Kotlin provides enough power to get the job done without
               | giving people too much rope to hang themselves with.
        
               | lmm wrote:
               | For me it's not about power, it's about consistency.
               | Scala has a couple of general concepts that get applied
               | to everything. Kotlin seems to have a new ad-hoc way of
               | doing every different thing. E.g. my teammate just
               | rewrote some code to use a Scala-style IO type rather
               | than suspend functions because none of us could
               | understand which coroutine context was being used where.
        
         | bobbylarrybobby wrote:
         | Mypy with python's new static types is a fantastic replacement
         | for "real" types. The types mostly don't exist at runtime
         | (except for some rare cases like how dataclass handles ClassVar
         | variables) but they'll take you very far during development
         | time, especially paired with a language server like Microsoft's
         | Pylance.
        
       | tengbretson wrote:
       | This is all just crazy to me. I cannot even remember the last
       | time I dealt with array indices directly.
        
       | nitinreddy88 wrote:
       | In my opinion, coming from Java,C# background and currently
       | working on golang, here's my take.
       | 
       | 1. Golang did right design in terms of OOP
       | 
       | 2. They didnt complicate the language by accepting everything.
       | 
       | 3. At the same time, they made simple things so complex. Simple
       | logics to removing an element from slice/array and even iterating
       | them without screwing up the data.
       | 
       | I have been programming for 10yrs and I find myself doing those
       | mistakes without proper IDE/golint, while I can easily code in
       | other languages without much help. I can concentrate on solving
       | the business problem rather than fiddling around the language
        
         | ironmagma wrote:
         | "Everything should be made as simple as possible, but no
         | simpler." I've often felt Go went too far trying to make
         | everything simple, to the point that it's wrapped around and
         | made things complex.
        
           | avmich wrote:
           | "Civilization advances by extending the number of important
           | operations which we can perform without thinking of them."
           | --Alfred North Whitehead
           | 
           | Go forces writing loop for removing an element from an array
           | even if the programmer knows it's linear time. Ruby gambles
           | that programmer isn't ignorant - or it's not critical - but
           | allows the convenience every time.
           | 
           | Designing languages is hard.
        
             | cratermoon wrote:
             | > Go forces writing loop for removing an element from an
             | array
             | 
             | It does not:
             | 
             | copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index.
             | 
             | a[len(a)-1] = "" // Erase last element (write zero value).
             | 
             | a = a[:len(a)-1]
        
               | millstone wrote:
               | Your code removes by index. This can be done in one line
               | in Go:                   list = append(list[:i],
               | list[i+1:]...)
               | 
               | but to remove by value you need a loop.
        
               | pansa2 wrote:
               | It's crazy that the function you call to remove an
               | element from a list is `append`.
        
               | pests wrote:
               | If you come at it with the thought that you are creating
               | two sublists (on each side, avoiding the index) you then
               | need to merge them back together. That append reads to
               | just recreate the list from the two parts.
        
               | ziml77 wrote:
               | This makes it very unclear what's going on. I had to test
               | it to find that this specific use is special cased to not
               | copy. And very surprisingly you don't even have to assign
               | back to the same variable to get this no-copy behavior!
               | 
               | Edit: I didn't do my test quite right. It's not really
               | special-cased. But it's still very surprising to see this
               | happen:
               | 
               | Code:                   s1 := []int{1, 2, 3, 10, 11, 12}
               | s2 := []int{4, 5, 6}         s3 := s1[:2]         s4 :=
               | append(s3[:2], s2...)         fmt.Println(s1)
               | fmt.Println(s4)
               | 
               | Output:                   [1 2 4 5 6 12]         [1 2 4 5
               | 6]
        
               | millstone wrote:
               | This is a Go idiom, one of the "slice tricks" that you
               | are expected to just know. In fairness every language has
               | its non-obvious idioms.
               | 
               | It may or may not copy.
        
               | Groxx wrote:
               | Yup.
               | 
               | Simple! But not easy. Go is absolutely filled with
               | nuggets like this in my experience. False-simplicity is
               | deeply ingrained in the standard library as well:
               | https://fasterthanli.me/articles/i-want-off-mr-golangs-
               | wild-...
        
               | mrmonkeyman wrote:
               | Just write the damn loop. This magic bullshit is very un-
               | Go.
        
               | c-cube wrote:
               | When I see that, I have no idea if it allocates a new
               | array or if it's in place. Which one is it? At least with
               | a delete function it's clear.
        
               | millstone wrote:
               | In Go it may or may not allocate a new array; it depends
               | on the array's capacity. This means that the resulting
               | slice may or may not alias the original slice.
               | 
               | See this playground for an illustration:
               | https://play.golang.org/p/mOpuGVj2ypG
               | 
               | That uses append to delete, and then modifies element 0
               | in the result. In the first call, only the new slice is
               | modified. But in the second call, both slices are
               | modified, since they share the same backing array.
               | 
               | I consider this to be one of the worst mistakes of Go, to
               | design a highly multithreaded language in which variable
               | aliasing is a _dynamic_ property. I am convinced that
               | many Go programs have latent races and bugs, invisible to
               | the race detector, and they have not yet been tripped
               | because they have been (un)lucky with the runtime 's
               | capacity decisions.
        
               | z0r wrote:
               | you are right that a loop isn't required, but the point
               | that kind of index / array element juggling is a headache
               | still stands
        
               | Aachen wrote:
               | Having worked a lot with at least a dozen languages, this
               | is definitely on the cumbersome side of the spectrum. And
               | a sibling comment says it doesn't even do what it says on
               | the tin. In what other language does it take two devs to
               | remove an element from an array?
        
         | gameswithgo wrote:
         | A lot of the things in Go that seem horrible seem simple if you
         | are used to the slice syntax used in Python and other langs,
         | which I am not.
        
         | cratermoon wrote:
         | removing an element from a slice is trivial if you don't care
         | about order. Just swap the element you want to remove with the
         | last element and truncate the slice. If you want to maintain
         | order, shift all the elements following the one you want to
         | remove left one and then truncate. The latter can be slow if
         | the slice is large, but it's linear time.
        
           | systemvoltage wrote:
           | OP isn't asking for a solution. OP is talking about not
           | having to implement the solution themselves and having the
           | language provide some shortcuts for doing what you just
           | described.
        
             | cratermoon wrote:
             | shortcuts for trivial operations is how we got the leftpad
             | disaster.
        
               | gizmo686 wrote:
               | Leftpad was a result of making libraries (comically)
               | small. If you have a bunch of convenience operations, you
               | can put them all together into a single utility library
               | (or even the standard library).
        
               | justin_oaks wrote:
               | People need those shortcuts so they don't reimplement
               | them over and over. Not having the leftpad as part of the
               | language or standard library is what caused the problem.
               | This is exactly the problem the article talks about.
        
               | oftenwrong wrote:
               | >Not having the leftpad as part of the language or
               | standard library is what caused the problem.
               | 
               | There are a few other comments in this tree making the
               | same claim - is this what most people took away from the
               | left-pad incident?
               | 
               | To me, the stdlib lacking left-pad is a fairly distant
               | cause. You could make the same case for any popular
               | library, but that road leads to a bloated stdlib, which
               | forever will haunt us. The more relevant causes, in my
               | book, are:
               | 
               | 1. Many builds relied on pulling left-pad directly from
               | NPM
               | 
               | 2. NPM allowed left-pad to be unpublished
               | 
               | I like to compare this to the usual practice in the Java
               | world:
               | 
               | 1. The primary repository in use is the Maven Central
               | Repository. Nobody can unpublish a library from it. This
               | is specifically to avoid pulling dependencies out from
               | under consumers of those dependencies.
               | 
               | 2. Most Java shops run their own repository. This serves
               | both to caches requests to the Maven Central Repository,
               | and to provide a fail-safe in case something were to
               | happen to Maven Central. Backing up dependencies is
               | considered as important as backing up your own code.
               | Sometimes vendoring is used as an alternative approach.
        
               | dagmx wrote:
               | If the leftpad functionality was part of JS by default it
               | wouldn't have happened.
        
               | throwaway189262 wrote:
               | Leftpad was a result of the JS standard library being
               | terrible and not providing built-ins for such simple,
               | common things.
               | 
               | And Go is the same way...
        
       | cletus wrote:
       | I'm a little surprised deleting an element from a list is this
       | hard but that seems to be a case. That seems like an API
       | oversight. You shouldn't be writing your own code for what is a
       | fundamental operation.
       | 
       | As for leaking Goroutines, yep that's a real thing. I had
       | colleagues working on a Go project where exactly this was
       | exhausting memory and causing the process to be killed. It can
       | happen really easily.
       | 
       | I view this as a fairly fundamental problem with GC languages. GC
       | solves some problems of course but in doing so it creates other
       | problems.
       | 
       | For request-servicing type problems like servicing HTTP requests,
       | I actually think the PHP/Hack model is pretty much ideal:
       | stateless core (so low initialization costs unlike, say, Python),
       | cooperative multitasking (ie no threads per se) and just throwing
       | everything away after you're done. But I digress...
       | 
       | What Go does have going for it is syntax. It's minimal,
       | opinionated without being too opinionated (IMHO) and familiar to
       | anyone who has ever used a C-style language.
       | 
       | I tend to view Go as a better Python.
        
         | jlouis wrote:
         | Deleting an element from a slice is a O(n) operation. It better
         | be explicit in the program so the programmer knows just how
         | expensive it is. Either you know what you are doing, and then
         | writing it down is trivial, or you don't know what you are
         | doing, and you are creating an efficiency problem for the
         | future. Other data structures have a delete operation because
         | it is efficient on those.
         | 
         | And fully agree on the GC part. GC doesn't remove the work of
         | resource management. It simplifies it vastly. This can some
         | times lull people into thinking they can ignore resources
         | altogether.
        
           | cletus wrote:
           | Yeah, I'm on board with the idea that expensive or not-
           | recommended operations should be more verbose to discourage
           | them.
           | 
           | But you'll find other expensive operations (eg prepending an
           | element to a slice) are much less verbose. At a certain point
           | (of verbosity), you're just adding the potential for bugs.
        
           | damagednoob wrote:
           | > Deleting an element from a slice is a O(n) operation. It
           | better be explicit in the program so the programmer knows
           | just how expensive it is.
           | 
           | The chances of deleting an element in an array being a
           | bottle-neck are really low. I'd rather save my brain cycles
           | for thinking about disk/network access and wait for profiling
           | to show me the problem.
        
             | s3cur3 wrote:
             | Agreed. And the fact that disk access might end up being a
             | bottleneck is not a good argument for making disk access
             | APIs less ergonomic. I say give the programmer the nicest
             | set of tools possible and let them make informed decisions
             | about what's appropriate to use when.
        
           | skybrian wrote:
           | But why does the strings package exist if it's better to
           | write out the for loop each time? Do you think there won't be
           | a slices package after Go gets generics?
           | 
           | The argument that writing out the loop is better for
           | readability is just status quo bias. The real reason we don't
           | have standard functions for such things is a technical
           | limitation that will probably be removed soon.
        
         | otter-in-a-suit wrote:
         | Another thing that drove me up the wall recently (semi-related
         | to leaking goroutines) are libraries that doesn't use the
         | context package or a done/quit channel and can get stuck in
         | long-running operations that hug absurd amounts of CPU/memory.
         | 
         | As far as I'm aware, you simply cannot kill a goroutine from
         | another goroutine, i.e. there is no terminate()/stop()
         | construct, meaning you cannot implement a timeout on top of an
         | existing library without actually modifying said library.
        
       | 02020202 wrote:
       | Go's simplicity forces you to write exact code and not use
       | "magic" methods. It makes the code very readable and easy to
       | understand right off the bet even if you have never worked with
       | the project. Go IS easy, it's just on occasion a bit verbose due
       | to the lack of generics or "magic" methods. That does not mean it
       | is hard.
        
       | mybrid wrote:
       | Simple and easy are two words with no meaning. Why? Because
       | simple and easy attest to common brain paths that do not exist.
       | In computer science the divide between functional programming
       | ease and simplicity as with the likes of Lisp and procedural
       | languages as with like Python stand in stark contrast. Functional
       | programmers believe everyone thinks like they do even though the
       | vast majority of software is procedural and whose population do
       | not find being lost in sets of parenthesis easy or simple. Easy
       | and simple are meaningless because there is no uniform, blank
       | slate learning path in the human brain. In fact, if one had to
       | model every person having a unique learning path versus today's
       | notion that what's easy from me is easy for thee then the unique
       | model would surely realize far more efficacy in practice.
        
       | zimbatm wrote:
       | It depends what "easy" means. Go is a language that has very few
       | surprises.
       | 
       | As a beginner, it takes a while to figure out good goroutine
       | design patterns. And you get bit by append to slice copies a few
       | times. And it takes a bit of time to figure out how to structure
       | code efficiently, and get over the fact that the code is not
       | completely DRY.
       | 
       | I'm sure I forgot a few gotchas, but this list is still a miles
       | shorter than all the popular languages out there.
        
       | thayne wrote:
       | As someone without a ton of experience with Go, a good amount the
       | Go code I have encountered "in the wild", has actually been more
       | difficult to read and understand than code in more complicated
       | languages because I have to read through and understand all of
       | these patterns. Hopefully the addition of generics will help with
       | that. But IMO the simplicity of go actually hinders readability.
        
         | fileeditview wrote:
         | My personal experience is very different. Of course I have seen
         | bad Go code in the ~5 years I do Go development professionally.
         | 
         | But when compared to previous monstrosities in C++ or Java with
         | exceptions cluttered everywhere, deep inheritance trees that
         | are absolutely useless..
         | 
         | then Go code is an absolute breeze to read and work with. The
         | one thing I see frequently and dislike a lot in Go code bases
         | is the frequent usage of interfaces for single structs just for
         | the sake of mocking for unit tests.
         | 
         | Often I see cases where you could just strip the whole layer of
         | crap and unit test the real functions themselves. But nobody
         | seems to think about that. It seems that this "write interfaces
         | for everything and then mock/test this" pattern is dominating
         | currently.
        
           | thayne wrote:
           | > But when compared to previous monstrosities in C++ or Java
           | 
           | Perhaps part of it is the languages we are comparing to? I'm
           | comparing to languages such as rust and scala, and to some
           | extent python and ruby.
           | 
           | > with exceptions cluttered everywhere
           | 
           | I prefer errors to be part of the return value to exceptions,
           | but I also find repeating                   if err != nil {
           | return nil, err         }
           | 
           | for almost every line of significant code in go functions
           | pretty distracting. I much prefer rust's `?` operator. Or
           | haskell's do notation (or scala's roughly equivalent
           | for/yield).
           | 
           | > deep inheritance trees that are absolutely useless.
           | 
           | uh, you can have deep inheritance trees in Go, and not have
           | them in Java or C++, I'm not sure what your point is.
        
             | fileeditview wrote:
             | > Perhaps part of it is the languages we are comparing to?
             | I'm comparing to languages such as rust and scala, and to
             | some extent python and ruby.
             | 
             | I think you should compare to languages that have a similar
             | purpose / area of usage. In my experience that is C++ and
             | mostly Java. I wouldn't dare compare dynamically typed and
             | interpreted languages with Go.. what's the point? I don't
             | have much experience with Rust so I cannot compare it and
             | additionally it's rarely used in companies. Scala I just
             | don't like personally. For my taste it just "is too much of
             | everything".
             | 
             | > ... err != nil ...
             | 
             | In the beginning I was thinking the same. Over time I got
             | used to it. When I write it I use a snippet. And clearly
             | reading the flow of the error has been beneficial to me a
             | lot of times. Yes it is verbose.
             | 
             | > uh, you can have deep inheritance trees in Go, and not
             | have them in Java or C++, I'm not sure what your point is.
             | 
             | I am sure you know that there is no inheritance in Go so I
             | am not totally sure what you are getting at. My point is
             | that I think OOP by composition is a lot clearer than by
             | inheritance. Also composition is not overused in Go as say
             | inheritance is overused in Java.
        
               | shakow wrote:
               | > I am sure you know that there is no inheritance in Go
               | 
               | https://golangdocs.com/inheritance-in-golang
        
               | majewsky wrote:
               | This tries really hard to look like an official resource,
               | but it seems to be run by some random IT consultancy.
               | 
               | What they call inheritance is a narrow syntactic sugar
               | intended to forward method calls to a member without
               | having to write something like                 func (x
               | Outer) Frobnicate(val int) {
               | x.inner.Frobnicate(val)       }
               | 
               | by hand. It's arguably not inheritance because Liskov
               | substitution is not permitted:                 type Outer
               | struct {         Inner       }            func process(i
               | Inner) { ... }            var x Outer       process(x)
               | // ERROR: type mismatch       process(x.Inner) // OK
        
               | thayne wrote:
               | > I think you should compare to languages that have a
               | similar purpose / area of usage. In my experience that is
               | C++ and mostly Java.
               | 
               | The languages I listed first were rust and scala, which
               | serve very similar purposes to c++ and java. In fact,
               | rust is closer to c++ (no GC, more direct memory control)
               | and scala is closer to java (runs on JVM) than go is to
               | either.
               | 
               | > Over time I got used to it. When I write it I use a
               | snippet.
               | 
               | Which is my point. You have to get used to things like
               | this, which probably adds about as much cognitive load as
               | having something in the language to reduce this noise
               | (although I think this is an known problem in go and may
               | be improved in a future version).
               | 
               | > I am sure you know that there is no inheritance in Go
               | 
               | Fine. Replace "inheritance" with struct embedding and/or
               | interface hierarchies, and you can get a similar effect.
               | My point is you can have overly abstracted designs in
               | either language.
        
               | fileeditview wrote:
               | > The languages I listed first were rust and scala, which
               | serve very similar purposes to c++ and java. In fact,
               | rust is closer to c++ (no GC, more direct memory control)
               | and scala is closer to java (runs on JVM) than go is to
               | either.
               | 
               | The overlap in purpose and usage for Go and Java is
               | gigantic. Also I don't understand why it matters whether
               | Scala is closer to Java or whether Rust is closer to C++.
               | We were comparing Go and X right?
               | 
               | This whole argument tree is a bit nonsensical.. I
               | compared Go projects to Java and C++ projects which I
               | have worked on. All of the mentioned languages are very
               | common in companies these days and are used for similar
               | topics. Why bring other languages in to this?
               | 
               | > Which is my point. You have to get used to things like
               | this, which probably adds about as much cognitive load as
               | having something in the language to reduce this noise
               | (although I think this is an known problem in go and may
               | be improved in a future version).
               | 
               | You always have to get used to some quirks in any
               | language out there. It adds a few lines writing the code
               | but reading it is way easier IMHO and not cognitive load.
               | Opinions may of course vary on this.
               | 
               | > Fine. Replace "inheritance" with struct embedding
               | and/or interface hierarchies, and you can get a similar
               | effect. My point is you can have overly abstracted
               | designs in either language.
               | 
               | As I already wrote, embedding is rarely used and was not
               | a problem ever in my experience and yes interface
               | spamming is a problem.
               | 
               | However Java e.g. has these abstraction complexities
               | already baked into the standard library and encourages
               | the overuse of abstractions IMO.
        
         | philosopher1234 wrote:
         | Could you share an example?
        
           | gher-shyu3i wrote:
           | Not the poster you're responding to, but practically every
           | time someone wants to write code that mimics `map` or
           | `filter` and gang is a 5-7 line function (at least).
           | Something that would have been a 1 liner in languages like
           | Java or C#. It gets tiring and distracting very quickly to
           | jump around verbose implementations which amount to nothing
           | more than standard streaming functions.
           | 
           | Another example is the lack of annotations for validations.
           | In Java or C#, you'd annotate the function argument with
           | something like `@Validated`, and it's taken care of. In
           | golang, calling the validation code would have to be done
           | manually each time that's another 3-4 lines (including error
           | handling).
           | 
           | Yet another example is that golang lacks a construct similar
           | to C#'s `Task`. You can't execute a function that returns a
           | value asynchronously (if you care about that value) without
           | creating a channel and passing it.
           | 
           | golang also lacks pattern matching and discriminated unions
           | (aka algebraic data types). Java and C# are getting both
           | (both have pattern matching, and ADTs are in the works as far
           | as I'm aware).
        
             | dullcrisp wrote:
             | You got me excited about Java having pattern matching, but
             | all I could find was a proposal.
        
               | [deleted]
        
               | gher-shyu3i wrote:
               | It's partially there since Java 14: https://docs.oracle.c
               | om/en/java/javase/14/language/pattern-m...
        
             | fileeditview wrote:
             | Have to agree with you. I wished for sum types & pattern
             | matching more than for generics.. but who knows what the
             | future brings.
        
               | hderms wrote:
               | this is why I have moved to Rust. I worked in Go a number
               | of years ago, but after becoming proficient in Scala,
               | I've decided sum types and pattern matching are where the
               | sweet spot is. The JVM has its own issues though and I'd
               | like to have a performance-conscious language in my
               | toolbelt that doesn't sacrifice expressivity. Hence:
               | Rust.
        
               | cy_hauser wrote:
               | RemindMe! 10 Years "Are we there yet?"
        
       | pansa2 wrote:
       | > _Go is not an easy programming language. It is simple in many
       | ways_
       | 
       | This, exactly. It's worth contrasting this with Python, which has
       | a reputation for being simple but is actually extremely complex -
       | but it _is_ easy (at least for small programs).
       | 
       | However, I can't help thinking that the two shouldn't be in
       | opposition. IMO it should be possible for a language to be both
       | simple and easy to use.
        
         | vinger wrote:
         | Meet php.
        
       | pid_0 wrote:
       | Yeah I agree with this. Go is awesome but pretty complicated.
       | Channels still break my brain every time I use them.
        
       | TurboHaskal wrote:
       | The blog post misses the point. Of course writing idiomatic,
       | gotcha-free, production ready code is never easy.
       | 
       | But Go itself is comparatively easy. By far the fastest I have
       | ever onboarded new engineers with, even more so than Python
       | (which is terrible but somehow has a good reputation at being
       | easy and friendly).
        
         | aalhour wrote:
         | What is horrible about Python wrt onboarding new engineers?
         | 
         | In my personal experience, I had an easier time getting
         | onboarded on Python than Golang.
        
           | TurboHaskal wrote:
           | Everything that happens in between development and
           | deployment.
        
       | [deleted]
        
       | taeric wrote:
       | Conversely, I think go can be pushed as an easy language. In
       | large, whether a language is "easy" seems dominated by
       | perception.
       | 
       | That is, most things that are "hard" have a reputation as being
       | hard by non practitioners.
       | 
       | Which isn't to say that empirically it is clear some languages
       | make some errors easier to make.
        
       | iandanforth wrote:
       | Barriers to entry are invisible. They are invisible to people on
       | the inside and most frequently invisible to people who have a
       | hand in creating those barriers.
       | 
       | I once had a conversation with a founder about their signup flow.
       | This founder had aspirations to be a global player. They said
       | that step 2 of their flow included entering a credit card number.
       | I had to stop them. "What about people who don't use credit
       | cards?" They were nonplussed. "You know, China, much of Africa
       | ..." they had never actually thought about whether people had
       | credit cards because every single person they interacted with had
       | one.
       | 
       | Back to languages. If you've never taught an introduction to
       | computer programming for a general audience you are blind to what
       | does and does not matter about a language. You look at things
       | like `foo.bar()` and think "Yeah that's a simple method
       | invocation" and have no idea how many people you just lost.
       | 
       | Never underestimate how much ease of use matters. We, as a
       | community, have selected for decades _against_ people who care
       | about language ergonomics because they get hit in the face with a
       | wall of punctuation filled text and turn away. We fight over
       | where braces belong while not realizing how many hundreds of
       | thousands of people we 've excluded.
       | 
       | Ease of use is _the_ most important part of a language. It 's
       | just that the barriers to entry are invisible and so the people
       | who happen to be well suited to vim vs emacs debates get to talk
       | while the vast majority of potential programmers are left on the
       | outside.
       | 
       | We create barriers to entry accidentally because we design for
       | people like us. To us the barriers are invisible.
        
         | jodrellblank wrote:
         | I don't think you can write that much of a rant using the term
         | "ease of use" without explaining your view on what "ease of
         | use" is; does "use" really only mean a beginner using it for
         | the first time? You mention languages with punctuation, what
         | alternatives do you see instead? You claim "ease of use is THE
         | most important part of a language", but why is it the most
         | important?
        
         | allenu wrote:
         | > Ease of use is the most important part of a language.
         | 
         | It's really a matter of who your target audience is and what
         | they're trying to achieve, though, isn't it? You might make a
         | language easy for the whole world to use, but simultaneously
         | make it hard for specific tasks. Likewise, a language might be
         | easy to use for experts who are trying to achieve a specific
         | task, but difficult for newbies. That's totally okay.
         | 
         | BASIC was super easy to understand and helped me get into
         | programming, but there's no way I would use it for anything
         | serious today.
        
         | onlydeadheroes wrote:
         | I agree completely and I would add that the less mental effort
         | you are spending on your language, the more you are using on
         | your problem. This can have big effects on code and design
         | quality, and allow you to grasp harder problems.
        
         | solosoyokaze wrote:
         | A programming language is a tool. I don't think it should
         | really be optimized or designed around how easy it is to teach
         | someone who is at step zero, basic programming concepts. I _do_
         | think Go is a decent language to teach up and coming
         | professional devs precisely because it doesn't hide the real
         | complexity of what's going on, but I'd probably opt for
         | something a bit higher level as the absolute intro to
         | programming.
        
           | itake wrote:
           | I've been coding for 10+ years in primarily ruby and python.
           | I switched to golang recently for work. While its an
           | interesting language, it takes forever to express what I want
           | the code to do, unlike the previous languages.
           | 
           | Go's simplicity forces developers to introduce insane amounts
           | of complexity.
        
             | solosoyokaze wrote:
             | Ruby and Python are notorious for being difficult to read
             | because of all the foot guns in place. Monkey patching,
             | duck typing, metaprogramming... and that's not even talking
             | about structural limitations like the GIL.
             | 
             | Go is definitely more verbose and less "fun" to write, but
             | it's 10X easier to reason what's going on in a large
             | application.
             | 
             | Of course, it's also the correct kind of solution for some
             | types of problems. If you need a compiled language (many
             | do), the competition to Go isn't Ruby and Python, it's C++
             | and Rust.
        
               | cb321 wrote:
               | Or Nim or Zig or ...
               | 
               | Actually, I feel like Zig may be most in the compiled-
               | but-keep-it-simple-like-C headspace as Go. I don't know
               | either Go or Zig super well. From what I do know, Zig
               | seems not quite as stubborn as Go about forcing code to
               | be "simple to reason about" (which is _also_ subjective,
               | though maybe less so than  "easy to do things").
        
               | dnautics wrote:
               | I would say Zig is interesting in that "safe things are
               | easy and pretty", "unsafe things are difficult and ugly",
               | thus drawing eyes to the code that needs it. It's four
               | lines of nasty looking code for me to convert a slice of
               | bytes to a slice of (f64/f32/i32)... Which the compiler
               | no-ops. This is dangerous because the alignment and byte
               | count must be correct.
        
               | solosoyokaze wrote:
               | I would say both of those are interesting languages, but
               | lack the amazing standard lib of Go and definitely the
               | tooling and industry support.
               | 
               | Personally, I find Swift to be a really great language,
               | but can't deal with the Apple eco-system, XCode, raw
               | Linux support...
        
               | cb321 wrote:
               | Totally agree Re: Swift drawbacks.
               | 
               | Nim's stdlib is actually quite large (like 300 modules).
               | I am sure there are things in Go's that are not in Nim's
               | (like Go's `big`), but I am equally sure there are things
               | in Nim's that are not in Go's (like Nim's `critbits`,
               | `pegs`, etc).
               | 
               | I doubt there is much in Go's stdlib not available in
               | Nim's nimble package system, but OTOH, I am also sure the
               | Go ecosystem is far more vast. I just didn't want people
               | left with the impression Nim's stdlib is as small as
               | Zig's which it is definitely not. Nim has web
               | server/client things and ftp/smtp clients, json and SQL
               | parsers, etc.
        
               | jacobr1 wrote:
               | > If you need a compiled language
               | 
               | I would wager jvm/clr are probably the main competition
               | for Go at least for product/tech companies and enterprise
               | IT.
        
               | solosoyokaze wrote:
               | Definitely for server-side software, but not for binaries
               | that need to be distributed to end users.
        
               | eeZah7Ux wrote:
               | Python is widely considered to be very expressive and
               | very readable.
               | 
               | You can write unreadable code in virtually any language
               | but that's besides the point.
        
               | solosoyokaze wrote:
               | Considered "very readable" by whom? It's pretty well
               | accepted that dynamic/weakly typed languages are more
               | difficult to deal with at scale.
        
               | prionassembly wrote:
               | Python is dynamically but strongly typed. "2+True" is a
               | type error; once something has a type, it has a type.
               | There's no WAT.
        
               | solosoyokaze wrote:
               | Variables can change type though. This is working python
               | code:                 x = 1       y = 2       print(x +
               | y)            x = "no "       y = "types"       print(x +
               | y)
               | 
               | Whereas this will not even compile in Go:
               | x := 1         y := 2         x = "no"         y =
               | "types"
        
               | anaerobicover wrote:
               | That's the difference between static and dynamic typing,
               | not the difference between weak and strong. The _values_
               | cannot be used as if they were another type.
        
               | anaerobicover wrote:
               | > what's the type signature of `print()` then?
               | 
               | Dynamic typing doesn't rule out polymorphism.
               | void print(PyObject objects...)
               | 
               | where `PyObject` is a base type.
        
               | solosoyokaze wrote:
               | So what's the type signature of `print()` then?
               | 
               | I do think my example code demonstrates why Go is easier
               | to reason at scale than Python. Python is conflating
               | assignment and type declaration. Go has = and :=, so it's
               | crystal clear what's going on.
        
               | anaerobicover wrote:
               | > what's the type signature of `print()` then?
               | 
               | Dynamic typing doesn't rule out polymorphism.
               | void print(PyObject objects...)
               | 
               | where `PyObject` is a base type.
               | 
               | Additionally, you could perfectly well have constants, or
               | require variables to have a fixed type, in a dynamic
               | language. You would just pay a cost at runtime to check
               | the type on assignment.
        
               | solosoyokaze wrote:
               | I would argue that you pay the cost at runtime but you
               | also pay a cognitive overhead cost while writing in a
               | dynamically typed language. Refactoring in particular is
               | a lot more difficult.
        
         | joshspankit wrote:
         | > Back to languages. If you've never taught an introduction to
         | computer programming for a general audience you are blind to
         | what does and does not matter about a language. You look at
         | things like `foo.bar()` and think "Yeah that's a simple method
         | invocation" and have no idea how many people you just lost.
         | 
         | I keenly remember in college when they were first starting to
         | teach me C++ and I asked something like "Ok: I hear what you're
         | saying that this is a function, and that's a parameter, but my
         | question is _how does the computer know that you've named it
         | [whatever the variable name was]_?
         | 
         | The teacher had no understanding that this was a conceptual
         | barrier.
         | 
         | Of course now I know that the answer is "because order of the
         | syntax tells it so", but stuff like that made those classes
         | much harder than they needed to be.
        
         | cannabis_sam wrote:
         | > Barriers to entry are invisible. They are invisible to people
         | on the inside and most frequently invisible to people who have
         | a hand in creating those barriers.
         | 
         | I just want to say that I really, really like this phrasing.
         | 
         | I have a lot of opinions on how programming languages could be
         | improved, and however much I disagree with Rob Pike on types, I
         | still think Go hit a real sweet spot.
        
         | numlock86 wrote:
         | > "What about people who don't use credit cards?"
         | 
         | > "You know, China, much of Africa ..."
         | 
         | Oh, you don't even have to go that far. Of all the people I
         | know maybe 1 out of 10 has a credit card. The irony in regard
         | to your post: Most of them got one because "it was needed for
         | something online" at some point. This is in Germany.
        
         | sangnoir wrote:
         | Programming has and almost infinite _depth_ to it, depending on
         | the complexity you are tackling /modeling. In a way, it is like
         | mathematics in the way you have layers acting as
         | foundations/abstractions for more complex representation.
         | Introduction to mathematics is counting discrete things, then
         | the number line, addition, which is a foundation for/abstracted
         | by multiplication, which is in turn a foundation for/abstracted
         | by exponents, etc. and in no time, you're at the level of
         | multi-variate calculus and n-dimensional hypercubes.
         | 
         | Each level requires notation that is concise (so it can fit in
         | short-term memory to be actually useful) - one cannot expect
         | graduate-level mathematics to share the same notation/symbols
         | as beginner (elementary) maths.
         | 
         | Programming languages have to trade off which level they are
         | biased towards, I do not believe in a universal language that
         | is both easy for beginners while being concise for advanced
         | users.
         | 
         | Back on topic - Go is not an _easy_ language, it is a _simple_
         | language, those 2 things are not the same.
        
           | Zababa wrote:
           | > Back on topic - Go is not an easy language, it is a simple
           | language, those 2 things are not the same.
           | 
           | I often see this point here, but I always wonder what people
           | mean by it. Could you elaborate on that point?
        
             | epicide wrote:
             | Not OP, but I'd be happy to try and differentiate, as well.
             | 
             | Simple means uncomplicated, not a lot of pieces. A violin
             | is arguably simpler than a guitar because of a lack of
             | frets.
             | 
             | Easy means it is not difficult. A guitar is arguably easier
             | than violin [0] because it has frets.
             | 
             | It's important to remember that "easy" is very subjective.
             | What is easy for one person might be insurmountable to
             | another.
             | 
             | tl;dr "simple" usually means "easy to understand" and
             | "easy" usually means "easy to do". Both inherently assume
             | some amount of prior knowledge or skill, so neither is
             | entirely universal or objective.
             | 
             | [0]: I'm not trying to say one instrument is _better_ or
             | disparage and guitarists. It 's an example.
        
             | steveklabnik wrote:
             | The primary source for this is
             | https://www.infoq.com/presentations/Simple-Made-Easy/
             | 
             | (I am not entirely sure I agree with its thesis or its
             | applicability to Go, but since nobody had actually linked
             | you directly to the concept, I thought it would be
             | worthwhile to do so.)
        
             | tyingq wrote:
             | I think the author's example is pretty good.
             | 
             |  _" How do you remove an item from an array in Ruby?
             | list.delete_at(i)...Pretty easy, yeah?"_
             | 
             |  _" In Go it's ... less easy; to remove the index i you
             | need to do:"_                 list = append(list[:i],
             | list[i+1:]...)
             | 
             | So Go is simple in that it doesn't have shortcut functions
             | for things. There's generally one way to do things, which
             | is simple. But it's not easy, because it's certainly not
             | intuitive that "append" is the way to remove an array
             | element.
        
             | sangnoir wrote:
             | To me, what makes Go simple language is limited set of
             | building blocks, and being very opinionated - this mostly
             | relates to syntax and what the core language provides out
             | of the box. With Go, you only need to understand looping,
             | branching, channels, slices and you're mostly good to go.
             | 
             | Ease is measured by how a language may be used to solve a
             | problem. As an example, text-wrangling with Perl is _easy_
             | - but you may have to do it using complex (i.e. not simple)
             | representation.
             | 
             | Back to Go - channels are a _simple_ concept, but are not
             | (always) _easy_ to work with if you do not put a lot of
             | thought into your concurrency model.
             | 
             | Edit: I just thought of another way to express the
             | difference between "simple" and "easy". The notation of
             | adding "1+1=2" is _simple_ , however _proving_ that
             | "1+1=2" is _not easy_ (at least at the level of elementary
             | students).
        
           | kstenerud wrote:
           | Go isn't even simple. They tried to be simple, but mostly
           | managed to only move the complexity around, or foist it on to
           | the user's shoulders by not providing a layered, composeable
           | API in the standard library.
           | 
           | There are some kinds of complexity you can't eliminate; only
           | contain through careful, thoughtful architecture.
           | Unfortunately, people often confuse this with the
           | eliminatable kind, and end up making a bigger and bigger mess
           | as they push things around.
        
             | t0astbread wrote:
             | What do you mean by "layered, composeable API"? Go does
             | provide abstractions for common use cases in its standard
             | library (`io.ReadAll` for example) and there are interfaces
             | to connect different parts of the standard library
             | (`io.Reader`/`Writer`, `fs.FS` etc.).
        
           | epicide wrote:
           | > Go is not an easy language, it is a simple language, those
           | 2 things are not the same.
           | 
           | This distinction is fundamental to any sort of design, but is
           | unfortunately lost on a _lot_ of people (especially
           | developers, in my experience). Easy and simple are near-
           | universally conflated.
           | 
           | > I do not believe in a universal language that is both easy
           | for beginners while being concise for advanced users.
           | 
           | Exactly. I really want some languages to be more _intuitive_
           | and easy to pick up for non-programmers. The average person
           | (whatever that means) does not have the first clue about the
           | machines that run so many parts of their lives.
           | 
           | At the same time, such a language would likely be a bad fit
           | for most professional software development. That hardly means
           | it's without value.
           | 
           | I know languages like that exist, but they're often aimed at
           | absolute beginners and are treated like toys. There doesn't
           | seem to be much middle ground or "transition languages".
           | 
           | Insert joke about how $LANGUAGE_YOU_DISLIKE is a toy.
        
             | jacobr1 wrote:
             | It seems like Basic has been the closest we've come on a
             | general purpose language with "easy to pick up features"
             | (at least in widespread use)?
        
             | msiemens wrote:
             | > Easy and simple are near-universally conflated.
             | 
             | That's an interesting statement as it doesn't always work
             | in other languages. In German (my native language) my first
             | instinct was to translate both words as "einfach" which
             | contains both concepts. In fact, in my online dictionary of
             | choice the word "einfach" is the first entry for both
             | "easy" and "simple". So if Germans conflate these two it
             | might be because of the language they speak :) But more to
             | the point, I'm wondering how universal the distinction
             | between easy and simple is when other languages cannot
             | express that distinction as easily as in English.
        
             | malloci wrote:
             | > The average person (whatever that means) does not have
             | the first clue about the machines that run so many parts of
             | their lives.
             | 
             | Sadly, I would argue that this is also true of many
             | developers.
        
             | jacobajit wrote:
             | I mean, Python is the de facto beginner language and also
             | used for professional software development (and of course
             | intermediate data science work). Are you suggesting this is
             | an unwise or unstable equilibrium?
        
               | epicide wrote:
               | I'm glad you bring that up. I think Python is the closest
               | we have to a "universal language" (even so, it still has
               | some limitations).
               | 
               | I think it works well for beginners because the language
               | itself is so consistent and they have put a lot of effort
               | into avoiding corner cases and "gotchas". And I think it
               | works for professional uses because of third party
               | library support.
               | 
               | To answer your question: I'm not suggesting that at all.
               | I'm honestly not entirely sure how Python balances it
               | seemingly so well. Given the lack of focus in the
               | industry towards "intermediate" programmers and use
               | cases, my slight fear is that Python will be shoehorned
               | into one direction or the other.
               | 
               | Even if the language itself isn't, it does feel like the
               | use-case-complexity gap is growing exponentially, at
               | times.
               | 
               | And not just with Python. Seemingly, you're either a
               | complete beginner learning conditionals and for-loops or
               | you're scaling out a multi-region platform serving
               | millions of users with many 9's of uptime.
        
         | est wrote:
         | > You know, China
         | 
         | Another thing I find very interesting is Go is very popular in
         | China
         | 
         | https://blog.jetbrains.com/go/2021/02/03/the-state-of-go/
         | 
         | the US ranked #7, it barely had more devs than say, Belarus.
        
           | edoceo wrote:
           | But the metric was credit cards in China
        
       | dm03514 wrote:
       | I love go, but I think it is a straight up dangerous language. I
       | believe this because:
       | 
       | - The tiny standard library and low learning curve make it quite
       | easy to get started.
       | 
       | - Concurrency is a first class citizen, through goroutines and
       | channels
       | 
       | - The performance, combined with the low barrier to entry,
       | combined with its popularity make it very desirable for companies
       | 
       | In my experiences this means a lot of companies without deep
       | concurrent programming experience begin to adopt it. In my
       | experiences this means concurrent programming errors....literally
       | EVERYWHERE. I've seen (and created these of course) in every
       | company I've been at and in huge open source go projects.
       | 
       | I wish go would address this through first class tooling or type
       | supported in the language like rust.
       | 
       | I have a hueristic based approach that I've had success with when
       | writing and reviewing code, but writing a static analysis tool
       | for it is a bit out of my experience:
       | 
       | https://medium.com/dm03514-tech-blog/golang-candidates-and-c...
        
         | candiddevmike wrote:
         | Can you expand on the types of errors you're seeing? Go has a
         | fairly powerful race detector you can run as part of your test
         | suite that seems to catch everything I've thrown at it.
        
           | dm03514 wrote:
           | Me too! I generally think that the race detector is the best
           | approach right now.
           | 
           | At a prior job we had a lot of code that wasn't structured in
           | a way that made it easy to exercise using the race detector.
           | This was combined with misunderstanding about what the race
           | detector did and didn't do. For example there was one team
           | that ran `-race` on non-concurrent code, and expected it to
           | verify race conditions.
        
       | captainbland wrote:
       | I'd say that Go in its current form is easy but tedious.
       | Concurrency has some ways to shoot yourself in the foot but
       | that's pretty much par for the course. The good news is it's
       | going to become a lot less tedious if a little bit less easy with
       | the introduction of generics.
        
       | fishywang wrote:
       | There's so much wrong with the second example (it works, yes, but
       | for all the wrong reasons) I don't even know where to start.
       | 
       | Using the buffer size of a channel to control number of
       | concurrent jobs is just the wrong approach. It's so much easier
       | and cleaner to just use the number of goroutines for that:
       | const workers = 3         const jobs = 20
       | jobsChan := make(chan int)         var wg sync.WaitGroup
       | for i := 0; i < workers; i++ {           wg.Add(1)           go
       | func() {             defer wg.Done()             for work :=
       | range jobsChan {               time.Sleep(time.Second)
       | fmt.Println(work)             }           }()         }
       | for i := 1; i <= jobs; i++ {           jobsChan <- i         }
       | close(jobsChan)         wg.Wait()         fmt.Println("done")
       | 
       | One thing about channels in go is that the only time you want to
       | use a buffered channel is the time that you know exactly how many
       | writes (n) you'll have to the channel, while n is finite and
       | reasonably small, so you create a channel with buffer size n to
       | unblock all the writes. An example to that is that if you want to
       | strictly enforce a timeout to a blocking call:
       | const timeout = time.Millisecond*10         ctx, cancel :=
       | context.WithTimeout(context.Background(), timeout)         defer
       | cancel()         resultChan := make(chan resultType, 1)
       | go func() {           result, err := myBlockCall(ctx)
       | resultChan <- resultType{result, err}         }()         select
       | {         case <- ctx.Done():           return nil, ctx.Err()
       | case r := <- resultChan:           return r.result, r.err
       | }
       | 
       | If you are using a buffered channel in other cases, you are
       | likely doing it wrong.
        
       | tptacek wrote:
       | I don't think that the claim has ever been that Go is especially
       | easy to program in; it's about as good as any other mainstream
       | language for expressing logic. It's that Go is simple enough that
       | the language gets out of your way really quickly.
       | 
       | I think the article kind of got the idea midway through, at "I
       | think most programmers can figure out what the above does even
       | without prior Go experience". Yes. That's the idea.
        
       | hombre_fatal wrote:
       | I think anyone can agree with the author that Go definitely lacks
       | conveniences you're used to in other modern languages.
       | 
       | I just think we also tend to dramatize how much that matters.
       | 
       | I also think Go's benefit really is that it's simple and that you
       | have very few tools that let you do anything other than focusing
       | on solving your problem, and like the author I do think
       | goroutines are the exception to that.
       | 
       | Where I work, we don't even use them. We use plain ol' mutexes
       | instead.
       | 
       | When coming from higher level languages, Go does feel
       | frustrating. In Javascript, you're writing `const [a, b, c] =
       | await Promise.all([x(), y(), z()])`. In Go, you're writing 40
       | lines of WaitGroup code. It's easy to go ughhh.
       | 
       | But I think a nice way to appreciate Go's conservative
       | middleground is to go back to writing some C/C++ code for a
       | while, like some Arduino projects. Coming from that direction, Go
       | feels like a nice incremental improvement that doesn't try to do
       | too much (with perhaps the exception of goroutines).
       | 
       | Go's performance is also particularly stand-out which makes up
       | for many of its convenience shortcomings. It's fast enough that
       | I've written some code in Go where I would have written C not
       | long ago. And writing C involves quite a bit more concessions
       | than what Go gives you, so in comparison, Go kinda spoils you.
       | 
       | Go has plenty of annoyances too though. Not having any dev vs
       | prod build distinction is annoying. Giving maps a runtime penalty
       | of random iteration in order just to punish devs who don't read
       | docs is annoying. It's annoying to have crappy implementations of
       | things like html templating in the stdlib which steal thunder
       | from better 3rd party libs. Not being able to shadow vars is
       | annoying. `val, err :=` vs `val, err =` is annoying when it
       | swivels on whether `err` has been assigned yet or not, a footgun
       | related to the inability to shadow vars. etc. etc.
       | 
       | But it's too easy to overdramatize things.
        
         | arp242 wrote:
         | I don't think this article really overdramatizes things; if I
         | had written "zomg Go is terrible look what I need to write to
         | delete an item from a list!" - a sentiment I've seen expressed
         | a few times over the years - then, yeah, sure.
         | 
         | But that's not really what the article says. As it mentions, I
         | don't think these are insurmountable issues, and I still like
         | and use Go: it's pretty much my go-to language these days.
         | Overall, I think it did a lot of things right. But that doesn't
         | mean it's perfect or can't be improved.
        
           | hombre_fatal wrote:
           | I used "dramatize" to describe the blog post (in such that
           | the blog post was written) and then "overdramatize" at the
           | end in reference to my own infinite list of annoyances with
           | Go and the normal course of discussion on HN about proglangs.
           | 
           | The blog post does admit "Are these insurmountable problems?
           | No." Learning it in 5 minutes certainly is a stretch as you
           | point out, but I'd still say you can learn it in a weekend to
           | a greater degree than any other language I use.
           | 
           | Though you'll notice my comment after the first sentences is
           | a way to share my own thoughts on Go, an opportunity I'm
           | never going to neglect.
        
           | ganafagol wrote:
           | The curse of having landed on the HN front page. You write a
           | little blog post with some random thoughts, somebody found it
           | insightful and posts it to HN and if more people like it, it
           | ends up on the front page. That exposure tends to attach a
           | wide-ranging seriousness to this kind of blog post which may
           | dramatically overinflate the point that it originally
           | intended to make.
        
         | simiones wrote:
         | > I also think Go's benefit really is that it's simple and that
         | you have very few tools that let you do anything other than
         | focusing on solving your problem
         | 
         | I feel just the opposite: Go has very few tools, which forces
         | you to often have to solve language games instead of focusing
         | on your problem.
         | 
         | You want to remove an element from a list? Instead of writing
         | that, you need to iterate through the list and check if the
         | element at position i has the properties you want, and if it
         | does, copy the list from i+1 (if it wasn't the last!) over the
         | original list. This is NOT the problem you were trying to
         | solve.
         | 
         | You want to store a map from some struct to another? Instead of
         | writing that, you have to create a key struct that is
         | comparable for equality from your original struct, which
         | probably involves string concatenation and great care to avoid
         | accidentally making unequal structs have equal keys; and then
         | two maps, one from keys to the key struct and another from keys
         | to the value struct, and of course client code also has to know
         | about this explicitly.
         | 
         | You want to pass around some large structs, or iterate over
         | slices of them? Well, you'd better start using pointers, and
         | start being careful about what is a copy and what isn't,
         | otherwise you'll pay a steep performance price, without any
         | syntactic indication.
         | 
         | You want to work with sets of values? You'll have to store them
         | as map keys, perhaps doing all of the black voodoo described
         | above. And of course, you can't do a reunion of two sets, you
         | have to iterate through the keys of the first map and the
         | second map and add them to a third map.
         | 
         | All of these things are annoying when you are writing them, and
         | even more of a problem when you are reading the code, as you
         | have to understand the intent from the messy internals that Go
         | forces you to expose.
        
         | nemo1618 wrote:
         | > Giving maps a runtime penalty of random iteration in order
         | just to punish devs who don't read docs is annoying
         | 
         | Can you explain what you mean by "runtime penalty?" Last I
         | checked, the iteration is only random wrt its starting offset;
         | after that, it proceeds linearly through the buckets. So you
         | only generate one random integer per `range`, which seems
         | acceptably cheap.
        
           | scaladev wrote:
           | It still seems to be working as you described.
           | 
           | https://github.com/golang/go/blob/db8142fb8631df3ee56983cbc1.
           | ..
        
         | grey-area wrote:
         | Great summary of areas where Go could be improved for real
         | world use. I'd want any of these improvements over delete at
         | index in a slice (which I've never wanted).
        
         | lmm wrote:
         | But why would you be comparing to C? I've yet to find any
         | scenario where Go is an option and e.g. OCaml isn't (unless the
         | scenario is "I want to use a language whose creator called it a
         | "systems language"").
        
         | throwaway189262 wrote:
         | I frequently describe Go as "C, with memory safety and GC". I
         | don't run into many that disagree.
         | 
         | Go is basically how Java was pre-generics. People forget that
         | early Java was much like Go in some respects. Everything casted
         | to object. Not really sure what the object is unless you wrote
         | the code. Lack of common patterns and a rich set of libraries
         | (Guava, Apache Commons).
         | 
         | Early Java was supposed to be safe C++. Converting C++ to Java
         | is still trivial unless there's pointer craziness. Generally
         | copy paste the C++ and change naming conventions.
         | 
         | I hate working on unfamiliar Go projects. Copy pasted code
         | everywhere. And everyone builds their own abstractions because
         | there aren't enough.
         | 
         | I don't understand Go's appeal. To me it feels clunky and stuck
         | in the 90's. Designed by C programmers for C programmers.
         | 
         | Like C it has no generics, bad packaging, and is a pain in the
         | ass to cross compile. Its only killer feature, IMO, is it's
         | fiber threading model and that's quickly being copied by other
         | languages, even ancient Java.
        
           | sofixa wrote:
           | > Like C it has no generics, bad packaging, and is a pain in
           | the ass to cross compile.
           | 
           | Go modules are pretty good packaging IMHO. What don't you
           | like about them?
           | 
           | As for cross-compiling, are you joking? It's very easy and i
           | literally cannot imagine it being easier. What's painful
           | about it?
        
             | simiones wrote:
             | I would say Go has very poor packaging, but this is when
             | comparing it with Java (maven), Rust etc. It's definitely
             | much better than C.
             | 
             | The biggest reason why Go modules are a very poor packaging
             | solution is the horrendous stupidity of tying them to
             | source control. This makes it difficult to develop multiple
             | modules in the same repo, it requires your source control
             | system to know Go, it makes internal refactors a problem
             | for all consumers of your code (e.g. if you are moving from
             | github to gitlab, all code consuming your package will have
             | to change their imports).
             | 
             | The versioning ideas of their module system, particularly
             | around the v2+, have made it such a pain that there still
             | isn't a single popular package that uses the proposed
             | solution so far, not even Google ones like the Go protobuf
             | bindings.
        
               | sofixa wrote:
               | > The biggest reason why Go modules are a very poor
               | packaging solution is the horrendous stupidity of tying
               | them to source control
               | 
               | I think it's actually a great idea. It uses native things
               | you use anyway, avoids an unnecessary third party ( a
               | package repository like pypi), and adds in extra security
               | ( remember those hijacked pypi credentials and pypi-only
               | malicious packages that weren't in source control, so
               | hard to verify?).
               | 
               | > This makes it difficult to develop multiple modules in
               | the same repo
               | 
               | How so? You can justs use different folders and packages.
               | 
               | > requires your source control system to know Go
               | 
               | Not really.
               | 
               | > it makes internal refactors a problem for all consumers
               | of your code
               | 
               | That's always the case - regardless of your package
               | repository, if you change it, you have to update the new
               | references everywhere. You can use go.mod to replace a
               | reference ( e.g. use gitlab.com/me/library instead of
               | github.com/legacy/library) without updating all the code.
               | And then there's the goproxy protocol, which makes all of
               | this optional.
               | 
               | I agree on v2 modules, that is very weird.
        
             | zimpenfish wrote:
             | > It's very easy and i literally cannot imagine it being
             | easier.
             | 
             | `go build -target x.y` could be considered fractionally
             | easier than `GOOS=x GOARCH=y go build`? But yeah, this is a
             | nonsense claim by GP.
        
           | solatic wrote:
           | > I hate working on unfamiliar Go projects. Copy pasted code
           | everywhere. And everyone builds their own abstractions
           | because there aren't enough.
           | 
           | Arguably, if the project requires lots of abstractions and
           | copy-pasted code, then the project shouldn't be written in
           | Go. Just because you can, technically speaking, write object-
           | oriented code in Go does not mean that it is ergonomic to do
           | so or a recommended use-case for the language. Projects that
           | benefit from object-oriented design should stick to languages
           | with first-class support for it.
        
             | cy_hauser wrote:
             | I don't disagree with your conclusion but I don't see it
             | following from the quoted premise. What was your thought
             | chain to get from the quoted text to OO stuff?
        
               | solatic wrote:
               | There's a difference between using a struct to represent
               | concepts like program configuration or grouped function
               | parameters, and shoehorning OO-native design concepts
               | like the decorator pattern that need inheritance into a
               | language that doesn't support dynamic dispatch. Much of
               | the bad Go code I see comes, yes, from overusing
               | abstractions and lots of copy-pasting, but most of _that_
               | comes from trying to fit square pegs like OO design into
               | Go 's round hole.
        
               | cy_hauser wrote:
               | Still don't get the connection in the way you do. Maybe
               | I'm so used to OO stuff that I just gloss over it.
               | 
               | > a language that doesn't support dynamic dispatch.
               | 
               | Go's interfaces do support this enough that I consider it
               | worth at least mentioning.
               | 
               | https://en.wikipedia.org/wiki/Dynamic_dispatch#Go_and_Rus
               | t_i...
        
           | Tobu wrote:
           | In fact, Go is not even memory safe despite the GC. It has
           | data races, which are exploitable.                 -
           | https://blog.stalkr.net/2015/04/golang-data-races-to-break-
           | memory-safety.html       -
           | https://golang.org/doc/articles/race_detector#Introduction
        
           | otabdeveloper4 wrote:
           | > Converting C++ to Java is still trivial unless there's
           | pointer craziness. Generally copy paste the C++ and change
           | naming conventions.
           | 
           | What? You've obviously never seen actual C++ code to make
           | such a ridiculous statement.
        
             | pjmlp wrote:
             | 1996 C++ (when Java appeared) being used in MFC, OWL,
             | Powerplant and CSet++, alongside Smalltalk in the gang of
             | four book, definitely.
        
             | nvarsj wrote:
             | Google C++ looks a lot like Java, maybe that's where they
             | are coming from. But it's still quite a stretch!
        
               | otabdeveloper4 wrote:
               | As they say, "you can write FORTRAN in any language".
        
         | strken wrote:
         | Go's lack of generics and metaprogramming can be extremely
         | frustrating when you're working with services that copy data
         | around from one struct to another, which ends up being a lot of
         | them. You're left with reflection, code generation, or doing it
         | field by field.
         | 
         | It'd be nice to have syntactic sugar for splatting structs
         | together, or a library that let you do it in a typesafe and
         | efficient way.
         | 
         | EDIT: I will defend the random map order, though. Golang maps
         | were only predictable up until a set number of items, which
         | caused hard-to-detect bugs where your small test case would be
         | in order but your large real-life data would be out of order.
        
           | hombre_fatal wrote:
           | I certainly agree. I would love for Go to get to a place
           | where I never have to copy and paste code again. Generics
           | will get us part way there.
           | 
           | The next item on my wishlist are algebraic data types. In
           | complex projects I've opted for Rust over Go just because of
           | the abstraction power of being able to represent things like:
           | Dest = Register (A | B | C)         Src = Register (A | B |
           | C) | Immediate value         OpCode = Nop | Add(Dest, Src) |
           | ...
           | 
           | In Go, I'm so tired of `interface{}`.
           | 
           | Edit: To respond to your edit about map iter order, that's
           | related to my complaint of Go's lack of dev vs release build.
           | Randomizing map iter order in dev builds is acceptable to me.
           | Go makes you pay for that in prod.
        
             | yuribro wrote:
             | I don't understand the jump from the lack of algebraic
             | types, to using interface{}.
             | 
             | You can spell out all your types into explicit structs
             | (where you can choose between a simple implementation with
             | a little worse performance or to make the implementation a
             | little more complex) and pass them around.
             | 
             | There are many complex code bases (kernels & databases come
             | to mind) which use C (not C++), and they don't resort to
             | passing void* around the "business logic" parts.
             | 
             | The idea that for a complex project you would choose a
             | different language with so many different characteristics
             | due to a minor detail about the type system (this is not
             | exactly Haskell vs JS...). This kind of decision making
             | would not pass in any reasonable organization...
        
               | kaoD wrote:
               | I don't know Go. How can you emulate sum types on it? And
               | how do you pattern match them?
        
               | simiones wrote:
               | It depends on what you mean exactly by "emulating sum
               | types" and "pattern matching them".
               | 
               | An example is rolling your own discriminated union:
               | type Avariant struct {         a int       }       type
               | Bvariant struct {         b string       }       type
               | SumChoice int       const (         SumA SumChoice = 0
               | SumB SumChoice = 1       )       type Sum struct {
               | type SumChoice         A *Avariant         B *Bvariant
               | }            sumA := Sum{choice: SumA, A: &{a: 7} }
               | sumB := Sum{choice: SumB, B: &{b: "abc"} }
               | func foo (x Sum) {         switch(x.choice) {
               | case SumA: {             fmt.Printf("Value is %d", x.A.a)
               | }           case SumB: {
               | fmt.Printf("Value is \"%s\"", x.B.b)           }
               | }       }
               | 
               | As usual with Go, it's ugly and verbose and error prone,
               | but it just about works.
               | 
               | The previous poster was probably thinking of something
               | similar but using interface{} (equivalent to Object or
               | void*) instead of Sum, and then pattern matching using an
               | explicit type switch:                 func foo (x
               | interface{}) {         switch actual := x.(type) {
               | case AVariant: {             fmt.Printf("Value is %d",
               | actual.a)           }           case BVariant: {
               | fmt.Printf("Value is \"%s\"", actual.b)           }
               | }       }
               | 
               | This is slightly less risky and has less ceremony, but
               | it's also less clear which types foo() supports based on
               | its signature. Since Go has only structural subtyping [0]
               | for interfaces, you would have to add more boilerplate to
               | use a marker interface instead.
               | 
               | [0] In Go, a type implements an interface if it has
               | functions that match (name and signature) the interface
               | functions. The name of the interface is actually
               | irrelevant: any two interfaces with the same functions
               | are equal. So if you declare an interface MyInterface
               | with no methods, it is perfectly equivalent to the built-
               | in `interface{}`: any type implements this interface, and
               | any function which takes a MyInterface value can be
               | called with any type in the program.
        
               | kaoD wrote:
               | Thanks for the write up effort.
               | 
               | On the first approach, with "error prone" you mean the
               | tag could be incorrect, right? (or even have an
               | impossible variant with 0 or multiple variants set).
        
               | simiones wrote:
               | Yup. Also, there is no exhaustiveness checking (the
               | constants we chose to define for SumChoice(0) and
               | SumChoice(1) mean nothing to the compiler anyway -
               | exhaustiveness checking would have you test any possible
               | int, since SumChoice is simply an alias for int).
        
               | BenFrantzDale wrote:
               | But are those two variants held in memory inside Sum or
               | are they heap-allocated sitting out on some other cache
               | line? Can one write a performant Sum type?
        
               | simiones wrote:
               | With pointers, they are in the heap, but at least there
               | is no overhead. You could also declare them embedded
               | (without the *s), but then the Sum struct would have a
               | size that is a sum of all the variants' sizes, instead of
               | a proper union which should have the max size (more or
               | less).                 type Sum struct {         type
               | SumChoice         A Avariant         B Bvariant       }
               | 
               | This is what I meant by saying that it depends on exactly
               | what you mean by "sum types".
        
               | BenFrantzDale wrote:
               | Got it. Although I'm not sure what "no overhead" means if
               | the instances have to live way far away on the heap. That
               | means you've got an alloc (including a mutex lock), then
               | the value lives far away, then the garbage-collection
               | overhead then the delete. When I think sum-type I think a
               | flag plus storage for the maximum size and alignment
               | type, and all of the above bookkeeping and cache misses
               | go away.
        
               | simiones wrote:
               | Yes, you're right - I was thinking of "no space
               | overhead", and even that is not entirely correct (you pay
               | an extra pointer per variant size, which in this case
               | would be some overhead over the embedded version).
               | 
               | Still, I think most people don't worry so much about
               | efficient packing of sum types, and instead the safety
               | and convenience factors are more important. Of course,
               | YMMV based on exact usage.
               | 
               | I'm not in anyway claiming that Go supports sum types.
               | Someone just asked how they may be emulated, and I don't
               | think it should be surprising that emulation has an
               | overhead.
        
               | kuschku wrote:
               | > (this is not exactly Haskell vs JS...
               | 
               | No, it's more like Haskell vs C, considering the
               | expressiveness of Rust's vs Go's type system.
        
         | xpressvideoz wrote:
         | I agree with that. Go can be frustrating if you expect the
         | level of expressiveness of Python, Java, or Rust. But if you
         | regard it as a better C, you can appreciate its ergonomic
         | upgrades compared to C. What is unfortunate is that Go could be
         | much more than just a better C, considering Google's potential
         | and the decades of programming language research that were
         | present at the inception of Go. By trying to simplify things
         | too hard, and ignoring precedents, Go missed a huge opportunity
         | cost. And it even failed to fulfill its original intent: it
         | cannot be used in place of C, unlike Rust.
        
           | hombre_fatal wrote:
           | Agreed. I would love for Go to have stolen more pages from
           | Rust's crusade for zero-cost abstractions and high level
           | conveniences. I can appreciate ruthless conservatism in some
           | places, but Go isn't actually ruthlessly conservative. It has
           | quite a lot of weird things in its stdlib.
        
           | systemvoltage wrote:
           | Could you please expand on why is Go not a replacement for
           | Rust? Is that because of the lack of manual memory
           | management?
        
             | hombre_fatal wrote:
             | The obvious case is embedded programming. You can run Go
             | via a custom compiler for microcontrollers:
             | https://tinygo.org/.
             | 
             | But to their point: why incur that sort of overhead for a
             | language that doesn't necessarily give you enough
             | conveniences to be worth it? Or rather, Rust gives you more
             | conveniences with very little runtime overhead.
             | 
             | It's not exactly damning to be unable to compete with C on
             | a microcontroller with 2K RAM, they were just pointing out
             | the end of my comparison with C/C++.
        
             | xpressvideoz wrote:
             | What I wanted to say was that Go was not a replacement for
             | C, compared to Rust which could have similar performance
             | characteristics as C or C++. The lack of manual memory
             | management is one thing, but there are cultural reasons
             | too. For example, in Go it is very idiomatic to return an
             | error value as a string, which cannot be even imagined of
             | in the C, C++, or Rust ecosystem. That's because as Go is
             | targetted toward backend-level programming nowadays (which
             | was different at the beginning of Go, when Go was marketted
             | as a systems language), some minor overhead is acceptable
             | if the convenience it brings is worth it. Go is full of
             | such compromises, which isn't exactly a bad thing, but
             | unsuitable to the lowest level programming which begs for
             | an extremist position regarding performance issues. The
             | mentality of Go is good enough for conventional
             | programming, but bad for systems programming. And I think
             | such cultural differences stem from Go's simplicity first
             | mindset.
        
         | pjmlp wrote:
         | > But I think a nice way to appreciate Go's conservative
         | middleground is to go back to writing some C/C++ code for a
         | while, like some Arduino projects. Coming from that direction,
         | Go feels like a nice incremental improvement that doesn't try
         | to do too much (with perhaps the exception of goroutines).
         | 
         | As someone that was writing Arduino like code in C++ in 1993
         | (MS-DOS), I fail to see that.
        
           | BenFrantzDale wrote:
           | If in 2021 you think of "C/C++" as a single language, you are
           | missing out on the last decade of C++.
        
             | pjmlp wrote:
             | C/C++ isn't a programming language, rather the abreviation
             | for C _and_ C++.
             | 
             | I am fully aware of the last decade of C++, including
             | papers written by Bjarne Stroustrup and other C++ key
             | figures, where they uses the hated C/C++ expression, that
             | so many happen to have issues with.
             | 
             | Which I can gladly point you to.
             | 
             | Given that security is one of my areas, I always keep up to
             | date with C and C++ standards, even if I don't use them
             | every day.
        
               | c-cube wrote:
               | > C/C++ isn't a programming language, rather the
               | abreviation for C and C++.
               | 
               | Maybe the abbreviation should be `C & C++` then :-). But
               | I'm not sure if infix precedences allow it to work.
        
       | q845712 wrote:
       | Agree, agree, agree. I've bounced between Python, C++, and Rails
       | in my career, and lately have been ramping up at a Go shop. Go is
       | like C++ in the sense I can tell that eventually I'll get better
       | at eyeballing a file, sensing the shape of the code, and zeroing
       | in on the important bits.
       | 
       | Just like learning to read large C++ codebases, there's clearly a
       | learning curve because a simple idea that would be a one-liner in
       | a high level language is going to be 5-15 lines.
       | 
       | Go also makes the "learn to read" learning curve even steeper
       | when folks pick up and propagate the core team's propensity for
       | single letter variables. I feel like one of the understated
       | benefits of ruby/python/similar languages being relatively
       | readable is that folks are inclined to keep them readable.
       | Whereas when you feel like you're one step away from specifying
       | the register and how hot the welding torch should be to toggle
       | the bits, you're somehow more likely to write
       | `c.T.Client.Send(v)`.
       | 
       | IMO if we accept that most of our work is maintenance &
       | modification of existing code, and most of the work in those
       | tasks is understanding what's already been written, it follows
       | immediately that code that's easy to read has high business
       | value. I know it's too soon in my learning curve, but I'm really
       | hoping I find the patterns that make Go easy to read soon. (and a
       | little tangentially but the descriptive power of a variable name
       | should be proportional to its scope - so imo anything exported or
       | that I might see across multiple files is an awfully strong
       | contender for a multi-word descriptive name.)
        
         | thedevelopnik wrote:
         | The Go team's recommendation (from Effective Go) is exactly
         | what you're suggesting. One letter variables should only be
         | used in short functions, a couple lines away from where they're
         | created. The further you get from where a variable is created
         | the longer and more descriptive its name should be. Some people
         | just take it too far or don't read the whole guide I guess.
        
         | benjaminjosephw wrote:
         | Learning code shapes and conventions as a skill is definitely
         | an important approach for the pragmatic programmer. Ultimately
         | though, isn't this a necessary consequence of just not having a
         | high enough level of abstraction in our programming languages?
         | 
         | Here's my very opinionated view: in scenarios where
         | maintainability and readability are the most important long-
         | term characteristics of a program then the best programming
         | language would be one which separated out the specification of
         | a program's intent from its implementation.
         | 
         | Separating concerns is a design pattern that we've learnt is
         | really important (remember the early days of PHP?). We
         | currently encapsulate logic around reasons for that code to
         | change because we recognize the value of that pattern.
         | Extending that same design pattern to what we know about the
         | long-term maintainability of code means that we should be
         | abstracting intent from implementation to allow both to change
         | independently. Compilers and virtual machines already do some
         | optimizations to this effect at the moment but we could go way
         | further in our programming languages themselves. I think this
         | means that declarative programming is the logical evolution of
         | software design (really hope someone has an intelligent counter
         | argument here because I can't see any myself).
         | 
         | Go makes a great implementation language in this sense (Rust
         | too) but it's bound to loose ground in the application space as
         | soon as we find the right high-level declarative language to
         | describe application software.
        
       | [deleted]
        
       | fmakunbound wrote:
       | https://github.com/golang/go/wiki/SliceTricks I was reading
       | through this, which was referenced from the article. This just
       | seems insane. None of any of that seems intuitive.
        
         | Measter wrote:
         | Meanwhile, in the much more complicated Rust, all but four of
         | those are one-liners[0]. The exceptions being:
         | 
         | * Expand. Rust doesn't provide an easy way to insert multiple
         | values into the middle of a vector, so I had to do 2 lines:
         | append, then rotate right a subslice starting at the desired
         | insertion point by the length of the insertion.
         | 
         | * Shuffle. No RNG in Rust's stdlib, so I used Rand. I did re-
         | implement the shuffle, but in reality I'd probably just use the
         | shuffle function provided by Rand.
         | 
         | * In place dedup. Needed two lines: one to sort, then I could
         | call dedup.
         | 
         | * Move to front. This is not a function Rust provides, so it's
         | completely implemented. Needed 10 lines. This one needs to
         | search for the item and move that if it exists. In the event it
         | exists, I rotate it left onto the end of the vector, then
         | rotate the entire vector right. Otherwise it inserts at the
         | beginning.
         | 
         | [0] https://play.rust-
         | lang.org/?version=stable&mode=debug&editio...
        
           | Dr_Emann wrote:
           | And for move to front, why not `a[..i+1].rotate_right(1);`,
           | rather than moving to the back and then shifting everything?
        
             | Measter wrote:
             | Uh... because I'd been up for 20 hours and by brain fixated
             | on a weird way to do it?
             | 
             | Don't program when tired, kids.
        
           | Dr_Emann wrote:
           | Expand does actually exist in the standard library, but it's
           | a bit of a strange incantation...
           | vec.splice(i..i, std::iter::repeat(0).take(length));
           | 
           | This replaces the values in range `i..i` (an empty range
           | starting at i) with `length` copies of `0`.
        
             | Measter wrote:
             | That's not exactly the same. Splice returns an iterator, it
             | doesn't modify the collection in place.
        
         | cutler wrote:
         | Me too. Made me realise I picked the wrong language just for a
         | performance boost. The Go patterns mentioned earlier are even
         | worse.
        
       | adsharma wrote:
       | The idea behind https://github.com/adsharma/py2many is that you
       | can still write:                   del a[i]
       | 
       | and have the go code auto generated. py2many doesn't do that yet,
       | but you can teach it to.
        
       | parhamn wrote:
       | Most of these will be solved with the recently approve generics.
       | 
       | Concurrency (less the pattern libs that generics will provide)
       | feels generally hard. Although channel semantics in Go are very
       | annoying (e.g what happens when you close a buffered channel).
       | This is hard in most languages I've used. Anyone have an example
       | of what the state of the art hard-to-shoot-yourself-in-the-foot
       | concurrency looks like in another language?
        
         | cutler wrote:
         | Clojure's core/async and Elixir's actors.
        
       | endisneigh wrote:
       | I really like Go, but I'd _love_ something like JavaScript that
       | had the performance of C^. If and when that happens there really
       | wouldn 't be any need for other programming languages IMHO.
       | 
       | Oh well - that being said Go is way simpler than C++18 that I've
       | done a tiny bit of work in. I think the issue is that Go
       | generally competes with Ruby/Python/JavaScript in certain
       | scenarios and just seems way more complex in comparison.
       | 
       | If you compare Go with C++/Java/C# it seems more reasonable and
       | "easy."
       | 
       | ^ the good news is that a ton of money is being spent to improve
       | Node performance - whether it'll approach Java/C++ speed is TBD,
       | but the performance gap is shrinking year by year.
        
         | vitus wrote:
         | > C++18
         | 
         | (Out of curiosity, is this C++17 or C++20? I'd assume 17, and
         | also I'm curious which aspects you find confusing.)
         | 
         | Several coworkers have remarked that it's just easier to reason
         | about correctness of concurrent code in Go vs. C++, while not
         | sacrificing performance as in, say, Python. (There's definitely
         | a performance hit, per the use of a garbage collector.)
         | 
         | One thing that's always bothered me about Go is just the things
         | that are missing from the standard library; I think that
         | contributes significantly to it being "harder" to work with.
         | 
         | (A good number of these likely stem from the lack of generics,
         | e.g. max(a, b int) int -- math.Max takes and returns floats.)
         | 
         | That said, the biggest thing I liked about Go from the first
         | day I used it was the treatment of concurrency as a first-class
         | citizen (goroutines, channels).
        
         | Aachen wrote:
         | I find Java and C# to be _significantly_ easier than Go. (
         | "more reasonable" depends on your goals of course, though if I
         | had to pick one language that makes it easy to do things in a
         | reasonable way it would be Python.)
         | 
         | C# was one of the first languages I learned after HTML4 and
         | Game Maker, no problem.
         | 
         | Java I learned later, no problem.
         | 
         | I also get C++ though I never used it enough to be really
         | proficient.
         | 
         | And of course I know a bunch of other languages from awk to
         | bash to javascript to php to ruby to sql to vba (not an
         | exhaustive list).
         | 
         | But whenever someone makes something in Go, they seem to expect
         | that the unique patterns and unusual operators for
         | backgrounding tasks etc. to be intuitive to me and I should be
         | able to pick up where they left off in a few minutes... it's
         | not just not.
        
         | jpgvm wrote:
         | I would try Kotlin.
         | 
         | Syntactically it's even more terse than JS and is as fast for
         | most use cases as C. Main downsides are memory usage and
         | needing to understand JVM limitations for latency sensitive
         | codebases (TLDR: Use a modern JVM and ZGC or Shenandoah).
        
           | thu2111 wrote:
           | Note that with SubstrateVM/Graal native image, you get far
           | lower memory usage and startup time equal to or even better
           | than C.
        
         | throwaway189262 wrote:
         | > but I'd love something like JavaScript that had the
         | performance of C^
         | 
         | Java honestly is close. Data object creation is notably worse
         | unless you're using Lombok or similar. Besides that, modern
         | Java and JS syntax are similar. And Java doesn't have a
         | horrible underlying object model based on prototype chains.
         | 
         | IMO Go seems easy because it's simple. I know Java and JS well
         | and they're about the same complexity these days.
         | 
         | Knowing both I will never touch JS for backend again. Java is
         | better suited for it. Strong typing is light-years easier to
         | follow code paths and refactor. IDE's, debugging, profiling
         | tools, memory usage, and performance are all better in
         | Javaland. Real threading with proper shared memory exists. App
         | size is smaller since Java bytecode is compact and distributed
         | zipped.
         | 
         | My company stopped using JS on backend after the hype wore off.
         | Our Java backends are easier to monitor and maintain.
        
           | cutler wrote:
           | How does Kotlin compare?
        
           | matwood wrote:
           | I agree wrt to Java. Because Java has been around for so long
           | it has a lot of emotional baggage that I think makes people
           | reflexively dislike it. But, modern Java with any of the
           | micro frameworks or something like Spring boot, plus jOOQ
           | makes for a very easy and productive backend. And because
           | Java has been around for so long, if you run into edge cases
           | like parsing a weird file format, there is probably a library
           | out there. Kotlin has a lot of same advantages, plus a more
           | expressive syntax if that's what someone is after.
           | 
           | Go is also works for the backend, but you have to get into
           | the mindset that you're probably going to write a lot of
           | code. That's fine, but it's a shift from many other
           | languages. As someone who has programmed for 20+ years, I
           | like the single way to do most things in Go. There's also a
           | bit of elegance coming full circle through many languages
           | where a for loop is once again just a for loop.
           | 
           | And maybe I'm showing my age, but I also completely agree
           | that backend languages need strong typing. The other option
           | is significant test coverage, but we have all seen how that
           | goes in practice. Code without strong typing always feels
           | like it's meant to be written once and thrown away because
           | it's so hard to read later. And, this style fits with how UIs
           | often work where the life of a product will end up with many
           | UIs on the same backend.
        
         | kaba0 wrote:
         | There won't be one ultimate language. The difference between
         | low-level and high-level languages is significant. High-level
         | languages allow you to not have to worry about intricacies of
         | memory layout mostly, and thus even changing the data
         | structures you use will not result in a refactor. While using
         | low level languages, even ones like rust, you can change the
         | allocation strategy/memory layout of your data structure, but
         | you will need to refact on it. So it is a tradeoff between ease
         | of code base change, and tight control on memory/resource
         | usage.
        
       | val_deleplace wrote:
       | I agree with the 2 example tasks, which are not "simple" to
       | write.
       | 
       | The remove-by-value snippet is 8 lines, and it can be wrong in a
       | subtle way: if list contains pointers, then we have a memory leak
       | beyond the slice final length (pointer values still exists within
       | capacity, and objects are not garbage collected).
       | 
       | I wrote several possible implementations here, none of which is
       | as concise and as obviously correct as an hypothetical
       | "list.remove(v)": https://programming-
       | idioms.org/idiom/136/remove-all-occurren...
        
       | nerdbaggy wrote:
       | In Go tutorials I almost never see anything about mutexes or how
       | to be thread safe
        
         | strken wrote:
         | Generally the answer is to stick everything into a channel.
         | This doesn't fix race conditions, but it does make it easier to
         | write code.
         | 
         | Want to query the db in parallel? Start n goroutines and write
         | the results to a channel, then read from the channel n times.
         | Want to limit your concurrency to m tasks? Start m goroutines
         | reading tasks from a tasks channel and pushing results to a
         | results channel, write n tasks to the tasks channel, read n
         | results from the results channel.
        
           | eknkc wrote:
           | I find the channels to be the most confusing thing in Go.
           | 
           | What happens when you write to a closed channel? What about a
           | blocked one? What if you read from a closed one. Can you
           | close a closed channel? Does that panic? What was the non
           | blocking read? What about write?
           | 
           | Fuck that, I use the sync Mutex primitives and good old
           | slices instead.
        
             | Groxx wrote:
             | +1 to this. I'd say nearly half of the channel code I've
             | encountered in "real code" has had severe flaws when things
             | don't align perfectly. They don't have _data races_ , in
             | that the race detector will give it a thumbs-up, but they
             | do have _semantic races_ leading to deadlocks, misbehavior,
             | dropped data, etc. They generally work fine when they 're
             | not under heavy load / contention, so they aren't noticed
             | until they collapse.
             | 
             | Mutex code though? 90% correct, easily. It's often
             | _dramatically_ simpler to make correct, especially when
             | more than one construct is needed, or nearly anywhere with
             | error handling. (`defer` is quite nice for locks)
        
       | domano wrote:
       | In the go trainings i have given i noticed that syntax really was
       | never the issue, but some concepts were awfully hard for some
       | participants to understand.
       | 
       | Also pointers are a thing people are afraid of for some reason.
       | 
       | On the other hand i notice that most go code i have written does
       | not need any maintenance at all, which is neat.
        
       | soheil wrote:
       | The fact that Go supports channels, buffers and workers means
       | it's very much tailored to parallel processing. I always thought
       | of it as a soft of worker farm except it's running on a single
       | machine. You can run it in a multi-node environments as well, of
       | course. I find the parallelism feature of Go so powerful that it
       | overshadows its other features. For server side there are already
       | many really well designed systems that allow for parallelism,
       | Apache, Nginx, Kubernetes, RabbitMq... For client side
       | parallelism maybe useful in things like games but even then there
       | are frameworks better suited to take advantage of parallelism
       | offered by GPUs. I like Go for command line apps, it's a much
       | faster alternative to Python.
        
       | opportune wrote:
       | Go is not designed to be a scripting language. It _is_ designed
       | to have users think about edge cases and error handling so they
       | are sure they are doing what they actually want to. Both the
       | slice and goroutine patterns the author uses are pretty much
       | standard patterns in Go, like making a factory function in a more
       | OO language.
       | 
       | To echo others, when Go makes something cumbersome (like an O(n)
       | operation) it's usually to hint to users that they're doing
       | something inefficient.
       | 
       | But, agreed you can't pick up Go in minutes. It's best approached
       | coming from a background of writing service binaries in C/C++. If
       | you aren't familiar with strong type systems and concurrency
       | you're going to have problems.
        
       | divan wrote:
       | You can't reply to someone's comment on internet about "...could
       | pick up Go in 5-10 minutes..." that seriously.
       | 
       | But couple of points about simplicity author missed out:
       | 
       | - deleting from slice is expensive so should be explicit. True
       | that lack of user generics prevents people to write own generic
       | function for that, but that's not the only thing that generics
       | will inevitably bring - are we still talking about complexity?
       | 
       | - Go is still the easiest language to pick up and understand due
       | to smaller basis in the feature vector space. That's what Go
       | authors mean by orthogonality of features. The search for the
       | solution is way more straightforward in Go space - and that's
       | what we call a cognitive load.
        
         | marvinblum wrote:
         | Agreed wholeheartedly. I professionally progammed in Java, PHP,
         | and Go, alongside a few other languages for open-source/pet
         | programms (Python, C, C++, ...) and finding a solution to a
         | problem in Go is straightforward. When I had to write Java, I
         | always had to decide which language feature I'd like to use
         | first before getting down to the problem.
        
       | grodes wrote:
       | I hope this will change with generics, and we will get
       | 
       | > slices.delete(slice, index)
        
       | labrador wrote:
       | Easy to learn doesn't necessarily mean easy to use, which I
       | gather is where the "go is easy" meme got started. Morse code is
       | easy to learn but hard to use.
        
       | aimatt wrote:
       | Some things are done on purpose so that you consciously know how
       | expensive of an operation you are doing. In languages like
       | Python, it's easy to abuse convenient operations that do a ton of
       | computation. Then you have to spent time with valgrind or
       | something similar and figure out what the heck is causing the
       | code to be so slow.
        
       | zxcvbn4038 wrote:
       | Thinking outside the box, maybe people who don't like Go ought to
       | try a different language. Give Perl or Ruby a whirl. No memory
       | management, no types, no concurrency, nothing to think about,
       | just write code. Learn bash and never worry about arrays again. I
       | knew a woman who went straight from 6502 assembly to bash. All
       | her bash programs had two variables, $x and $y, and she reused
       | them throughout. What other language gives you that freedom?
        
       | omginternets wrote:
       | This article is built on a false premise: that people reach for
       | Go because it is _easy_.
       | 
       | Despite it's flaws (and it definitely has flaws), Go has replaced
       | Python as my go-to language, not because it is _easy_ but because
       | it is _small_.
        
       | chmod775 wrote:
       | We're comparing (linked-) lists with arrays and compiled
       | languages with scripting languages now?
       | 
       | Why would you use an array/slice instead of a linked list, but
       | then want to use it like a linked list?
       | 
       | If you need a linked list, use something like this:
       | https://golang.org/pkg/container/list/
        
       | cwojno wrote:
       | I agree with the author that Go is not as easy as Python or Ruby.
       | However, the first example given is bad.
       | 
       | Running delete on a slice is the same as trying to delete an
       | element of an array. It's the wrong data structure if you need
       | that type of access pattern. That's why it's hard. You can cut a
       | tree down with a hammer, but it's going to take a while and
       | you're going to have some blisters.
       | 
       | Go has built-in linked lists.
       | https://golang.org/pkg/container/list/ and I will agree, they're
       | not as easy to use as other languages due to the lack of generics
       | in go, at present. Using the list.* package requires type
       | conversion to evaluate and pull values out of the structure.
       | 
       | I think it's fair to say that Go needs to do a better job
       | explaining the list package or possibly including it in the "Tour
       | of Go" guide. That might help avoid these misconceptions in the
       | future.
        
         | jrpt wrote:
         | I feel like the majority of the time you just have arrays or
         | slices of a manageable size, say <= 25 elements, and you just
         | want to delete an element from it. You don't care about
         | performance because it'll be fast regardless and surely not the
         | bottleneck of your program.
         | 
         | In my experience with Go, a lot of basic functionality is
         | missing and requires you to write your own utility functions.
         | And some things you would expect like mySlice.append(elem) are
         | instead written mySlice = append(mySlice, elem) which isn't as
         | elegant. Plus you have to remember it's not just
         | append(mySlice, elem)
         | 
         | If there's a handful of open source utility function libraries,
         | that will split the Go developer base, kind of like how you
         | used to have both jQuery, Prototype.js, underscore.js and
         | others in Javascript. They should have just standardized all
         | the common functionality you'd want and in an intuitive way.
        
           | mrmonkeyman wrote:
           | People say things like "plumbing" and "utility" functions
           | like their code consists of something more, something more
           | meaningful.
           | 
           | In Go, there is plumbing, everything is plumbing because that
           | is what your job is. It makes it clear and boring. No fun I
           | agree, but engineering is no fun.
        
         | masklinn wrote:
         | > Running delete on a slice is the same as trying to delete an
         | element of an array. It's the wrong data structure if you need
         | that type of access pattern.
         | 
         | If you keep deleting elements from arrays then maybe, but if
         | you do so once in awhile the cache-friendliness and limited
         | allocations overhead of an array will most likely come out
         | ahead. Especially if you also want arbitrary access.
         | 
         | Removing an element from a linked list is cheap if you're
         | already at that element (assuming the node itself is exposed),
         | but most operations are way costlier.
        
         | kaba0 wrote:
         | Delete on a not-too-long arrays is pretty cheap. Probably even
         | better than iterating over a linked list, but of course it
         | depends on the exact application.
        
         | atilaneves wrote:
         | If the array fits in the cache, deleting from the middle of an
         | array and copying all the subsequent elements one to the left
         | is actually faster than a linked list.
         | 
         | And most arrays fit in the cache.
        
       | eternalban wrote:
       | Statesments about ease of programming Go are relative.
       | 
       | Go is easy once you internalize its semantics. And this is the
       | kicker of Go, it _remains_ simple even after you have achieved a
       | level of mastery of the language. This is _not_ the case even for
       | a language like Java. Certainly, C++ gurus will have your eyes
       | bleed reading their code.
       | 
       | But this is not the case for Go, for the simple fact that Pike et
       | al have engineered so that you _can not_ do astronautics with Go,
       | without enduring severe pain. So no one does it. Short of Go
       | assembler code, any Go code out there is accessible to an
       | exceptionally wide range of Go programmers. There is something to
       | be said for that.
        
       | d0100 wrote:
       | > But I also don't think that Go is a language that you "could
       | pick up in ~5-10 minutes"
       | 
       | I disagree. I use Go for our API backend because I can onboard
       | junior developers and have them producing very fast. Because they
       | are familiar with loops and C-syntax, this is very much a
       | guaranteed "pick up in ~5-10 minutes"
       | 
       | The explicitness of Go makes it easy for them to get to work, and
       | easier for me to understand what they are doing when it comes to
       | code review
       | 
       | They won't ever have to use interface{}, nor concurrency, and
       | slice manipulation won't take much time to lookup any "cheat
       | sheet", or copy from another file
        
         | klelatti wrote:
         | I hope "~5-10 minutes" is shorthand for a somewhat longer time
         | periods.
         | 
         | Even so have you considered whether this approach has some
         | downsides and risks?
        
           | d0100 wrote:
           | Since we just work with mostly simple CRUD, there isn't many
           | downsides. Also we use SQL queries for most of our logic, so
           | SQL knowledge is the real barrier
           | 
           | I usually ask them to read gobyexample.com, but they can
           | start working on projects on the first day.
           | 
           | CRUD project with Go makes it easy to add new endpoints and
           | simple features, so we can focus on hiring frontend devs and
           | smoothly turning them into "fullstack CRUD devs"
           | 
           | I am sure this can be achieved with other languages, but for
           | our use case (software-house) Go just fits really well
           | because it provides a low cognitive overhead for juniors and
           | even interns to make sense of the project and contribute
           | 
           | It fits pretty nicely in an "assembly line" style
           | development, but maybe the downside is that it is boring and
           | makes devs want to leave because they feel like they're
           | always hitting the mark and "mastered" development and want
           | to climb to the next level. It seems reasonable for them, but
           | the turnover is tiresome (around 6 months) for me
        
             | klelatti wrote:
             | Thanks - interesting perspective.
             | 
             | I do think that we (or me!) hold ourselves to too high a
             | standard in terms of mastering all aspects of a language,
             | especially given some languages have got too big. It's
             | refreshing to hear an approach that focuses on knowing just
             | enough to get the job done!
        
       | Philip-J-Fry wrote:
       | But no one said Go was easy. It's just simple and easy to reason
       | about. There's a huge benefit to something doing exactly what it
       | says. No hidden behaviours, no overly complex syntax, nothing
       | like that.
       | 
       | I came from C# where I have gotten increasingly frustrated with
       | the amount of additions to the language. Like we now have
       | classes, structs and records. We now have async/await but we also
       | have channels. This is what having a huge toolbox causes. Some
       | people are going to write code using channels, me others will use
       | async. Some people will use features that others don't care
       | about.
       | 
       | I think there's huge benefit in a simple language. In Go I know
       | that there's only 2 ways to do concurrency. You either use go
       | routines and mutexes or you use go routines and channels.
       | 
       | Generics will bring a lot of helper functions that weren't
       | possible before which will remove a lot of repetitive code.
       | 
       | But otherwise I am super happy with writing Go code. All my pet
       | peeves of something like modern Java or C# are gone.
        
         | dTal wrote:
         | > It's just simple and easy to reason about.
         | 
         | These aren't the same thing. Brainfuck is "simple" in the same
         | sense - no hidden behaviors or complex syntax - but it's
         | virtually impossible to tell at a glance what a Brainfuck
         | program does, or say with certainty how it will react to
         | different inputs. Complexity has to live somewhere, and the
         | more restrictive the language, the more complexity is moved to
         | the program structure. Conversely, every special-purpose
         | construct in a language is a place where you don't have to rely
         | on reasoning about an unboundedly complex Turing-complete
         | system, and can instead go and check the documentation for what
         | it does.
        
         | kenniskrag wrote:
         | We solve that with communication to use a to the team well
         | known subset of the language and internal trainings if we see
         | potential helpful features.
        
         | corty wrote:
         | > No hidden behaviours, no overly complex syntax, nothing like
         | that.
         | 
         | Arrays vs. slices and standard functions dealing with slices
         | are full of weird behaviour, unnecessarily complex syntax and
         | obscure features. Like, why are there even arrays at all? Which
         | slice functions return a copy and which ones don't? Why is
         | append() so braindamaged to make a copy sometimes? Why do I
         | need make() as often as I do when mostly the language knows all
         | make() would do?
         | 
         | Go still has lots of footguns, even right at the trivial
         | basics.
        
           | [deleted]
        
           | jerf wrote:
           | "Like, why are there even arrays at all?"
           | 
           | Because while Go is garbage collected, it also provides
           | modest control over memory layout. Arrays are allocated
           | chunks of contiguous memory for holding a fixed number of
           | things. Slices are something that sit on top of that and let
           | you not worry too much about that.
           | 
           | You almost never want arrays yourself, but they are a
           | legitimate use case that the language has to provide because
           | you can't create them within the language itself. But the
           | right answer for most programmers is to ignore "arrays" in Go
           | entirely.
           | 
           | "Why is append() so braindamaged to make a copy sometimes?"
           | 
           | (Tone: Straight, not snark.) If you understand what slices
           | are and how they are related to arrays, it becomes clear that
           | "append" would be braindamaged if it _didn 't_ sometimes make
           | copies. Go is simple, sure, but it was never a goal of Go to
           | try to make it so you don't have to understand how it works
           | to use it properly.
           | 
           | It is a fair point that quite a few tutorials don't make it
           | clear what that relationship is. Most of them try, but
           | probably don't do a good enough job. I think more of them
           | should show what a slice is on the inside [1]; it tends to
           | make it clear in a way that a whole lot of prose can't. "Show
           | my your code and I will remain confused; show me your data
           | structures and I won't need to see your code; it'll be
           | obvious."
           | 
           | [1]: https://golang.org/pkg/reflect/#SliceHeader
        
         | simiones wrote:
         | Of course Go has hidden behaviors and overly complex syntax,
         | like any programming language before or after.
         | 
         | For example:                 xrefs := []*struct{field1 int}{}
         | for i := range longArrayName {         longArrayName[i].field =
         | value         xrefs = append(xrefs, &longArrayName[i])       }
         | 
         | Perfectly fine code. Now after a simple refactor:
         | xrefs := []*struct{field1 int}{}       for _,x := range
         | longArrayName {         x.field = value         xrefs =
         | append(xrefs, &x)       }
         | 
         | Much better looking! But also completely wrong now,
         | unfortunately. `defer` is also likely to cause similar problems
         | with refactoring, given its scope-breaking function border.
         | 
         | And Go also has several ways of doing most things. Sure, C# has
         | more, but as long as there is more than one the problems are
         | similar.
         | 
         | And I'm sure in time Go will develop more ways of doing things,
         | because the advantage of having a clean way to put your intent
         | into code usually trumps the disadvantage of having to learn
         | another abstraction. This is still part of Go's philosophy,
         | even though Go's designers have valued it slightly less than
         | others. If it weren't, they wouldn't have added Channels to the
         | language, as they are trivial to implement using mutexes, which
         | are strictly more powerful.
        
           | Measter wrote:
           | What exactly is the difference here? Is the `x` variable
           | being updated in the second sample so that the stored
           | references all point to the same item?
        
             | [deleted]
        
             | simiones wrote:
             | Yes, that code is equivalent to                 xrefs :=
             | []*struct{field1 int}{}       var x struct{field1 int}
             | for i := range longArrayName {         x = longArrayName[i]
             | //overwrite x with the copy         x.field = value
             | //modify the copy in x         xrefs = append(xrefs, &x)
             | //&x has the same value regardless of i       }
             | 
             | The desired refactoring would have been to this:
             | xrefs := []*struct{field1 int}{}       for i := range
             | longArrayName {         x := &longArrayName[i] //this also
             | declares x to be a *struct{field1 int}         x.field =
             | value         xrefs = append(xrefs, x)       }
        
           | feffe wrote:
           | I don't think this is a good example of proving the point
           | that Go is difficult to read. Isn't it expected to
           | internalize the semantics of basic language primitives when
           | learning a new language, or does people just jump in guessing
           | what different language constructs do? IMHO you learn this
           | the first week when reading up on the language features.
           | 
           | range returns index and elements by value. The last example
           | does what you asks of it, it's like complaining something is
           | not returned by reference when it's not. Your mistake.
           | Perhaps some linter could give warnings for it.
        
             | simiones wrote:
             | I don't think you can truly internalize the varying
             | semantics of which operations return lvalues and which
             | return rvalues. At least, I haven't yet been able to in ~2
             | years of Go programming, and it's a constant source of bugs
             | when it comes up.
             | 
             | The lack of any kind of syntactic difference between vastly
             | different semantic operations is at the very least a major
             | impediment to readability. After all, lvalues vs rvalues
             | are one of the biggest complexities of C's semantics, and
             | they have been transplanted as-is into Go.
             | 
             | As a much more minor gripe, I'd also argue that the choice
             | of making the iteration variable a copy of the array/slice
             | value instead of a reference to it is the least useful
             | choice. I expect it has been done because of the choice to
             | make map access be an rvalue unlike array access which is
             | an lvalue, which in turn would have given different
             | semantics to slice iteration vs map iteration. Why they
             | chose to have different semantics for map access vs array
             | access, but to have the same semantics for map iteration vs
             | array iteration is also a question I have no answer to.
        
               | marcosdumay wrote:
               | > and they have been transplanted as-is into Go
               | 
               | Hum... I don't think anything can return an lvalue in C.
               | 
               | Your first paragraph is not a huge concern when
               | programming in C, declarations create lvalues, and that's
               | it. I imagine you are thinking about C++, and yes, it's a
               | constant source of problems there... So, Go made the
               | concept much more complex than on the source.
        
               | simiones wrote:
               | I think Go has exactly C's semantics here. The following
               | is valid syntax with the same semantics in both:
               | array[i] = 9       *pointer = 9       array[i].fieldName
               | = 9       (*pointer).fieldName = 9
               | structValue.fieldName = 9         pointer->fieldName = 9
               | // Go equivalent: pointer.fieldName = 9            // you
               | can also create a pointer to any of these lvalues
               | // in either language with &
               | 
               | Go has some additional syntax in map[key], but that
               | behaves more strangely (it's an lvalue in that you can
               | assign to it - map[key] = value - but you can't create a
               | pointer to it - &(map[key]) is invalid syntax).
        
             | pizza234 wrote:
             | I think this is actually a very good example of the inverse
             | relationship between logical complexity and language
             | complexity.
             | 
             | A language that has implicit copy/move semantics is easier
             | to write (since it's less constrained), and more difficult
             | to read (since in order to understand the code, one needs
             | to know the rules).
             | 
             | A language that has explicit copy/move semantics, is more
             | difficult to write (since the rules will need to be adhered
             | to), but easier to read (because the constraints are
             | explicit).
             | 
             | Although I don't program in Golang, another example that
             | pops into my mind is that slices may refer to old versions
             | of an array. This makes working with arrays easier when
             | writing (as in "typing"), but more difficult when reading
             | (as in understanding/designing), because one needs to track
             | an array references (slices). (correct me if I'm wrong on
             | this).
             | 
             | In this perspective, I do think that a language that is
             | simpler to write can be more difficult to read, and this is
             | one (two) case where this principle applies.
             | 
             | (note that I don't imply with that one philosophy is
             | inherently better than the other)
             | 
             | EDIT: added slices case.
        
         | thayne wrote:
         | > But no one said Go was easy.
         | 
         | Lot's of people have said go is easy. In particular that go
         | code is "easy to read" is an oft-cited benefit of go.
         | 
         | Also, if you go to golang.org and the first thing you see is:
         | 
         | > Go is an open source programming language that makes it
         | _easy_ to build simple, reliable, and efficient software.
         | 
         | (emphasis mine)
        
           | fomine3 wrote:
           | simple != easy.
        
           | Philip-J-Fry wrote:
           | Go is easy != easy to read.
           | 
           | I personally do think Go is easy to read. The fact that I
           | don't get bitten in the ass by hidden behaviours and there's
           | no inheritance to step through 5 files of. The fact that
           | everyone's Go code looks more or less the same (unless you're
           | an absolute beginnner) because there's only so many ways to
           | do something. Compared to C# where reading someone else's
           | code means I have to take a shot to calm my nerves and then
           | take a guess at which permutation of language features they
           | decided to use today.
           | 
           | A simple language is easier to read. This doesn't mean it's
           | going to be easy for anyone who isn't a Go developer. There's
           | no claims of that. But given a week to understand syntax and
           | getting used to reading files, I don't think there's many
           | places where you'd get stumped.
           | 
           | >Go is an open source programming language that makes it easy
           | to build simple, reliable, and efficient software.
           | 
           | This still holds true for me. A simple language builds simple
           | software. No noob Go dev is going to have a good time, but
           | that's true in almost every language. Once you understand how
           | everything works, how interfaces work, how channels work, you
           | can simplify down most problems and build them with ease.
           | Needing to write a few extra lines of code doesn't mean you
           | can't write simple, reliable and efficient software.
           | 
           | Neither of those quotes say Go is an all round easy language.
           | It's not like you can throw it at a baby and get back
           | Kubernetes. It's not easy to do custom containers, it's not
           | easy to do manipulate lists, it's not easy to do a bunch of
           | things you'd do in one line in something like C#. But I think
           | these are small hurdles for the mental burden you're relieved
           | of when you see that the rest of the language is equally as
           | simple and benefits from it.
        
             | rswskg wrote:
             | I think c# is an unfair comparison in this instance.
             | 
             | You are right, c# does too much. But Go makes it far more
             | work to do basic things - list comprehension for example
             | (what is everyones obsession with for loops?). An opinated
             | language with one way to do things sounds great, but not at
             | the expense of basic niceities you get in other languages.
        
               | nmfisher wrote:
               | For what it's worth, I'll take for loops over list
               | comprehensions any day of the week. I just find it a lot
               | easier to read.
        
               | rswskg wrote:
               | It's exactly this sort of comment that gives go lovers a
               | bad name.
        
               | random5634 wrote:
               | Agreed! Check out pythons move with walrus operators
               | inside list comprehensions - I think u can do that now -
               | starts to be line noise
        
               | simiones wrote:
               | So you are claiming this                 arr2 := []int{}
               | for i := range arr1 {         if arr1[i] > 9 {
               | arr2 = append(arr2, arr1[i] * 2)         }       }
               | 
               | Is more readable than these?                 var arr2 =
               | arr1.Where(x => x > 9).Select(x => x*2);       var arr2 =
               | from x in arr1 where x > 9 select x*2;       arr2 = [x *
               | 2 for x in arr1 where x > 9]       (setf arr2 (loop for x
               | across arr1 when (> x 9) collect (* x 2)))
               | 
               | I honestly can't imagine by what measure any of the
               | latter ones could be harder to understand the former. And
               | as the complexity of the expression increases, I only see
               | the advantage increasing typically (though often it pays
               | to split it into multiple comprehensions, just like a
               | loop that does too much).
        
             | Nullabillity wrote:
             | As someone who reads a lot more Go than I write, Go is
             | awful to read.
             | 
             | Reading Go means that I keep having to read idioms (as
             | posted in the article) and parse them back into the actual
             | intent, rather than just reading the intent directly. But I
             | can't even trust that, since the nth rewrite of the idiom
             | might have screwed it up subtly.
             | 
             | Reading Go means that I can't easily navigate to the
             | definition of a function, because it implicitly merges
             | folders into a single namespace, so I have to try them one
             | by one.
             | 
             | Reading Go means that I usually don't have tooling that can
             | jump to definition, because there are umpteen different
             | dependency management tools, and Gopls only supports one of
             | them.
             | 
             | Reading Go means that even when I have tooling available,
             | _and_ it feels like working today, jump-to-definition
             | becomes impossible as soon as interfaces are involved,
             | because structural typing makes it impossible to know what
             | is an intentional implementation of that interface.
        
               | sagichmal wrote:
               | > Reading Go means that I keep having to read idioms (as
               | posted in the article) and parse them back into the
               | actual intent,
               | 
               | What? I never have to do this...
               | 
               | > I can't easily navigate to the definition of a function
               | . . . I usually don't have tooling that can jump to
               | definition
               | 
               | What? You command-click on it... ?
               | 
               | > jump-to-definition becomes impossible as soon as
               | interfaces are involved, because structural typing makes
               | it impossible to know what is an intentional
               | implementation of that interface
               | 
               | What? You obviously can't jump-to-definition of an
               | interface, but in what way is this a limitation?
        
               | Philip-J-Fry wrote:
               | >Reading Go means that I can't easily navigate to the
               | definition of a function, because it implicitly merges
               | folders into a single namespace, so I have to try them
               | one by one.
               | 
               | Not sure what you mean here
               | 
               | >Reading Go means that I usually don't have tooling that
               | can jump to definition, because there are umpteen
               | different dependency management tools, and Gopls only
               | supports one of them.
               | 
               | There's 1 dependency management tool though. It's go
               | modules. Unless you live 2 years in the past?
               | 
               | >Reading Go means that even when I have tooling
               | available, and it feels like working today, jump-to-
               | definition becomes impossible as soon as interfaces are
               | involved, because structural typing makes it impossible
               | to know what is an intentional implementation of that
               | interface.
               | 
               | You just right click in VS Code and click "Go to
               | implementations". Or if you use Goland it's right there
               | on the gutter.
               | 
               | Reading comments like this really makes me wonder if
               | anyone complaining about Go has actually used it.
        
               | Nullabillity wrote:
               | > Not sure what you mean here
               | 
               | In Rust, if I see a call to `foo::bar::baz()`, I
               | instantly know that `foo/bar.rs` or `foo/bar/mod.rs` (and
               | only one will ever exist) contains either a `fn baz()`
               | (in which case I'm done) or a `pub use spam::baz;` (in
               | which case I can follow the same algorithm again).
               | 
               | In Go, if I see a call to `import ("foo/bar");
               | bar.baz()`, all I know is that ONE of the files in the
               | folder `foo/bar` contains a function `baz`, with no
               | direction on which it is.
               | 
               | > There's 1 dependency management tool though. It's go
               | modules. Unless you live 2 years in the past?
               | 
               | As I said, 99% of my interaction with Go is reading the
               | code that other people wrote. I don't make the decisions
               | about which dependency management tools they use.
               | 
               | > You just right click in VS Code and click "Go to
               | implementations". Or if you use Goland it's right there
               | on the gutter.
               | 
               | I use Emacs, but VS Code still uses the same broken
               | Gopls.
               | 
               | > Reading comments like this really makes me wonder if
               | anyone complaining about Go has actually used it.
               | 
               | Can't really say I disagree, I guess.
        
               | sangnoir wrote:
               | > In Go, if I see a call to `import ("foo/bar");
               | bar.baz()`, all I know is that ONE of the files in the
               | folder `foo/bar` contains a function `baz`, with no
               | direction on which it is.
               | 
               | This is a code smell for poor file organization in your
               | "foo/bar" module - in a well organized project, it should
               | be obvious which file in a module contains a given
               | function[1]. Go doesn't force file=module paradigm
               | (preferring the folder to be the basis), however, it
               | doesn't _preclude_ having one file per module, if that 's
               | what you prefer. If you're reading someone else's poorly
               | organized code, you can always use grep.
               | 
               | 1. Say, you have an `animal` module, the first place you
               | check for `NewCow()` is `animal/cow.go`
        
               | thayne wrote:
               | Like the parent, I read more Go than I write, and I have
               | never seen a single-file-per-module (unless the entire
               | project is one file). And sometimes it's obvious what
               | file a function is from (like in your examples), but a
               | lot of times it isn't. For example is `GiveFoodToCow` in
               | food.go or cow.go? Maybe there are patterns that
               | experienced gophers know, but that adds cognitive load.
               | And would having a 1 file per module paradigm have made
               | go any more complicated?
        
               | sangnoir wrote:
               | food.go and cow.go do not belong in the same (sub)module,
               | IMO. That said, each team (and project) have unique
               | sensibilities - consistency and familiarity help here. My
               | gut feeling is that the "belongingness" of feeding a cow
               | is closer to the cow than the food, unless your food.go
               | is full of "GiveFoodToCow(),
               | GiveFoodToChicken(),...GiveFoodToX()" which is gross.
               | With that complexity, you're better of with a Feeder
               | interface and implement Feed() in cow.go (and chicken.go,
               | etc). If you cannot distill the logic to a single
               | interface, you're probably better off with a receiver (or
               | regular) function in cow.go, because having a list of
               | GiveFoodToX() with different args in food.go is the worst
               | possible design you could go with.
               | 
               | > And would having a 1 file per module paradigm have made
               | go any more complicated?
               | 
               | I was being facetious. Under normal circumstances, no one
               | should use one file per module in Go, but if Rubyists are
               | feeling home-sick while writing Go, the option is
               | available to them ;)
        
               | Nullabillity wrote:
               | > Go doesn't force file=module paradigm (preferring the
               | folder to be the basis),
               | 
               | Yes, this is what that complaint was about?
               | 
               | > however, it doesn't preclude having one file per
               | module, if that's what you prefer.
               | 
               | > 1. Say, you have an `animal` module, the first place
               | you check for `NewCow()` is `animal/cow.go`
               | 
               | This whole subthread was about reading other people's Go
               | code. Of course your own code is going to look organized
               | to yourself!
               | 
               | > If you're reading someone else's poorly organized code,
               | you can always use grep.
               | 
               | Yeah, ripgrep tends to be what saves me in the end. Still
               | annoying to have to break it out all the time.
        
               | sangnoir wrote:
               | > Yes, this is what that complaint was about?...This
               | whole subthread was about reading other people's Go code.
               | 
               | So the complaint, restated is "Go doesn't prevent other
               | people from writing bad code?" Am I getting you right? If
               | so, well, I have nothing to say about that.
               | 
               | edit: I do actually have something to say. I just
               | remembered having to work with an 82,000-line long Perl
               | module file that would defeat any IDE. Fun times. No
               | language can save you from poorly organized projects,
               | whether the modules are file-based or folder-based.
        
               | Nullabillity wrote:
               | I would say it's closer to "Go doesn't do a really simple
               | thing that nudges people towards writing more readable
               | code, while having basically no tradeoffs".
               | 
               | Considering the far more tedious tradeoffs that Go _does_
               | force on users in the name of supposed readability (for
               | example: the lack of generics), I 'd consider that a
               | pretty big failure.
               | 
               | I don't expect them to be perfect. I do expect them to
               | try.
        
               | sangnoir wrote:
               | FYI: Go's lack of generics was not related to readability
               | - the Go team didn't have a solution they liked, so
               | instead of saddling the language with a half-assed
               | solution forever, they waited for a more elegant
               | solution. Also: the design of Go generics was recently
               | approved (as in the past month).
               | 
               | It's no secret that Go is targeted at "programming at
               | large". Go's design favors larger, fewer modules, how
               | those modules are structured is left to
               | teams/individuals. I may be lucky to work with a great
               | team, but I always find the code where I expect to find
               | it. When I'm starting from scratch, I utilize modules,
               | <$VERB>er interfaces, structs and receiver functions: I
               | cannot remember ever running into an ambiguous scenario
               | where I'm unsure where a particular piece of code ought
               | to go.
        
               | fxtentacle wrote:
               | Let's go with "Go is easy if you use an appropriate IDE"
               | 
               | Emacs is great for many things, but Go was clearly
               | designed with GUI IDE tooling in mind. So if you decide
               | to forfeit that huge benefit, of course it'll be a sub-
               | optimal experience.
        
               | psanford wrote:
               | Gopls works great with Emacs.
        
               | prussian wrote:
               | What makes Emacs not a GUI IDE?
        
               | morelisp wrote:
               | > Go was clearly designed with GUI IDE tooling in mind.
               | 
               | I would like to see what happens if you say this to Rob
               | "syntax highlighting is for children" Pike's face.
        
               | cy_hauser wrote:
               | > Go was clearly designed with GUI IDE tooling in mind.
               | 
               | Surprisingly not. One of Go's authors commented many
               | times in the early days that Go shouldn't need an IDE,
               | only a text editor. He even commented that syntax
               | highlighting/coloring were unnecessary distractions.
        
               | fxtentacle wrote:
               | He said:
               | 
               | there was talk early in the project about whether Go
               | needed an IDE to succeed. No one on the team had the
               | right skill set, though, so we did not try to create one.
               | However, we did create core libraries for parsing and
               | printing Go code, which soon enabled high-quality plugins
               | for all manner of editor and IDE, and that was a
               | serendipitous success.
               | 
               | Which I read as they considered IDEs important.
        
               | simiones wrote:
               | To be fair, we then had to throw all of those "high-
               | quality" plugins in the garbage where they belonged, and
               | go with Microsoft's Language Server architecture instead
               | to really have a usable Go IDE experience in something
               | like Emacs or vim (GoLand from JetBrains is of course
               | much better, and that might be using some of the built-in
               | Go tools?).
        
               | cy_hauser wrote:
               | I'll accept that as a reasonable conclusion. Then he also
               | said the following:
               | 
               | https://usesthis.com/interviews/rob.pike/
               | 
               | https://groups.google.com/g/golang-
               | nuts/c/hJHCAaiL0so/m/kG3B...
               | 
               | (and some other stuff about syntax highlighting from
               | around that time (?) I couldn't find.)
               | 
               | So maybe it was a bit of both.
        
               | flippinburgers wrote:
               | I happily use golang in vim using gopls. I don't see an
               | IDE being a requirement at all.
        
               | tankenmate wrote:
               | The first one is an issue, not just for reading but also
               | for writing; you'll find slightly similar ways of doing
               | the same thing, and Murphy's (Real) Law kicks in "If
               | there are two or more ways to do something, and one of
               | those ways can result in a catastrophe, then someone will
               | do it."
               | 
               | The rest of the issues seem to be tooling issues, not an
               | issue with Go itself. There are tools out there that do
               | the right thing (personally I'm old school and use
               | gotags, warts and all), so I'd suggest get better tools
               | that work for you (or write one if you can't find one you
               | like).
        
               | [deleted]
        
             | joelfolksy wrote:
             | "A simple language is easier to read." "A simple language
             | builds simple software".
             | 
             | It's become a cliche to bring up brainfuck here, but it
             | really is a direct refutation of these ideas, being a
             | maximally simple language that produces maximally
             | complicated code.
        
           | nvarsj wrote:
           | Go _is_ easy to read. I can read almost any go codebase, even
           | giant ones, and see what's going on pretty quickly. Same with
           | C - the Linux kernel is surprisingly easy to understand once
           | you know the basic data structures it uses. There is a lot of
           | benefit in using simple/limited languages from a readability
           | point of view.
        
             | owl57 wrote:
             | The data structures in C require thorough documentation to
             | have any chance to keep your foots. And keeping the
             | preprocessor wizards in check. But I agree that Linux
             | authors have done a consistently good job.
             | 
             | In fact, a kernel is even easier to read than other
             | programs whose authors worked as hard, because having no
             | external libraries at all also helps.
        
             | johannes1234321 wrote:
             | Reading the kernel is relatively easy since some people
             | worked hard on a structure, which is relatively clean.
             | 
             | There are many macro C code bases with tons of function
             | pointer fun (for example when doing OOP) where you have a
             | hard time to find things, till you spent considerable time
             | learning the choices made in that code base.
        
             | toolz wrote:
             | Maybe for small pieces of code, but getting the overall
             | higher level feature goals of code in go is hell (for me).
             | The lack of function overloading to generalize intent for
             | generic work is abysmal and outdated. It pains me to no end
             | that their built in functions are overloaded, but I can't
             | do it in my own go code.
             | 
             | Generally speaking if you're modestly familiar with a
             | language it's rare that it's difficult to read small pieces
             | of code. The hard part of programming on a team is writing
             | code such that high level intent is quickly apparent with
             | APIs that support that intent and make it easy to
             | understand where edge-case handling is happening vs direct
             | feature intent
        
           | baby wrote:
           | Go is easy to read
        
         | threatofrain wrote:
         | But for most programmers, easy outweighs simple in the
         | hierarchy of values.
        
           | felipellrocha wrote:
           | Which is a problem. (Though, i will say i think go is neither
           | easy nor simple, but i believe that is an unpopular opinion
           | here at HN)
        
             | [deleted]
        
             | lima wrote:
             | Simple > easy should be common sense for anyone who ever
             | worked in a team. Code is written once and read many times
             | (and usually not by the author).
        
         | DennisP wrote:
         | I don't entirely disagree but I can't help noticing, you
         | mentioned two ways to do concurrency in C# and two ways to do
         | it in Go.
        
         | sigzero wrote:
         | I am pretty sure I have read something similar to "Go is easy"
         | a multitude of times.
        
         | callmeal wrote:
         | >This is what having a huge toolbox causes. Some people are
         | going to write code using channels, me others will use async.
         | 
         | I don't know if this is a problem. Redundancy is good. If
         | simple languages ruled, we would all be speaking Esperanto.
         | 
         | >Some people will use features that others don't care about. I
         | end up having this argument with product managers who insist
         | that because most people only use 60% of the functionality of a
         | product, we don't need to implement the remaining 40%. You need
         | more than one way of doing the same thing.
        
           | sagichmal wrote:
           | On the contrary, every instance where there is more than one
           | way to do something is a failure of the language. It's OK,
           | all languages have failures, but the ideal is pretty clearly
           | a set of precisely orthogonal features, where there is
           | neither repetition nor exception.
        
         | rednum wrote:
         | Actually Rob Pike said that Go was supposed to be "easy to
         | understand and adopt":
         | https://www.youtube.com/watch?v=uwajp0g-bY4
        
           | BenFrantzDale wrote:
           | This is the same guy who somehow thought a half-speed
           | language without generics was a somehow going to be a C++
           | competitor.
        
         | mbesto wrote:
         | > But otherwise I am super happy with writing Go code. All my
         | pet peeves of something like modern Java or C# are gone.
         | 
         | I'm always confused by this. Why do people consistency cherry
         | pick specific languages to compare then when those languages
         | offer fundamentally different paradigms? I understand comparing
         | C/C++ vs Go, but isn't obvious that C# is going to be
         | fundamentally different than Go?
        
           | Agingcoder wrote:
           | Yes it's obvious, but it doesn't prevent OP from liking go
           | more than c#/java.
           | 
           | Furthermore, its very likely that the languages people
           | 'cherry pick' are actually languages they use everyday. So
           | they just compare 'new everyday language' to 'previous
           | everyday languages', and just tell you whether they're
           | happier or not when they write code. The point is not really
           | about the language theoretical properties and merits, but how
           | people experience the language in real life.
        
           | simiones wrote:
           | C# and Java are much closer to Go than C++. C is also closer
           | from another perspective (simplistic syntax).
           | 
           | C++ is about as far from Go as you can imagine a language.
           | Maybe only Prolog would be a worse comparison point.
           | 
           | C++ is designed with one goal in mind: no special compiler
           | built-ins. If it is possible to do it at all, it can be done
           | in a 3rd party library. C++'s design philosophy is Library
           | first. Bjarne Stroustroup explicitly advocates this: don't
           | write special case code. Write a library that solves the
           | problem, then use that library to achieve your special case.
           | 
           | Go doesn't think writing libraries is a useful endeavor for
           | most programmers: they should be writing application code,
           | and let their betters design the tools they need, build them
           | into the compiler, and stop wasting time designing your own
           | abstractions.
        
             | mbesto wrote:
             | > C++ is about as far from Go as you can imagine a
             | language. Maybe only Prolog would be a worse comparison
             | point.
             | 
             | https://commandcenter.blogspot.com/2012/06/less-is-
             | exponenti...
             | 
             | /headscratch
             | 
             | EDIT: I think you're misinterpreting syntax of the language
             | with use case. e.g. low level vs high level programming
             | language
        
               | simiones wrote:
               | I'm not talking about either syntax nor use case, but
               | about language philosophy, the approach to problem
               | solving embodied in the language - the "paradigm" as you
               | called it. As that post points out, even inside Google,
               | the C++ committee members had a vastly different vision
               | than Rob Pike on what makes a language good. C++11 is
               | essentially universally loved in the C++ community as a
               | giant step forward, as are most subsequent revisions. Rob
               | apparently hates them.
               | 
               | Even the article shows that Go and C++ are fundamentally
               | at odds philosophically, in terms of their paradigm:
               | 
               | > Jokes aside, I think it's because Go and C++ are
               | profoundly different philosophically.
               | 
               | > C++ is about having it all there at your fingertips. I
               | found this quote on a C++11 FAQ:
               | 
               | > The range of abstractions that C++ can express
               | elegantly, flexibly, and at zero costs compared to hand-
               | crafted specialized code has greatly increased.
               | 
               | > That way of thinking just isn't the way Go operates.
               | Zero cost isn't a goal, at least not zero CPU cost. Go's
               | claim is that minimizing programmer effort is a more
               | important consideration.
               | 
               | If you want to compare them on use cases, Go is far too
               | slow and memory hungry to be used in most domains where
               | C++ is actually necessary. That is, for any C++ program
               | that could be re-written in Go, you could also re-write
               | it in Java or C# or Haskell, bar a few problems around
               | startup times and binary sizes.
               | 
               | And that is what has been seen, again as the post points
               | out: C++ programmers are not switching to Go. Ruby and
               | Python and (Java and C#) programmers are.
               | 
               | Also, the designers of C++ have never made any effort to
               | think about how easy the language is to learn, unlike the
               | designers of Go, Java and C#. In fact, all of these
               | languages share a common heritage: they are all designed
               | as answers to C++, in slightly different ways.
        
         | Hawxy wrote:
         | > Like we now have classes, structs and records.
         | 
         | IMO records are a great addition, especially when it comes to
         | point-in-time/immutable data. They also provide an improvement
         | to developer efficiency, as basic data classes can be
         | represented with a single line record definition.
         | 
         | > We now have async/await but we also have channels. Some
         | people are going to write code using channels, me others will
         | use async.
         | 
         | I'm not really sure what you're talking about here. C# has no
         | concept of a channel. If you referring to the
         | "System.Threading.Channels" package, it exists for those in the
         | community that would benefit from it, and still uses familiar
         | async/await syntax. It's also a very niche package that is
         | unlikely to have significant adoption, so there's no real
         | concern of the pattern "segmenting" the community.
        
       | netmonk wrote:
       | OP complains that GO official tutorial is all about syntax. So
       | what are the ressources to learn GO specific mindset ? More
       | generally, that was my concern back in the day with "The C
       | programming language" which was only about how to write C code
       | but not how to think in C. Which is quite different !
       | 
       | What are the best ressource online about how to think in a
       | language (RUST/GO/C/C++/JAVA/JS/PYTHON/HASKELL/CAML...) ? Not
       | only limited on about to syntaxically write a correct program !
        
         | marvinblum wrote:
         | Tutorials on source code structure and open-source projects.
        
       | mraza007 wrote:
       | After reading this article now I'm questioning my self should i
       | continue learning Go as this is giving me second thoughts that if
       | it's going to be useful for me to learn go
        
         | arp242 wrote:
         | I could write a "ugh, this is kinda annoying"-criticism of any
         | language I've ever used in depth. I certainly didn't intend
         | this to be a "Go sucks!" kind of post: as the author of this
         | article, I still use (and like!) Go very much, and is pretty
         | much the language I use by default for most problems.
         | 
         | And even if it turns out you don't really like Go: that's okay.
         | The only way for yourself is to actually learn it, and you will
         | be more knowledgable and experienced regardless.
        
       | marcus_holmes wrote:
       | > "I've seen many people coming from Python or C# try to shoehorn
       | concepts or patterns from those languages in Go. Common ones
       | include using struct embedding as inheritance, panics as
       | exceptions, "pseudo-dynamic programming" with interface{}, and so
       | forth. It rarely ends well, if ever."
       | 
       | I've seen this so much. I did it too. You can almost watch people
       | go through this process in places like r/golang.
       | 
       | We all start off coming from a different language and being used
       | to that, and while Go takes about an hour to learn all the
       | syntax, it's only then that the learning starts. We all then try
       | to write code the way we've always written it, only to find that
       | Go doesn't work like that. Then we bump into the "this standard
       | lib logging library is so shit, let's write a better one" (for
       | the more ambitious, this is a whole framework). Then we learn
       | more, and begin to realise why the logging lib is like that, and
       | why there's no need for some kind of middleware construct, and so
       | on. I'm not sure where this journey ends, because I haven't
       | finished it yet - I suspect my understanding of chans and
       | goroutines is incomplete because I still think they're "a bit
       | clunky".
       | 
       | Simple is hard. But good.
        
         | majewsky wrote:
         | My favorite way to summarize this is "some people can use any
         | language to write Java" (or C or Python or Haskell etc.).
        
       | SEJeff wrote:
       | FWIW, the generics proposal was accepted:
       | 
       | https://github.com/golang/go/issues/43651
       | 
       | This will make a generic "slices" package much more trivial to
       | write.
        
         | CameronNemo wrote:
         | Error checking/handling is still a pain. Maybe generics will
         | make that easier? Result type when?
        
           | tptacek wrote:
           | How does a Result type making error handling _easier_? More
           | reliable, sure, I buy that. But I write a bit of Rust these
           | days, and error handling is definitely not _easier_. It 's
           | actually kind of an enormous pain in the ass, and it
           | certainly seems to involve more lines of code than it does in
           | Go (not just in my code, but in all the libraries I've read).
        
             | lmm wrote:
             | The most common thing you want to do with an error is
             | bubble it up. IME Result types strike the least-bad
             | compromise between making that too intrusive (if err != nil
             | clutter) and making it too subtle (exceptions i.e. magic
             | invisible control flow).
             | 
             | (I don't think Rust is the best example of result types;
             | it's missing a lot of tools for working with them because
             | Rust doesn't have HKT and functions aren't quite first-
             | class because of their lifetime checker, so instead of
             | using ordinary functions you end up using ad-hoc macros or
             | language special cases like ?. Since Go is garbage
             | collected they wouldn't have that problem)
        
             | jjnoakes wrote:
             | I've not had that problem. I would love to see the question
             | mark operator in go because I think it is an elegant way to
             | avoid a ton of boilerplate while still remaining a tad
             | explicit about where errors can occur.
        
           | ampdepolymerase wrote:
           | Hasn't there been several error handling proposals already?
        
             | kyrra wrote:
             | Yes, and all have been rejected thus far. Finding the right
             | balance for those things seems to be hard.
             | 
             | See:
             | 
             | Problem statement: https://go.googlesource.com/proposal/+/m
             | aster/design/go2draf...
             | 
             | Proposal one: https://go.googlesource.com/proposal/+/master
             | /design/go2draf...
             | 
             | Proposal two: https://go.googlesource.com/proposal/+/master
             | /design/32437-t...
        
         | Pxtl wrote:
         | Finally! I honestly think that any statically-typed language
         | without some minimal facility for generics - even C macros will
         | do - isn't fit for purpose. I used Java and C# before the
         | generics were introduced and you can't pay me enough to go back
         | to that.
        
       ___________________________________________________________________
       (page generated 2021-02-22 23:02 UTC)