5 ms·
I think people have a habit of taking relatively surface-level features of functional programming and focusing on them to the exclusion of the real benefits of
by andolanra 5y ago
I think people have a habit of taking relatively surface-level features of functional programming and focusing on them to the exclusion of the real benefits of functional programming. The use of something like compose3 in the "functional" example here is a perfect example. Sure, function composition is a "functional" thing, but I don't see why you wouldn't instead write something like (handwaving wildly on the specifics, since I know barely any Go and certainly am not up to speed on the generics proposal):
func getTopUsers(posts []Post) []UserLevelPoints {
return posts.GroupBy(func (v Post) string { return v.Level })
.Values()
.Map(getTopUser)
}
This pipeline style is significantly easier to read (especially without having to put all those extraneous type parameters in your call to compose3!) and doesn't actually lose any of the core advantages of the functional style: purity, testability in isolation, equational reasoning, and so forth. Sure, the Haskell equivalent might use composition… but composition reads more or less naturally in Haskell, and I don't think it does at all here in Go. If your specific approach to "functional programming" makes your code theoretically easier to reason about but practically harder to both read and write, then is it really helping you much?
- mjburgess 5y agoExactly my thought: why is this clearer? I'm often think FP-first (data science) -- here though, I was surprised by how I went back over the imperative alternative. Avoiding `append()` isnt worth it in a language where this level of annotation is required to do FP. It's less clear.
- mappu 5y agoPiling on here - My impression of your snippet here is that GroupBy() and Values() both construct (potentially very large) temporary arrays on the stack. Maybe Haskell and F# can elide this kind of thing, but it's not something the Go compiler is going to do.
- Yoric 5y agoRust and OCaml (the latter if you use the right libraries) can definitely avoid it. Why couldn't Go?
- wrnr 5y agoI've been waiting to do things like that in Go for a long time. It can be done efficiently just like the Java Stream interface.
- mappu 5y agoAn alternate-universe Go with a lot more (& slower) compiler passes could do it, but I don't believe there are any plans to build such a thing. As it stands, this kind of code (where all intermediates have type []T) will run much more poorly than you could do with imperative loops. The final generics design is probably not expressive enough to do it ergonomically in user code neither (building something where the intermediates are a library type with a last `.something()` call to collect a final []T).
- pjmlp 5y agoOCaml compiles as fast Go. No secret sauce, just having the pleasure of chosing multiple backends, including an interpreter. Bytecode runtime during workflow, optimized native AOT for release and final production tests.
- duped 5y agoThat's just bad design - iterators and sequences need to be lazy. The intermediary type should not eagerly create large arrays on the stack, it should only ever do real work once the iterator has been driven.
- rad_gruchalski 5y agoNo, they don’t need to be. They can be. Go wasn’t designed as a functional language but to be fast to compile, fast to execute, zero dependency binary and simple syntax. It achieved that by a mile.
- deleted 5y ago[deleted]
- Serow225 5y agoFWIW C# would as well (LINQ)
- deleted 5y ago[deleted]
- platinumrad 5y agoYeah if you're writing "Compose3" you've already lost. The extra type annotations only make it worse. x := foo() y := bar(x) z := baz(y) Wow.
- pharmakom 5y agoOk, now what if foo, bar and bar are async? Nullable? Result types?
- platinumrad 5y agoWell it's not like Go has do notation so I don't know what you're expecting here.
- pharmakom 5y agoI’m pointing out that functional programming goes beyond trivial things like compose3 and list operations (useful though they can be).
- platinumrad 5y agoSure, but e.g. monadic futures aren't going to be very usable in a language without do notation, custom operators, or non-verbose lambdas. Might as well just use channels. As for nullability and error handling, writing "if" over and over again might be bad, but in Go I don't think functional approaches are going to be any better. Maybe liftA2, etc. on pointers would be usable, but not much else.