10 ms·
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 bene
by harikb 6y ago
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 6y agoYes. 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.
- nobleach 6y agoAs 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.
- d3nj4l 6y agoWhat part of list.delete(x) Is hard to understand if you didn’t write it?
- philosopher1234 6y agoI 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 6y agoMy persistent impression of the Ruby ecosystem is that everyone optimizes for clever one-liners so hard that all other priorities go out the window.
- theshrike79 6y agoIf we're still talking about Ruby here: List could be _anything_. It might be a list of numbers, it can be a dataset in a remote server, it can be a web page parsed into a list of sentences, it might be list of people in Active Directory. You literally can't know what's going on under the hood without popping it and looking for yourself. Ruby's infatuation with clever magic code is what turned me off it years ago. You can get up to 80% in 20% of the time compared to other languages, but then you spend the 80% of time you have left fighting against the magic to get the last 20% done properly. There's way too much stuff You Just Need To Know.
- chinmaythosar 6y agoLinear 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.
- harikb 6y agoDoes Ruby track that a list is already sorted? If not, you are still adding cognitive overhead for the programmer to track it
- vinger 6y agoYou can sort by many things. Knowing where you sort (usually as far up the chain as possible) is important
- z0r 6y agolinear 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)
- chinmaythosar 6y agomy 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 6y agoIf 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.
- technics256 6y agoI'm just learning Go myself, where can I learn or reference these popular patterns that have emerged over the past few years?
- harikb 6y agoWhat 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 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 6y agoYour 1. and 2. are merged in the almost-stdlib package errgroup: https://pkg.go.dev/golang.org/x/sync/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
- wisswazz 6y agoI really like http://tmrts.com/go-patterns/ http://tmrts.com/go-patterns/
- majewsky 6y agoThis 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.
- majewsky 6y agoStudy 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 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.
- eweise 6y agoDevelopers 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.
- btilly 6y agoMy 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.
- andi999 6y agoBut isn't usually when using lists performance is dead anyway (due to cache misses).
- pansa2 6y agoLinked-lists, maybe, but `list`s in Python are just resizable arrays.
- kindall 6y agoresizable 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.
- d3nj4l 6y agoThe 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.
- arp242 6y ago> 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 6y ago[deleted]
- kaba0 6y agoI’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 6y agoThis 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 6y agoGood 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.
- moonchild 6y ago> 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 6y agoThat’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 6y ago> 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/FizzBuzzEnterpriseEdition https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris... [2]https://github.com/Droogans/unmaintainable-code https://github.com/Droogans/unmaintainable-code (and 20 others)
- deleted 6y ago[deleted]
- dmitriid 6y ago> 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 6y agoIt 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 6y agoMost 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.
- joelfolksy 6y agoSince simpler languages are easier to work with, let's just remove goroutines and channels from Go. Then all these problems will just go away.
- SergeAx 6y agoIt's okay. They will add generics soon, someone will immideately write a generic array.Delete() function, it will klog compiling and linking, and in couple more iterations we will have another JVM.
- deleted 6y ago[deleted]