-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add instance Exception (Either e1 e2) #233
Comments
I'm in favor! I used something very much like that in my It ends up being a recursive instance, so you can catch three types of exceptions at once: action `catches`
[ Handler (\(a :: A) -> handleA a
, Handler (\(b :: B) -> handleB b
, Handler (\(c :: C) -> handleC c
]
action `catch` \case
Left (a :: A) -> handleA a
Right (Left (b :: B)) -> handleB b
Right (Right (c :: C)) -> handleC c |
There was a usecase of this in the unpack :: Exception e => FilePath -> Entries e -> IO ()
checkSecurity :: Entries e -> Entries (Either e FileNameError)
checkPortability :: Entries e -> Entries (Either e PortabilityError) With the proposed instance, you can do: safeUnpack :: Exception e => FilePath -> Entries e -> IO ()
safeUnpack dir entries = unpack dir (checkPortability . checkSecurity $ entries) where |
It does have some resemblance of an open sum type for errors, doesn't it, like ExceptT, plucky (written by @parsonsmatt) and oops (which I think was never released). It does seem like the proposed instance could potentially be inefficient for unknown error recursion depths. I briefly considered using |
* Add a `Compat` module to accomodate two different `tar` interfaces.
I've updated the proposal and prepared an MR: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/11899 |
I guess a slightly unusual thing here is that if you have an Maybe possible to fix with something like newtype LeftException a = LeftException a deriving newtype (Show, Exception)
newtype RightException a = RightExecption b deriving newtype (Show, Exception)
instance (Exception a, Exception b) => Exception (Either a b) where
toException (Left e) = toException $ LeftException e
toException (Right e) = toException $ RightException e
fromException e =
case fromException e of
Just (LeftException e') -> Just (Left e')
Nothing -> case fromException e of
Just (RightException e') -> Just (Right e')
Nothing -> Nothing where |
See #135 (which was approved, but alas not implemented). |
Ah, in that case I guess we would want something that roundtrips, to avoid violating the undocumented law. I now realize that the code I gave above is overly complicated, and it would suffice just to use the default implementation.
One potentially-problem that occurs to me is that cabal-install would now have a different instance. |
If code threw |
I would suggest either: instance (Exception a, Exception b) => Exception (Either a b) where
displayException (Left a) = "Left (" ++ displayException a ++ ")" or just instance (Typeable a, Typeable b, Show a, Show b) => Exception (Either a b) |
For that purpose, I'd rather see an explicit exception union type. It could be a biased union data BU a b = BL a | BL b
-- Catching BU a b will catch both `a` and `b`, preferring `a`. Throwing will unwrap. Or it could be an explicitly disjoint union, probably most simply like this: data DU a b where
DLeft :: PlainEq a b ~ False => a -> DU a b
DRight :: PlainEq a b ~ False => b -> DU a b
type family PlainEq a b where
PlainEq a a = True
PlainEq _ _ = False Here no bias is required because the types are definitely distinct. However, a little bit of bias can be convenient even in the disjoint case: data DU a b where
DLeft :: a -> DU a b
DRight :: PlainEq a b ~ False => b -> DU a b There's no need to prove the types distinct unless both are thrown. |
It would with the original suggestion, not with either of the things I suggested. I guess there's three things we might want:
I don't know if we can get (1) and (2) at the same time, except that if I understand @treeowl correctly we can use a type other than @Bodigrim, which of these did you want from the instance you were defining? Do you know which cabal-install wants? (Am I missing something from the list?) |
I think you get the gist of what I'm saying. A biased instance for |
That's a good point. Thanks all for the discussion. I think such instance would be less useful that I perceived originally. Let me withdraw the proposal. |
Making the two types explicitly distinct doesn't completely avoid the overlap issue because we may have Also, if we insist that |
cabal-install
defines an orphan instance:This can be expressed more eloquently as
Is there a reason not to put it into
base
? Recently @hasufell and I needed such instance in another project, but had to workaround to avoid orphans.According to Hackage Search the only packages affected are
cabal-install
-the-executable andhackport
(because it includescabal-install
as a submodule). Both are executables without public library components, so there is no transitive breakage. Both are affected because of defining orphan instances and could be trivially patched by wrapping them in CPP. If the change is approved, I'll prepare patches once we know which version ofbase
to use.MR is available here: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/11899
The text was updated successfully, but these errors were encountered: