I mostly agree with you, but I also think a bit part of what Haskell-like FP shows is how often your code is invariant to so many things that the variables no longer have any sense to them whatsoever.
That doesn't really excuse badly named variables when that doesn't hold. It's more that it's something sort of novel to a lot of Haskell-like programmers and so we all get excited about it and probably overdo it somewhat. But on the other hand, I think it's very well-justified often enough.
For instance, with
class Functor f where
fmap :: (a -> b) -> (f a -> f b)
there are many words you could give to f, a, and b but they're essentially all misleading.
With another example from the article, Strong Syntax, the "Syntax" bit is basically a convention in scalaz that ought to be immediately obvious if you're familiar with the scalaz library. The "Strong" bit has to do with a subtype of "Profunctor" structures, "Strong Profunctors".
Profunctors are generalizations of functions that show up all over the place. Strong profunctors are profunctors which can "distribute over a tuple".
Giving names to these types is an exercise in futility. At least with a function `a -> b` might be named `in -> out`, but with a Profunctor there isn't even that intuition. It's just too general. Subsequently, it shows up all over the place.
With profunctors there's not necessarily anything going in or out and especially not necessarily and notion that the thing going in produces a thing going out.
That's all roughly true with one kind of profunctor, a function arrow, but not true in general.
For instance,
data Counterexample a b = Cx (Set a) b
is (very, very, very nearly [0]) a profunctor, but a isn't necessarily "going in" and b isn't necesssarily "coming out".
[0] It's a profunctor if a is finite. You can handle the infinite form by writing it as `data Cx a b = Cx (a -> Bool) b` which is equivalent to what I wrote when `a` is finite... but it also makes it a little easier to pretend that a is "going in" even if that's a bad intuition.
I'm not sure I see how the intuition is bad here? What would the lmap implementation for your (Set a) example be if not equivalent to the (a -> Bool) case? And how is that not an example of data "going in"?
More generally even if there are Profunctors for which this analogy isn't perfect, I feel like the intuitive type names are still more useful than random letters. Especially for a relatively advanced concept like Profunctors, for which I expect pretty much all users to be comfortable with the idea that a type variable does not necessarily imply that concrete values of that type will exist.
It is equivalent to the (a -> Bool) case. In Haskell at least, all "negative type parameters" are ultimately generated by the left side of a function—but this is more a concern for how ideas are modeled (perhaps partially) in Haskell then a fundamental one. If you're writing code that's
forall p . Profunctor p => ...
then `in` and `out` aren't generally valid.
I definitely hear the argument that ergonomically it might be a good idea to use a sort-of-appropriate model to drive better terminology... but at the same time I think there are drawbacks. I think it helps the early part of a learning curve but then hinders the latter part. An expert doesn't care what they're called since they're just mentally erasing the names as appropriate anyway. A non-expert will try to carry the metaphor further than it can go and gets stuck.
This is why there's endless debate about calling Monoid "Appendable" or something like that. For the commonest cases that's the right idea... but the first time someone questions why there's an Appendable instance for Bool (two, actually!) you're fighting with your own inappropriate metaphor.
"Oh, Appendable actually means Monoid but we didn't want to say that straight up."
That doesn't really excuse badly named variables when that doesn't hold. It's more that it's something sort of novel to a lot of Haskell-like programmers and so we all get excited about it and probably overdo it somewhat. But on the other hand, I think it's very well-justified often enough.
For instance, with
there are many words you could give to f, a, and b but they're essentially all misleading.With another example from the article, Strong Syntax, the "Syntax" bit is basically a convention in scalaz that ought to be immediately obvious if you're familiar with the scalaz library. The "Strong" bit has to do with a subtype of "Profunctor" structures, "Strong Profunctors".
Profunctors are generalizations of functions that show up all over the place. Strong profunctors are profunctors which can "distribute over a tuple".
Giving names to these types is an exercise in futility. At least with a function `a -> b` might be named `in -> out`, but with a Profunctor there isn't even that intuition. It's just too general. Subsequently, it shows up all over the place.