Thanks very much @lpw25 for this very clear discussion (and for others’ comments on it). I think it’s helpful to think in terms of tradeoffs, as your post suggests.
Some additional thoughts.
First, I don’t think it’s a question of how often a pattern has been used, but how often it would be used if it were more convenient. The use of composition operators in other languages and in math seems relevant to this question. (As I’ve suggested earlier, I also think that it could be useful to consider future OCaml users in this calculation as well as existing ones. I’ve made it clear that this should not mean adding any feature that seems to be popular outside of OCaml, but careful consideration of existing styles of functional programming in other languages might be relevant.)
Second, I don’t feel that an operator must come to be be used pervasively for its use to be justified. If it’s used occasionally but regularly, and many of those uses make code more succinct and easier to understand, that can be enough. I’m in effect suggesting that there is a sort of relative expected value of a pattern–something roughly like the number of uses times their relative benefit compared to alternative patterns. (Even in math the composition operator is uncommon, at least in domains with which I’m familiar, but when it’s used, it often aids understanding of certain ideas, I believe.)
About cost, I gather that the main cost that we’re considering would be the confusion that results from having to learn an arbitrary operator character or characters. (I hope it would not be three characters!) I absolutely agree that that’s a reasonable concern. (Here’s an illustration of such confusion: I was confused about some of the discussion in this thread until I looked up some of the operators.)
In this case I really think that adding a new operator for composition is not much of a burden, because (a) it will be used–regularly, even if not in all code or even a lot in some code–and (b) the function represented by the operator is one that is familiar to any experienced functional programmer. It’s difficult to remember a new operator that represents an unfamiliar function, but this one is not unfamiliar. A composition operator, it seems to me, would quickly become something that anyone would assume should be memorized–even those people who never wanted to use it. Here again the presence of composition operators in other languages and in math is relevant. (As I said earlier, I’ve been using OCaml quite a bit over the last year or so, and I’ve never used any operators other than math operators, or even looked for them, except for a composition operator, which I sought out very soon after I started using OCaml, because I wanted to use it in my code to simplify a
map application without defining a new function name.)
Btw, I completely agree with the points you make about math as a model, @lpw25, but the role of math as an examplar in my reasoning above is different. The argument isn’t “Math has this operator, so OCaml should.” It’s that the familiarity of the composition operator in math helps to make the concept familiar, so that it’s readily understandable and memorable, and might suggest useful applications in code. (My first comments about the significance of math didn’t make this clear, and I don’t think I’d made it completely clear for myself, either.)
All of my points do, indeed, depend on judgements about which disagreement can be reasonable. For my part, I still feel that the potential benefits of a composition operator to (some) users and to the language community in the long term outweigh the costs of adding it, which seem small to me.
(About precedence, for me personally that’s not the main issue. I wouldn’t mind e.g.
List.map ((add 1) % (mul 2)) mylist
as well as simpler cases in which the functions have only one argument. If a composition operator allowed removing those inner pairs of parentheses, that might be good for some users. I might just add the parentheses sometimes, anyway, so I don’t have to remember the precedence rule when the syntax doesn’t make it obvious. In this particular example it would be easy to guess what’s going on without parentheses, though.)