monad-bayes icon indicating copy to clipboard operation
monad-bayes copied to clipboard

Is there a reason for no MonadFail instance?

Open luke-clifton opened this issue 1 year ago • 4 comments

Sometimes it would be neat to be able to use a fallible pattern match in do-notation, similar to what we get with lists. This is enabled by having a MonadFail instance.

I was wondering if there was some reason why there is no MonadFail instance available that I can not see?

luke-clifton avatar Aug 21 '23 05:08 luke-clifton

No reason that comes to mind, although do you have a specific example use case in mind? I've certainly used an ExceptT transformer to a similar effect.

reubenharry avatar Aug 21 '23 15:08 reubenharry

It just seems kinda neater in some scenarios. Especially if the sampling is abstracted and we can't "fix" it at the source.

enumerator $ do
   a <- uniformD [Just 1, Just 2, Nothing]
   condition (isJust a)
   pure a
enumerator $ do
   Just a <- uniformD [Just 1, Just 2, Nothing]
   pure a

luke-clifton avatar Aug 28 '23 11:08 luke-clifton

[lengthy comment following] Your two examples even have different types, the first being MonadMeasure m => m (Maybe Int), the second being MonadMeasure m => m Int. Which is more attractive because we know that only the Just path has nonzero measure, and the Nothing path cannot enter any observable in the end.

To achieve that effect, one would like to write:

enumerator $ do
  aMaybe <- uniformD [Just 1, Just 2, Nothing]
  case aMaybe of
    Just a -> pure a
    Nothing -> do
      score 0
      error "Impossible"

But unfortunately, there is no law in MonadScore that prevents anything after 0 score to be unreachable, i.e. a law like score 0 >> ma = score 0. Such a law should hold approximately in any reasonable probability monad, I believe, but I'm not sure it is mentioned anywhere in the original article. I don't know the literature on probability monads well enough to say whether this is a common law. Anyways it is not enforced in the implementations.

I read your proposal as adding such a possibility to the interface via MonadFail. The semantic of fail is supposed to mean "don't continue traversing this branch", like mzero.

turion avatar Aug 28 '23 14:08 turion

Yes, the types being different was a transcription error.

And yes, I had noticed that condition was not quite the same as guard in the Enumerator type, in exactly the way you describe. The computation doesn't "stop" with condition. That was going to be a follow up question. (In fact, when I want to use Enumerator I often use guard instead of condition for that reason and it seems to work as expected).

So your conclusion is correct, I'd actually like to have a Alternative (and possibly MonadPlus) instance to go along with the MonadFail instance. These (bar MonadFail) are there for Enumerator which I do find useful, but then can't use when I want to use a different probability monad.

luke-clifton avatar Aug 29 '23 09:08 luke-clifton