A few times now, I've found myself defining:
(<?>) :: [a] -> [a] -> [a]
[] <?> ys = ys
xs <?> _ = xs
This is an associative operation, of course, and the empty list [] is both left- and right-identity. It functions like Python's or.
It seems to me that this would make a nice (<|>), better than (++) would. Choosing the first nonempty list feels more like what I would expect from a typeclass named Alternative than concatenating lists. Admittedly, it doesn't fit MonadPlus as well, but I think that a small price to pay for salvation. We already have (++) and (<>) in the standard library; do we need another synonym, or would a new function (as far as I can tell) be more helpful?
I was at first thinking this might be a good Alternative instance for ZipList, but the discussion following this answer on the relevant question has convinced me otherwise. Other than backwards-compatibility and keeping MonadPlus sensible, what arguments are there for the current instance rather than this new one?
It is tricky to give your question a straight answer. Considered in isolation, there is nothing fundamentally wrong with your proposed instance. Still, there are quite a few things that can be said in support of the existing
Alternativeinstance for lists.One problem with going down that route is that
Alternativeis meant to capture the same general concept thatMonadPlusdoes, but in terms ofApplicativerather thanMonad. To quote a relevant answer by Edward Kmett:From that point of view, having mismatching
AlternativeandMonadPlusinstances is confusing and misleading, much like the analogous situation withApplicativeandMonadinstances would be.(A possible counter to this argument would be wondering why do we need to care about
MonadPlusanyway, given that it expresses the same concepts and offers essentially the same methods asAlternative. It should be noted, though, that theMonadPluslaws are stronger than theAlternativeones, as the relevant interactions of its methods with theMonadones aren't expressible in terms ofAlternative. That being so,MonadPlusstill has meaning of its own, and a conceivable outcome of a hypothetical reform of the classes would be retaining it as a laws-only class, as discussed for instance in the final section of this answer by Antal Spector-Zabusky.)Given such considerations, in what follows I will assume the continued relevance of
MonadPlus. That makes writing the rest of this answer much easier, asMonadPlusis the original expression of the general concept in Haskell, and so it is pretty much necessary to refer to it while tracing the origin of the list instance ofAlternative.Tracing the roots of
MonadPlusandAlternative, though, shows that the concatenating list instance is not just well-established, but even paradigmatic. For instance, quoting the classic paper by Hutton and Meijer, Monadic parsing in Haskell (1998), p. 4:(Note that the authors do use
(++)as their name formplus.)The notion
mpluscaptures here is that of non-deterministic choice: if computationsuandveach have some possible results, the possible results ofu `mplus` vwill be all of the possible results ofuandv. The most elementary realisation of that is throughMonadPlusfor lists, though the idea extends to cover other non-determinism monads, such as Hutton and Meijer'sParser:To spin it another way, we might describe non-deterministic choice as inclusive disjunction, while the operation you propose is a form of (left-biased) exclusive choice. (It is worth noting that Hutton and Meijer also define
(+++), a deterministic choice operator for theirParserwhich is rather like your operator except that it only picks the first result of the first successsful computation.)A further relevant observation: one of the monad transformers from transformers that doesn't have a mtl class counterpart is
ListT. That is so because the class which generalises theListTfunctionality is preciselyMonadPlus. Quoting a Gabriella Gonzalez comment:Note that the brokenness of transformers'
ListTis not an issue. In general, the various formulations ofListT-done-right are equipped with a concatenatingMonadPlusinstance (examples: one, two, three).So much for reasons why we might want to leave the
Alternative []andMonadPlus []instances as they are. Still, this answer would be lacking if it didn't recognise that, as Will Ness reminds us, there are multiple reasonable notions of choice, and your operator embodies one of them.The "official" laws (that is, the ones actually mentioned by the documentation) of
AlternativeandMonadPlusdon't specify a single notion of choice. That being so, we end up with both non-deterministic (e.g.mplus @[]) and deterministic (e.g.mplus @Maybe) choice instances under the sameAlternative/MonadPlusumbrella. Furthermore, if one chose to disregard my argument above and replacemplus @[]with your operator, nothing in the "official" laws would stop them. Over the years, there has been some talk of reformingMonadPlusby splitting it into classes with extra laws, in order to separate the different notions of choice. The odds of such a reform actually happening, though, don't seem high (lots of churn for relatively little practical benefit).For the sake of contrast, it is interesting to consider the near-semiring interpretation, which is one of the reimaginings of
MonadPlusandAlternativethat might be invoked in a hypothetical class hierarchy reform. For a fully fleshed account of it, see Rivas, Jaskelioff and Schrijvers, A Unified View of Monadic and Applicative Non-determinism (2018). For our current purposes, it suffices to note the interpretation tailors the classes to non-deterministic choice by adding, to the monoid laws, "left zero" and "left distribution" laws forAlternative...... as well as for
MonadPlus:(Those
MonadPluslaws are strictly stronger than theirAlternativecounterparts.)In particular, your choice operator follows the purported
Alternativeleft distribution law, but not theMonadPlusone. In that respect, it is similar tomplus @Maybe.MonadPlusleft distribution makes it difficult (probably impossible, though I don't have a proof at hand right now) to drop any results inmplus, as we can't tell, on the right hand side of the law, whetherm1 >>= korm2 >>= kwill fail without inspecting the results ofm1andm2. To conclude this answer with something tangible, here is a demonstration of this point: