In backend systems, I think that the overall architecture and management of data are much more interesting problems than the code. I feel like Go helps direct more of the thinking toward the overall rather than creating beautiful abstractions in the code.
We'll see if I still feel this way in a year.
This is a code smell for sure.
You end up seeing functions declared within functions so that people can work around this limitation and it is grotesque.
You are basically saying that one should never need a multi-line lambda. Obviously, I disagree.
It's a very intentional language limitation, much as semantic whitespace is an intentional limitation.
And there is actually a language difference. A for loop is a statement. A lambda is an expression. Python doesn't let you put statements into expressions.
I've sometimes wondered whether code would be universally clearer if all you could do in a loop is have a one-liner or call another function/method (mainly when looking at my own code within loops and thinking WTF!!!).
Would probably cause issues when teaching programming though. It would be interesting to have a compiler switch that enforced this...
foreach ( $item in $list )
{
if ( 1 == some_function( $item ) )
{
continue;
}
else
if ( 2 == some_function( $item ) )
{
break;
}
else
{
another_function( $item );
}
}
But I would immediately concede that it is starting to miss the entire point of why it was enforced in the first place...I will mention a core aspect of my argument, to add to your point.
When you make a named function and then use it separately somewhere else, there is a loss of locality. The logic is now further from its point of use. This is a cost, and sometimes it is an unreasonable one.
I've written snippets or data structures one way or another as I'm sure many have, and in my experience the results have been mixed. This and the fact that reduce has no bearing on your app architecture, I do see this as arguing over a small and vague gain or loss.
Streams and repeated application of short anonymous functions are a much better solution to the method chaining issue.
If js supported better streaming tools, they'd be less common in js too.
I’m having a difficult time understanding what you mean here. Can you offer an example?
I have never felt compelled to write a multi line lambda.
fooBaz(x => {
// multiple lines of logic
})
In python, you are forced to move the logic away from the point of usage, which means you lose some locality. This loss is sometimes not worth getting a "name" for the logic.Of course, sometimes it is better to create a named function away from the point of usage, but I don't think that is always the case.
There are a lot of cases like GUI programming where you need a lot of handlers which are suitable for lambda because they don't have a good name, and should never be called manually, and often they have multi-lines of codes.
def a(n):
def b():
print(n)
return bIt is annoying that you have to give the function a name, though.
def b(x):
return x + 1
vs x => x + 1 def a(n):
return lambda: print(n)In typescript, if I have a bunch of "User" objects where each user has an "name" and "age" field, and I want to print a comma separated list of all those names formatted as "name -- age", it's simple. I write:
let userNamesList = let userNames = users.map(u => `${u.name} -- ${u.age}`).join(", ")
console.log(userNamesList).
This is possible in no small part because map is generic.In go, to get the same level of expressiveness, I'd have to write the following stuff around it:
func mapUsersToStrings(f func(u User) string, users []User) []string {
result := make([]string, len(users))
for i := 0; i < len(users); i++ {
result = append(result, f(users[i]))
}
}
And after writing that boilerplate, I can finally write: userNamesList := strings.Join(mapUsersToStrings(func(u User) string { return fmt.Sprintf("%s -- %d", u.name, u.age) }), ", ")
That boilerplate, of having to write a specialized map/filter/etc function with a for loop for every combination of types you transform between (mapUsersToStrings, mapUsersToAddresses, mapUsersToAges) is really annoying.It's harder to point to many other cases because, simply enough, people don't write such cases. The fact of the matter is just that go libraries make very sparing use of first-class functions because the lack of generics prevents it from working well, and so we end up with an entire language ecosystem where code is harder to read and with fewer simple abstractions reused across it.
You can still write any code without good generics or first-class functions, you'll just have more programmers writing more code with less clean abstractions.
The big picture is that JS is the English of software languages. It’s not beautiful, it steals from everyone else, and it’s never the best tool, but it’s becoming - more and more - rarely the wrong tool.
I expect this trend to continue. Some genius will get JS to compile to native bytecode (WASM is the intermediate step). And, it too will be neither the best language for the job, nor the wrong tool for the job.
The proof is in the pudding, nothing so wrong (JS) can be so right - and yet it survives, nay thrives.
The Saxon words are direct and burly. E.g. oak, brash, death, iron, etc. The Latin words are multisyllabic (e.g. multisyllabic :) ). The Norse words are just plain fun (e.g. Yule, law, heathen, oaf).
It's nice to be able to choose depending on context. Similarly, I find versatility to be nice in JS, as the community goes from embracing OOP to a more functional style.
Ways to make your Python experience nicer:
- almost never try to use map or filter. List/set/dictionary comprehensions are more pythonic and will be easier in the long run
- learn about standard library stuff, especially the methods on dictionaries, as well as the collections package. If you’re thinking about lodash, Python tends to have replacements that end up being better tbh
- if you really want a local function, just creating an inner def statement is fine. But KISS should usually make this rare.
What if the end goal is not to do something elegantly, or perfectly, or with superb efficiency?
What if the goal is just to solve a problem in a way that's a little better than what came before?
If I was a business owner, I'd love Go. But what I can't really figure out is why so many devs love it. It's far from the worst thing in the world, I don't hate it, but I just can't get excited over it either.
As someone who writes — I mean prose, whether fiction or not (mostly not: essays, technical, etc) — I had to come to terms quite early with the dichotomy you explain here.
There is a place for the "beautiful language", the parts of it and the ways of using it that make it a pleasure, as a writer and as a reader. This, unsurprisingly, usually demands a whole lot of additional work on top of the 'direct meaning'.
Then there's a place, in casual communication, in business, in marketing, in essays as well, in technical docs, in speeches, in a lot of places, for the 'direct meaning', or close to that. The efficient use of language, when all form recedes in favor of meaning, of concepts, of getting that 'other' to get what you mean.
It's just that, in human language, you do it all with the same tools, we use formal or topical subsets of a vastly larger ensemble. In programming, we're more likely to use different languages [themselves subsets of human language if you think about it, but let's forget that for the sake of simplicity].
And there are programmers among us who love to dabble in the form, like some writers would spend 10%, twice, ten times the effort crafting just "better form" over an already well-defined idea/story. While other programmers, or at other times, just focus on getting things done. Cue the spectrum in-between.
So it all depends what we put in our code, as programmers, as human beings I guess. Is that thing a personal statement? Or is it just garbage code to temporarily expedite some roadblock? How do we approach complexity, bottom-up from the simplest elements/code, or top-down with the most expressive almost-meta entities? Maybe some side-line out-of-the-box angle? See how we'd word all of these, in human languages, as in code. We just wouldn't say the same, nor code (select languages) the same.
I don't know if I explained it well. But looking at it from a human language writer, it all seems clear now. The whole rat race of languages, the churches, the sheer effort put into form when meaning has already been solved 10 times by others, the strong NIH syndromes... it's all so common in traditional writers circles. We're all just writers, really!
The lack of quality batteries included also leads to "there is more than one way to do it" through the choice of unofficial libraries. My experience is that the extensive standard library of Go brings more normalisation.
I think, in order to understand why so many people love Go, you first need to understand why so many people love C.
Go is C with most of the warts removed (for application development). GC, easy strings, maps, easier first-class function syntax, code formatting, modern standard library, easy concurrency, etc.
Yes, there are plenty of places that C can go that Go cannot - operating systems and embedded devices are high up on that list. But for back end application development, it's great as long as your application is of a moderate size and you don't need specialized data structures.
That covers quite a bit of ground.
That is the reason, I like Go so much. It is still a very simple language, but improves in the key parts of memory safety, having a GC, better type checking, and a few high level constructs, most of all having first class functions and closures. These enable many of the features of "higher" programming languages. You can do mapping functions over lists in Go quite fine.
So what part of C is not in the list of warts averted in Go?
A simple language with only a few things to learn. Everything built up from those few things.
1: unless designed by a committee with no such single-minded aim
For those Go would beat anything that lacks convenient green threads (Type Script included, also Java, C#, C++), anything that is dynamically typed, and anything that is interpreted.
It's bit meant to be exciting, it's meant to be effective, efficient, and reliable.
Not sure why business owners should love it either. Not enough features in the language coding productivity suffer in a long run. Also if the developer needs Go because other languages are "too complex" maybe said developer can't produce proper code anyways.
Secondly, C/C++ is like the third or fourth most commonly listed programming language in job listings. If you think all but 2 or 3 languages are niche, that is not what the word means.
Most software problems are not about solving them faster, it’s about combining existing components in new ways and figuring out to orchestrate it all.
I don’t care about copy elision, heap fragmentation, perfect forwarding, when my performance is being lost in the communication between services. What I need is a better architecture and more scaling, not concerning myself with if this loop is being vectorized, or that object is being moved instead of copied, and other minutia which inevitably ends up wasting your time when writing C++.
Niche does not mean "stuff I don't personally use at my job", but that is the only definition under which Typecript is not niche and c++ is. C++ in 2019 had the 4th most job listings according to Indeed. Calling that a niche is absurd especially in comparison to Typescript.
There are also many places which just ask for Java/C++ experience for no apparent reason. Amazon is like this, all of their job listings mention C++ but only a very small percentage of the code base is in C++. There is at least 10 times as much Ruby code and it is part of systems that most engineers will have to work with, but no job application mentions that.
That's why the authors of YouTube built YouTube in Python, and the authors of Khan Academy (re)built Khan Academy in Go, instead of bickering about nitpicks on HN.
A short comment would help here.
It's pretty trivial to write your own custom map/filter/reduce though.
But oh man it's the same discussion every time when Go comes up here
Same with map, they are a constrained for each that is guaranteed to be a correct implementation.
Edit: Also, for loops are just a way of implementing a mapping, where you have data A that you want transformed into data A*. The map is the fundamental concept here, not the for loop.
In fact in many languages much of filter() can be implemented in the for construct rather than its body.
The map/filter is more fundamental in a mathematical sense. If you turn a list of A turned into a list of A*, the 'mapping' is the fundamental concept, the 'for' loop is just an implementation detail. Maybe the computer did it in parallel, or in random order, or I asked you to turn a pile of towels into a folded stack of towels. The same way if I say 'want to watch a movie' - the movie the fundamental concept, whether it's digital, film, etc. is irrelevant.
And maybe that is part of the problem with a generic 'map' your not really sure the underlying implementation, is it parallel, clustered, serial, etc? So you end up with map(), parallel_map(), mpi_map(), etc, and how to you control the parallelism. Do you just let it default or do you have levers to control the batch & interleave. Pretty soon, its not such a simple construct anymore.
Not a phrase you hear every day.
For some reason, JavaScript haters always seem baffled when confronted with a person who doesn't hate JavaScript.
> “Typescript is a hack on top of [a] [supposedly] server side language that is [awful]. ( Node doesn't even support int64 )”
"supposedly" in this context meaning Js is not worth being called a "server side language", in the commenter's opinion (citing lack of int64 support for example). This also implies in subtext that "server side languages" would be of a higher category/value than languages for whatever other use-cases.
I personally don't agree with any of that, just helping communication here.
Go doesn't make things easy. It asks you to repeat yourself. I don't like the lack of basic functions like a generic map / filter function.. I know Rob Pike just says "use for loops", but it feels so unnecessarily unexpressive. When I see map, I know what's going on almost immediately. For loops take more reading to understand. Nil pointers shouldn't be, yet still are, a thing (developers aren't perfect - why can't the type system help?). It feels like a straight downgrade from Python from a code clarity perspective. And it's typed, sure, but the type system doesn't let me express constraints that other languages allow me to do that would prevent entire classes of bugs. It doesn't feel worth it compared to Python. Yes, the core language ends up being comparatively "simple", but simple building blocks doesn't guarantee a simple overall system. And my company is very diligent from an architectural perspective.
But then when I look at Python, I'd rather just use Javascript with Lodash, esp. when it comes to the treatment of functions as first class objects[0]. Throw Typescript in there, and you get, in my opinion, a better type system than Go, so unless language performance is a major constraint (which it hasn't been for my company, our DB usage patterns it the biggest thing instead), why would I want to use either of these rather than Typescript?
[0] Edit: Dumb and wrong, I meant its treatment of anonymous functions. I don’t like lambdas.