4 ms·
Go offers a programming interface at a lower level of abstraction than languages like Python or Ruby. What you call boilerplate or housekeeping, I consider to b
by kiitos 3y ago
Go offers a programming interface at a lower level of abstraction than languages like Python or Ruby. What you call boilerplate or housekeeping, I consider to be mechanical sympathy.
Modulo extremes like Java, the bottleneck for programmers understanding code is about semantics, not syntax -- effectively never the literal SLoC in source files. It's not as if
for i := range x {
x[i] = fn(x[i])
}
is any slower to read, or more difficult to parse, or whatever, than e.g.
x.transform(fn)
in any meaningful sense.
- xmcqdpt2 3y agoYour example is very simple though. What's the go equivalent to x.map(fn).filter(predicate) ie returning a new collection of transformed items which is filtered by some predicate? Now we are talking more like 5-6 lines of Go.
- kiitos 3y ago> What's the go equivalent to x.map(fn).filter(predicate) Probably something like var output []T for _, val := range input { if newval := transform(val); allow(newval) { output = append(output, newval) } } No problem?
- xmcqdpt2 3y agoIt's definitely less clear though, in that it involves an if statement with an assignment, three temporary variable declarations, etc. Also, type inference won't detect the type of the output automatically from the transform function type, and this of course assumes you wanted to collect into a slice, but it could be a set, or a list. For some operations, the Go style of explicit, mostly in-place mutations produces more complicated code. Whether that's balanced out by the code being "simpler" is not clear to me, but I haven't worked with Go.
- kiitos 3y agoI see it as unambiguously more clear, because it makes explicit what the machine will be doing when executing the code. Whether map/filter copy values, or mutate in-place, or etc. etc. is non-obvious. I mean I'm not saying my way is the only way and I appreciate that other people have different perspectives but I just want to make it clear that "clear" in code isn't an objective measure, that's all.
- xmcqdpt2 3y agoI agree that there are no objective measure. I guess it's just different expectations. I would not say it's obvious what the machine is doing in the Go example though. For example it wasn't clear to me that append() mostly doesn't copy the full vector, but does a copy of the slice pointer. I had to look it up from a blog post, because the source for append() is gnarly https://github.com/golang/go/blob/go1.16.7/src/cmd/compile/internal/gc/ssa.go#L2841 https://github.com/golang/go/blob/go1.16.7/src/cmd/compile/i...
- kiitos 3y ago> For example it wasn't clear to me that append() mostly doesn't copy the full vector, but does a copy of the slice pointer. Well I guess you do have to grok the language spec and semantics in order to understand how builtins like append behave, I'm not sure that's avoidable.
- pharmakom 3y agoThat is significantly worse than the FP version and I think it proves the point.
- kiitos 3y agoIt's fine that you judge it that way, but it's not like that judgment is any kind of objective truth. I find it superior to the FP version because it is less ambiguous.
- saagarjha 3y agoSo my 2¢ as someone who's just been skimming this thread: I read the second example faster. I mean it's like 2 seconds vs 5 seconds, but in the first I have to actually read your loop to see what it's doing, whereas in the latter I can just go "oh apply fn over x".
- Cthulhu_ 3y agoOne caveat; if `fn` is declared inline when calling that function, it's not very pretty because Go doesn't have a function shorthand (yet?): x.transform(func(value int) string { return fmt.Sprintf("%b", value) }) This quickly becomes more difficult to read, especially if you want to chain some operations this way. But this applies to other languages as well, in JS (which has a function shorthand) I prefer to extract the predicates and give them a meaningful name.
- pdimitar 3y agoI don't write much Golang -- mostly using it for my own needs because it allows quick iteration but haven't made a career out of it -- but for any such cases I just extract out the function. I deeply despise such inline declarations, they are a sign of somebody trying to be too clever and that inevitably ends up inconveniencing everyone else, their future selves included.
- tuetuopay 3y agoYou don't need to go the python or ruby route to get such benefits. I daily write rust that has a pretty comprehensive iterator system, while still getting the nitty-gritty in your hands. As some other commenter put it, `x.iter().map(function).collect()` is mentally translated to "apply function to the collection x" at a glance. between var y []int for _, x := range x { y = append(y, function(x)) } and let y = x.iter().map(function).collect(); I'll take the second form any day. You express the flow of information, and think about transformations to your collections.
- za3faran 3y agoNow add filtering and groupBy and watch that loop become several dozen lines. I worked on one of the largest golang codebases in existence, and it's definitely harder to see the underlying logic compared to something like Java or C#.
- neonsunset 3y agoMechanical sympathy in Go? When you think you've seen it all... Go is not a high performance language which made a lot of decisions that don't lend its usage to be nice in scenarios where people want C and Rust. However, with the hype around it, the management continues to make decision, to everyone's detriment, to utilize Go in performance sensitive infrastructure code which one could write in Rust or C# and achieve much higher performance.
- kiitos 3y agoWhat? Go is all about mechanical sympathy, and is absolutely a high performance language. I guess it all depends on your context, though. If you're used to writing assembly or C, things may look different. (A "for" loop expresses much more mechanical sympathy than a list comprehension, as an example.) But at least in the context of application services -- programs that run on servers and respond to requests, typically over HTTP -- Go is the language to beat. I've yet to see an example of a program where the Rust implementation is meaningfully more performant than the Go implementation, and I've got plenty of examples where the Go implementation is much better.