> No map, no flatMap, no fold, no generics, no inheritance… Do we miss them? Perhaps we did, for about two weeks.
So they weren't complaining about map and flatMap, they just missed them.
Good for them! Their problem domain is likely so simple and neat that it fits the Go's limited built-in types well, and does not lead to frequent copy-paste programming. If so, Scala has been an overkill.
users.filter(_.active).map(_.email)
to be easier to read (and not a pain in the ass to type) than: emails := make([]string, 0)
for _, u := range users {
if u.active {
emails = append(emails, u.email)
}
}
return email
The first style is more explicit about what you want to do, the second style is more explicit about how you want to do it. Go made imperative vogue again, but for me it feels like a throwback all the way to Turbo Pascal. Sure, the Go compiler is really fast, but Turbo Pascal would beat it hands down - on 20Mhz machines with 1MB of RAM.With more features (TP 7)!
I mention this as neither a programmer on either Scala nor Go.
If it is of any relevance, I've been writing code for about 20 years across C, Java, Javascript, PHP and shell scripts. I'm sure other FP developers will roll their eyes to a traditional developer like me but the one-liner you mention is simply not self-explanatory for an outsider when reading it.
It returns 'email' instead of 'emails' which I assume was a typo, which of course the compiler would complain about, are you claiming that a variable name typo is somehow a 'subtle bug' ?
Only other thing I see directly is that 'users' is undefined, but this is a code snippet after all, not a working program.
Those dumb Go programmers and their toy programs (https://quicknotes.io/n/1XB0).
However, now that those features are part of the majority of mainstream languages, I surely don't want to go back to how I used to write software in the first two decades of my career.
Being "a better C" was the original battle cry of Go. It's definitely better than C (well, at least for some thing), but it doesn't mean all of these complex C software people used to write are toys.
Go is obviously not a useless toy language either. But that doesn't mean you won't miss features from more expressive languages. The amount of calls for generics from Go users tell a different story.
I think it is from Non-Go users who would use Go if only it had generics.
They guys from Movio did; their problem domain is likely different.
There are cases when the restrictions of map() and reduce() are necessary, most famously for a map-reduce in distributed programming, but that's not really relevant for implementing a simple function.
Also, error density is relatively constant per line of code according to most studies (this is a very general statement, but we can assume that most code just isn't trying to be clever). I've forgotten to add(), append() and drop items inside a loop far more than I've made mistakes inside more functional methods.
And, man, tail recursion in a language with function head pattern matching (ML family, Erlang) is so much easier to read than any complicated for loop.
(Update: realized afterwards that "foreach" isn't quite the same as "for", but the larger point still stands, mostly.)
They describe the intent of what to do with the data, not how it is done.
Just like using SQL versus an old xBase database to access data records.
We all know real programmers used gotos.
The very reason those other functional constructs were split out was not due to need, but due to -clarity-. And that same clarity applies to why to use one of those constructs instead of a for-each loop.
def filter_broken(widgets):
broken_widgets = []
for widget in widgets:
if widget.is_broken():
broken_widgets.append(widget)
return broken_widgets
The actual errors with loops and mutable objects come from complex nested loops, continues/breaks (multi-level ones especially!), extra boolean conditions, and sharing mutable objects between parts of the code base.I think that overstating the supposed risks of for-loops doesn't help anything. The problem is not that you're going to mess up a simple for loop. It's the ways that your code doesn't compose well that are more problematic. Oh, and the verbosity sucks too.
Maybe that's right, maybe that's wrong, but it's indeterminate from the code whether it's desired, whereas a filter function and a reverse function would make it explicit.
Btw: I'd venture to guess that you're not a Python person, and that's why it's not obvious that it constructs the method in the same order. In imperative languages (where Python isn't the perfect example b/c it has filter and list comprehensions), you get so used to these loops that you don't have to worry about questions like that.
If you wanted a reversed list, you'd either name your function appropriately, or call reverse after you run the filter method. It's a little painful to do the first bit.
Because having three specialized functions for three specialized operations makes it easier to see what a given piece of code is doing. One function per function.
I dabble in what sorts of programming languages, but when comes to work, it is mostly C#, Java, JavaScript and some occasional C++, because that is what customers pay for.
Hence why I see a big value in mainstream languages slowly evolving into multi-paradigm ones, as many of us don't have the luxury to move beyond the first class languages of each platform.
If your brain is organized enough to write clean for loops then ... maybe. But it's a bit opportunity for problems. At least to me; but then I may not be smart enough.
That being said, map and flatmap force immutability in some way. So you pay a price for this, either in speed or in memory, even with tail recursion.
I think the more esoteric features should be reserved for complex code, especially if it's possible that those features can prove runtime correctness at compile time and result in easily-composable modules. I wouldn't expect more than one or two developers to need to develop such a subsystem, however, so the "clever" is used in isolation.