So tuple() for lists takes two lists and gives all combinations with one item from each list.
We could call it product.
For options it takes two options and return an option with a tuple of values if both are set, otherwise it return None. We could call it bothOrNone.
For Either... It's not unambiguous, but presumably it's similar to option, returning either a tuple of Rights, or the first Left of the arguments, following convention of using Left for errors. We could call it bothRightsOrALeft.
For State I'm just guessing. getBothWithStateOfLast?
These may all have interesting mathematical similarities, but the way you would use them in a program would be pretty different. I don't think you are making your program clearer by giving them all the same name.
Sometimes reading "overabstracted" code can feel similar to reading assembly code. Sure, add and mov are simple operations with a clear definition of input and output, just like flatMap and fold, but they say very little about the programmer's intent in using them. If you give me a chain of them without any comment, I'll need to work backwards to figure out what you're actually trying to accomplish.
If you try to make them sound like completely different things, then yeah I suppose they are all different. But when I use tuple, I think "take F[A] and F[B], sequence their effects, then give me (A,B) to work with." That's an extremely common operation when writing referentially-transparent programs. Understanding "sequence their effects" is the prerequisite intuition, but if I can intuit it with no formal teaching or a finished degree at the time, so can you!
By your logic, it's silly and confusing for Option, List, Either, and State to all have a function called map. But in my (and everyone else I work with)'s experience, map feels like map! And it doesn't take a mathematician to feel that way given our backgrounds...
But if you look at Haskell's Control.Monad you see why the abstraction is helpful [0]. We get things like foldM, it's similar to how you can fold across more than just lists, you can fold across trees and so on, but for all these different structures like State, Either, etc. And yes you are right to point out that some of them have multiple implementations, State can be combined "backwards" or "forwards" (or both). The trade off is you have to invest the time so these abstractions become natural, but once you do so you can use generic control structures that save tons of implementation time.
Some might argue that many similarly behaving structures without structural connectivity (also known as, DRY principle) is the very essence of complexity.
Not necessarily. There is often code that works the same way "by coincidence." The simplest example is two constants that just happen to be assigned the same value. In that case, you shouldn't share common code - it's just going to make changes harder. If you modify one constant, you don't want to modify the other.
Having a mathematical concept in common: is that important, or is it a coincidence? Seems like it depends on the domain and maybe on your point of view.
At the end of the day it's about how hard the code is to maintain. Sharing dependencies makes things better if you want to fix a bug in one place. But every dependency has a cost, too, since it's another thing that can break you if it changes.
Too many dependencies and you get something like the "fragile base class" problem where you can't easily change the code, even to fix a bug, because there are so many downstream usages that depend on it working the way it does today.
So, I would not be quick to depend on a common utility library for simple convenience, unless I know how stable and bug-free it is, and that it doesn't have too many dependencies of its own.
Ok, but since I invested time to learn it and can now use it, I can save time every time I do. Perhaps I will pay down my debt eventually, perhaps not. It's a sunk cost and I enjoyed learning about it, so I'm pretty happy.
These may all have interesting mathematical
similarities, but the way you would use them
in a program would be pretty different.
Actually, by definition, how they are used are not different. This is a key concept. While the tuple example was just that, the idea of defining interaction contracts (often called "laws" in FP literature) is powerful. It can reduce conceptual load due to expectations being clearly defined. It also, perhaps more importantly, enables expressing a solution in a robust manner having clearly defined collaborator roles.
I think your concern about communicating programmer intent is keen. The risk is there whether it is a chain of flatMap, LINQ, or fluent interfaces[0]. IMHO, this the same concern with poorly formulated imperative logic. Having to work backwards to figure things out is orthogonal to the approach taken and IME directly correlated with the attention paid to non-executable artifacts (such as comments, diagrams, discussions, etc.).
For options it takes two options and return an option with a tuple of values if both are set, otherwise it return None. We could call it bothOrNone.
For Either... It's not unambiguous, but presumably it's similar to option, returning either a tuple of Rights, or the first Left of the arguments, following convention of using Left for errors. We could call it bothRightsOrALeft.
For State I'm just guessing. getBothWithStateOfLast?
These may all have interesting mathematical similarities, but the way you would use them in a program would be pretty different. I don't think you are making your program clearer by giving them all the same name.
Sometimes reading "overabstracted" code can feel similar to reading assembly code. Sure, add and mov are simple operations with a clear definition of input and output, just like flatMap and fold, but they say very little about the programmer's intent in using them. If you give me a chain of them without any comment, I'll need to work backwards to figure out what you're actually trying to accomplish.