27 ms·
Functional Programming in Go with Generics
- pcr910303 5y ago(Not directly related, but) The article reminds of a recent article named 'Why Go Getting Generics Will Not Change Idiomatic Go'[0]. Using functional programming in Go... just doesn't look right. Generics are useful, but won't change the whole Go ecosystem into FP. Maybe some uses of Map might make sense, but... idiomatic code will look the same. [0]: http://www.jerf.org/iri/post/2955 http://www.jerf.org/iri/post/2955
- whateveracct 5y agoAt the end of the day, if you want to program immutably or in such a way that is declarative and introspectable, you best look to FP for prior art in those sorts of spaces. Certain problems are age-old child's play in FP-land.
- mpfundstein 5y agoof course it will change ideomatic go. there is no reason to think otherwise. just look at javascript or c++. both languages transformed heavily and nowadays people look at you strange if you write 'var' in js or use a raw pointer in c++. :-) so Go will change and i think that sucks hardcore. there are already enough languages where folks can go crazy with types, fp, monads etc. My selling point for Go was that it was mega restricted :-)
- jerf 5y ago(I'm the author of that piece.) It will change what some people insist is idiomatic Go, but my contention is that when this all settles out, call it a year after production release of the generics, the community as a whole will generally come down against "functional style" programming in Go, and people who want to program functionally in Go will discover that they continue to be quite frustrated because you need half-a-dozen more features. It's going to be so verbose and unfun in Go that only very stubborn people are going to stick to it anyhow. I'm not saying that because I hate the style, I'm saying that because it's still going to be a lot of boilerplate wrapped around not much payload. It really won't be fun. I like "map (f1 . f2 . f3)"; the equivalent in Go even post-generics will be crazy full of boilerplate and garbage. It will change some aspects of idiomatic Go. I don't care much at all about the "functional" aspects but I am greatly looking forward to a proliferation of more specialized data structures that are type-safe. I think one of the major weaknesses of Go in what is arguably its wheelhouse, network servers, is that network servers are often precisely where I want some obscure data structure with the performance characteristics I need to scale up some service properly. It's true that the dynamic scripting languages showed us that you can get a long way with arrays & dictionaries, but I'd like some more stuff. I've got a few places a "map" would be slightly convenient, but I've got several places where I've got architectural problems because I had to hack in an immutable data type or tree or something else that I'd much rather be using a bullet-proof, community-tested implementation of a type-safe data structure than some subset I smashed together and lightly covered with unit tests just to do this one particular job. This is the change I'm actually looking forward to. I've also got some channel pipelines that, while they are set up and working, would be easier to understand with some helper functions that could deal with them. I am also looking forward to either using or writing a very simple concurrent pipeline that specifies a set of transform functions and the number of workers to start up for each and takes care of actually bringing up the network correctly. Or implement structured concurrency: https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ https://vorpus.org/blog/notes-on-structured-concurrency-or-g... Go may never enforce this but I'm tempted to try to enforce it in my programs.
- mpfundstein 5y agothanks foe the reply and i understand your sentiment. i think what will happen is that people will start advocating for more and more syntax changes. think of => in js instead of function. a minor tweak but with HUGE impact. probably much more than const/let etc. and eventually more and more of this will change the language. but i hope i am wrong. :-) in general i get your point amd agree with it. i have written large go codebases and ran into exactly the same issues. but i wouldn't agree to the trade off :-) then i would have used Rust in the first place I guess
- jerf 5y ago"i think what will happen is that people will start advocating for more and more syntax changes. think of => in js instead of function." They won't need to start; hit the Go issue tracker to see a whole bunch of them have already been made and rejected. People have been advocating for them for a while. I virtually guarantee that of all languages, the Go team will not change their mind in response to existence of generics for them. Their bar for accepting changes is very, very high. It is not insurmountable, but it is very high. Remember, Go didn't come out yesterday. Go is now over ten years old. We have a good idea of the velocity of changes it makes. It is low.
- twhitmore 5y agoJava is still an excellent option in many respects. Notably it has: - mature generics - streams - exceptions - rock solid for server apps - some FP style libraries - good timing - probably the widest universe of libraries Only downsides are, 1) JVM startup times and 2) it's not the newest shiny thing. I looked at the whole language comparison recently for a greenfields project. While initially optimistic, I was surprised how many capabilities some new alternatives are missing, and by the enthusiasm to write thousands of lines of low value C-style error checking. An architectural assessment: Above a trivial level of software, the majority of code composes something that can fail. Exceptions are an ideal mechanism for this general truth.
- jerf 5y ago
- svnpenn 5y agoFunctional programming is a kind of leetcode thing that I don't want to see come to Go. In many cases is destroys readability, and allows people to forget about the complexity of the code they are writing (in a bad way). With most Go code today, you can just look at the indentation, or count the for loops. https://github.com/golang/go/issues/45955#issuecomment-832355311 https://github.com/golang/go/issues/45955#issuecomment-83235...
- ebingdom 5y agoIt's a shame that someone can become so misguided about functional programming. Functional programming isn't about destroying readability. It's actually the opposite; it enhances the ability to reason about code by (a) being disciplined about where side effects are allowed to happen, and (b) leveraging reusable abstractions for common patterns (e.g., transforming each element of a list, building a generic container that can be used for arbitrary types without sacrificing type safety, etc.) rather than re-inventing them every time with copying and pasting. Generally, functional programs are safer and shorter than their procedural/OOP alternatives, which is a good thing. Less code means fewer places for bugs to hide.
- ramenmeal 5y agoIn this article, which code sample would you say is more readable? The imperative style or the functional style?
- viraptor 5y agoTheory vs practice for the specific language. Here, the extra signatures and unnatural patterns destroy the readability. It could still be better though if you have, for example a number of pipelines for your data. There, the reuse of Sort/Group/Frobnicate/... in close by blocks would actually be nicer. But yeah - the more extra syntax is required for those pattern, the worse they are in practice. (or, the more things you have to abstract in a small range to make it useful)
- ebingdom 5y ago
- cle 5y agoI seem to be a bit of an outlier, but I am not looking forward to generics in Go. I don't like the kinds of discussions it attracts...vague hand-wavy arguments about "expressiveness" and "purity". Expressiveness to me means more arguing in code reviews, and nothing is stopping us from writing pure functions now, I do it all the time. I maintain a few large Go codebases, tens of thousands of LoC each, and not having generics in practice is way, way down on my list of challenges. Equivalent Java codebases that I've maintained are usually harder to maintain because of generics, with over-eager functional programmers itching to show off their skills in abstraction, instead of solving business problems. I hope I'm wrong, and that this will make Go a better language. But I doubt it.
- klodolph 5y agoI'm hoping people won't go crazy with it. Generics are one of the things that drive me nuts about the Rust ecosystem... it feels like every library in Cargo is based around a bunch of types with a bunch of unnecessary type parameters. Like, if I want to parse command-line arguments. What I do want is to stop implementing sort.Interface over and over again.
- cle 5y agoAgree, this is my hope. Seeing the same thing play out in other language ecosystems though, I'm just not very optimistic about it.
- masklinn 5y ago> Like, if I want to parse command-line arguments. What command-line parsing libraries have “a bunch of unnecessary type parameters”? Only library I can think of which even has them would be structopt, and that’s not unnecessary, the entire point of the library is to deserialise to a structured type. And of course rust was designed from the ground up to leverage generics, e.g. by design it does not have reflection / RTTI, interactions with variable or foreign types are thus generics based (often, sometimes trait objects will do though they have their own tradeoffs), or it doesn’t have overloading so while not handing all overloading use cases by a long shot it uses conversion traits (aka generics) for “convenience” overloads e.g. foo(string) and foo(path) become foo<T>(_: T) where T: AsRef<Path>.
- andolanra 5y agoI 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?
- mpfundstein 5y agoi really love fp. heck i was even overly zealous about it few years ago, annoying everyone around me with it :-) but this development kibda makes me sad :-) i use go for so many projects because it finally restrained me and my employees enough to not argue about language features all the same. and all our code bases look the same. i loved that about go. no, with generics, and all potential libs (just wait for go lowdash, go rambda, go fantasyland, go promise etc), that will come to an end :-)
- graftak 5y agoMissed a big opportunity to go with godash.
- domano 5y agoI dont understand the list of supported features. In my opinion currying and variadic functions are supported in go to some degree. Variadic functions even links to the go example, but classifies them as unsupported in go.
- platinumrad 5y agoVariadic functions aren't even necessary for functional programming in the first place.
- jerf 5y agoGo does have variadic functions, but what are you counting as "currying support"?
- domano 5y agoWell, since functions are first class citizens nothing stops me from turning a (a,b,c) into a (a)(b)(c)
- jerf 5y agoSure, but nothing helps it either, and you're going to take a decent performance hit if you do that because you're turning one function call, possibly inlineable, into three that aren't going to inline, even with new Go generics you will not have enough power to write a "ToCurried" function that converts to a curried version at compile time (I think you could theoretically do it with reflect at runtime, but now you're taking an even bigger performance hit...) I would not normally say any language "support currying" just by having first-class functions. Python can do several of those things I said Go can't do and I still wouldn't call that "supporting currying". It sure isn't anything like the experience of using curry & uncurry in Haskell: https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:curry https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelu...
- domano 5y agoThanks, looking at it this way makes more sense!
- pharmakom 5y agoGo seems like one of the worst languages you could choose for functional programming. Why paddle upstream when you could either write idiomatic Go or switch to a language properly designed for FP?
- eru 5y agoSome people are stuck with Go for their current projects, perhaps?
- minkzilla 5y agoMixing in generics to an already existing code base sounds like a bad idea.
- pjmlp 5y agoAnyone that has DevOps on their daily tasks is now bound to deal with Go code, due its relation to Docker, Kubernetes and cloud infrastructure, just like we need to deal with C on UNIX clones or JavaScript on the browser.
- okamiueru 5y agoWhat relation does golang have with docker for devops use? I'm also wondering the same about k8s, other than the go templating language used by helm charts. And if it's the development of docker itself which is written in go, I wouldn't consider that devops (same as I wouldn't say editing images in photoshop is C++ development). I'm curious, because I've used k8s and docker extensively for devops, and aside from templating, which is arguably not so much golang as, well, just a templating engine. And I've thankfully not have had to use golang for anything serious.
- pjmlp 5y agoPlugins and other software that one might feel like customising. https://landscape.cncf.io/ https://landscape.cncf.io/ As always, using the platform language is the best road to avoid additional development costs dealing with interoperability and tier 2 compatibility issues. Although some stuff is now happening in Rust as well, https://deislabs.io/ https://deislabs.io/
- thatjamesdude 5y agoWhenever I see interface{} mentioned in Go I see vague references to polymorphism. That is wrong. Interface is a special type in Go. All interfaces in go, including ones you declare, hold 2 pieces of data in memory 1. Pointer to the concrete type 2. Metadata about the concrete type interface{} is not a special thing, it is just the empty interface, and everything satisfies the empty interface. This is quite neat. All a type assertion does is follow the pointer and check the type data. There is also a type switch in Go. https://play.golang.org/p/22aegLocHXI https://play.golang.org/p/22aegLocHXI This makes building handlers around the dynamic type easy. But for the most part: Single function interfaces. Compose those to make make larger interfaces if absolutely needed (see io.Writer, io.Reader and io.ReadWriter)
- rad_gruchalski 5y agoI am ambivalent over generics. I can today write: type meh interface{ foo() error } type whatever struct {} func (h *whatever) foo() error And still pass []whatever{} to func canihaz([]meh) so not sure how much generics really add to that. I don’t mind generics but when they are there, I use them. Whatever :) Accepting interface{} is a code smell. I’d accept that only when dealing with wire data. Never in the business logic.
- ricardobeat 5y agoThis is a pretty strong case for not adding generics, and I say this as someone who doesn't particularly enjoy the range loops splattered everywhere. What was pretty straight-forward code, just a bit verbose, becomes a tangle of library functions being composed. It's not shorter, it's not easier to understand, adds dependencies, and most likely will not perform any better. There is nothing gained.
- marcus_holmes 5y agoWhy choose Go as your programming language if you want to code in a functional style?