I feel like a lot of the “FP is beautiful but oh so unpractical” comes from seeing Swift as the exemplary FP language along with some amount of marketing bs about the beauty of FP. Most of the complaints seemed to be that FP in Swift is kind of a mess and it’s not clear how to best use it. I think that’s absolutely an artifact of the language.
Now there is a lot of flexibility in FP. I think that the “standard” Go idiom is certainly more specific. But I think that there are fairly “standard” FP idioms, too, developed in Haskell, OCaml, Clojure. This is clearly living in some kind of failure + logging + Frobulation monad (which is a terrific mixing of effects and indicates better reasoning could be had by separating them out)
frobulate :: ( Logging m
, MonadError FrobulationError m
, Frobulation m ) =>
Frobulator -> m ()
frobulate f = do
n <- thingsToFrobulate f
when (n > 0) $ do
logit frobulatingMessage -- where did this come from?
youngest <- processOld f
This is now automatically generic in the kind of effect handler you want to choose. Why “automatically generic”? Because I didn’t constrain the code to have to make some choices, so the compiler can happily determine that the code will work regardless of any of those choices. I can install this into a test handler and then port the exact same code to my real application handler knowing that I haven’t made any typos.
So maybe Go is a shop-built jig, yes, but it’s someone else’s shop-built jig. I believe a better language is just a… well, it’s a shop. If you need to make yourself a jig here’s a saw and some wood—knock yourself out. If you just want a premade one, then here’s a nice reusable library.
The few things that bother me about this article are these apparent contradictions:
And then, when it was all working, I refactored out the duplicated code. And I refactored again. And in the end, the whole thing was simpler and shorter than what I would have done with generics
When writing the Go function, I started at the top and typed until I got to the bottom. And that was it. There aren’t very many ways to write this function in Go.
You don’t need generics, just refactor until it works; also, you don’t really need to think about how to rewrite thing, it should be obvious.
Other bits, like:
So again, in the end, Go turned out to be a language for solving real problems rather than a language filled with beautiful tools, and so you build real solutions rather than finding excuses to use your beautiful tools. […] But if you’re trying to solve specific, practical problems in the forms professional developers typically encounter, Go is quite nice.
Seem to carry the implicit assumption that it’s a mutually exclusive problem, or that having beautiful tools leads to not solving the problem appropriately. That if you use fancy tools, the problem somehow doesn’t get fixed, masturbatory practices take place instead.
I’m not sure I can see myself agreeing.
So, when you are actually writing Go code, translating what you intend to do into actual code is much as the author describes: you just write it. That doesn’t mean your initial intent isn’t flawed. I have never written a program knowing exactly how I wanted to do everything from the get-go. Once I learn more about the problem I’m solving by actually writing code to solve it, then I discover flaws in my old approaches and refactor to fit an updated strategy.
The author could have wanted a generic behavior, but discovered at the end of writing the program that the behavior wasn’t actually as generic as they predicted in the beginning, and had common elements that could be factored together. I hypothesize this because the same thing just happened to me at work a week ago! =)
Have you written much Go? Because I notice all the same things as the author. I don’t spend nearly as much time deciding what approach to use on a function by function basis, and spend more time architecting the entire program. For me, it’s not so much a masturbatory need to use beautiful tools of a language that slows me down, but the paradox of choice.
I haven’t written much Go, but I’ve had that feeling in different languages at different times. For me the epiphany came when writing Erlang, and figuring out that all of a sudden, I had found a language where how I thought (planning my programs with bubbles and arrows on a whiteboard) and a declarative style where the errors are possibly handled outside of the ‘happy path’ fit exactly the way I think.
For me, reading and writing Go has always been a disagreeable experience that I felt was crufty, risky, and unnecessary.
Before anything, let me add that this isn’t to the merit of Erlang nor the detriment of Go. I strongly believe that what language you feel comfortable with is a happy coincidence, and that the following elements must all be met for you to be at home with a language:
The problem with arguments such as “this language is really well designed for solving problems” is that it carries the heavy assumption of “when I am using it” added to the end of the sentence, where ‘I’ is the big variable.
I didn’t notice the same things as you or the author did when reading about or using go, which is why these sentences confused me a bit.
Well, the first thousand lines (or some N lines of code) you write in any language are spent checking and double checking the documentation, so when I first started writing Go I didn’t notice those things either. But unlike, for example C++, it was very easy to get to a point where I remember everything important about the language and could blast out code.
I definitely agree that what language you write best in is dependant on what kind of coder you are. Go seems to resonate with systems programmers like myself, because it’s spiritually similar to C with a just enough stuff changed to make it easier (slices and maps being painfully absent from C). Then throw in the latest ideas about concurrency as primitives and you have the optimal language for a systems programmer.
Also, +1 for Erlang, it’s a great language. Idiomatic Go concurrency is fairly similar to the Erlang design principles. =)
As for the dynamic language people who are adopting Go so enthusiastically, I think they are just thrilled they can write performant code without having to use something heavy-handed like C++ or Java. ;)
I read this differently. I believe the author was refactoring because he’s new to Go and still coming to terms with the paradigm, just as there’s a learning curve in any language. He started by trying write complex generic code when a more idiomatic Go solution turned out to be more straightforward. This is a strength of Go - good solutions tend to minimize complexity and emphasize practicality.
While I liked this article, this one part stuck out to me.
Go feels under-engineered because it only solves real problems.
I usually don’t get that feeling while using Go. Maybe it is a failing on my part, a testament to my ignorance, or my inexperience of language design, but I generally get the feeling that Go was very carefully and purposefully engineered.
In fact, I find it quite jarring when I run across areas of the standard library, or even the language itself, which do not have that same feel to it.
To me, Go feels more like a *BSD, and less like a Linux.
It should be Go feels correctly-engineered because it only solves real problems.
If you feel this story should’ve also been tagged with “swift”, please see this meta story: https://lobste.rs/s/dr4wif/please_add_a_tag_for_apple_s_swift_language