
I'm a bit less convinced about the benefits removing the instance for
MonadFail (ST s).
Playing devil's advocate here:
Recall that throwIO is distinct from throw for a good reason, as it ensures
that the throwing occurs at the right step in the sequence of binds.
The `fail` instance for ST can similarly be viewed as a perfectly
reasonable monotone function affecting the result of runST :: (forall s. ST
s a) -> a, which produces an `a` that is the appropriate bottom at the
right time when you take a certain branch in the ST calculation. This is
rather different than Identity, as you can't just ape this behavior by
calling 'error' instead as you need the smarter call.
To achieve that functionality today _without_ fail, you need to reach for
unsafe operations `unsafeIOtoST . failIO` it to get the correct semantics,
which is a damn sight messier and scarier and importantly removing the
instance means this can't be something that is done by just delegating to
base monad transformer 'fail' as would be done through something like
`StateT s (ST s')`. This seems to create a false tension between doing the
most defined thing and doing the thing I want with a stronger constraint,
which I usually take as a sign that the building blocks are wrong.
Removing this instance comes at a real cost in terms of generality of code
that uses `MonadFail`: It does pass the left zero law!
Overall, I'm -1, as I'm actually leaning against the removal of the
instance personally on the grounds above.
-Edward
On Wed, Mar 14, 2018 at 3:31 PM, Michael Snoyman
One possible "well behaved" intuition could be "cannot result in an exception thrown from pure code without usage of unsafe functions." By this definition:
* Maybe's fail is well behaved: using `fail "foo"` results in a total Nothing value * List's: same thing, but with an empty list * IO: runtime exception, but the exception is _not_ in pure code, but rather from within IO, where exceptions are always to be expected * ST: `runST (fail "foo")` results in a pure value which, when evaluated, throws a runtime exception, breaking the well behaved definition * Identity: `Identity (fail "foo")` can only be a pure value which throws an exception, and is therefore not well behaved
Note that I added the requirement of "without usage of unsafe functions," since `unsafePerformIO (fail "foo")` can result in a pure bottom value.
On Wed, Mar 14, 2018 at 4:25 PM, Ryan Scott
wrote: Thanks, that makes more sense. I'm inclined to agree that MonadFail instances should fail in a "well-behaved" way. (I wish I knew how to make the phrase "well-behaved" more formal, but I don't.) It might be worth adding this intuition to the Haddocks for MonadFail.
That being said, one thing to consider before removing this instance is that there will be some breakage. Ben Gamari added this instance in [1] because apparently the regex-tdfa package needed it. Other than that, though, I don't have any real objections to removing this instance.
Ryan S. ----- [1] https://phabricator.haskell.org/D3982
I expect a MonadFail instance to have a well-behaved notion of failure within the monad. An exception from "pure" code (which is what ST simulates) is not that. On the other hand, perhaps you're right and the instance should be removed for IO as well; I don't have as strong a sense of revulsion, but maybe users should be forced to be explicit with throwIO.
On Wed, Mar 14, 2018 at 9:46 AM, Ryan Scott
wrote: OK. You used the phrase "utterly contrary to the purpose of MonadFail", so I'm trying to figure out exactly what you mean here. Prima facie, the purpose of MonadFail (at least, as explained in its Haddocks) is to provide a type class–directed way of desugaring partial pattern matches in do-notation. With this in mind, the current MonadFail instance for ST doesn't seem too offensive.
However, I think you have some additional property in mind that you feel the MonadFail ST instance runs afoul of. Do you mind explaining in further detail what this is? (I'm not trying to be snarky here—I genuinely don't know what you're getting at.)
Ryan S.
On Wed, Mar 14, 2018 at 9:41 AM, David Feuer
wrote: I am not. I think that instance is fairly legitimate, as it raises an IO exception that can be caught in IO. IO's Alternative instance is a bit shadier, but that's not a topic for this proposal either. ST is an entirely different story, and I'm sorry I accidentally mixed it in.
On Wed, Mar 14, 2018 at 9:05 AM, Ryan Scott
wrote: It's worth noting that the MonadFail instance for IO [1] also simply
On Wed, Mar 14, 2018 at 9:58 AM, David Feuer
wrote: throws an error (by way of failIO). Are you proposing we remove this instance as well?
Ryan S. ----- [1] http://git.haskell.org/ghc.git/blob/cb6d8589c83247ec96d5faa8 2df3e93f419bbfe0:/libraries/base/Control/Monad/Fail.hs#l80
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries