h&53      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL9Generalised coproducts and methods for working with them.(c) Tom Harding, 2019MITtom.harding@habito.com experimental Safe-Inferred )*-/0125#oopsSimilarly, we can fold the wrapper-less version in the same way. As an example, if all the types are the same, we can pull out whatever value is in there using the fold interface.':set -XRankNTypes -XScopedTypeVariables:{8fold' :: forall x xs. Fold ((~) x) xs => Variant xs -> xfold' xs = fold @((~) x) xs id:}If all the types in the list are the same, and we can turn values of that type into some result and return it.oopsA constraint-based fold requires a polymorphic function relying on a shared constraint between all members of the variant. If that's a lot of words, let's see a little example:foldF @Show (throwF ["hello"] :: VariantF [] '[(), String, Bool]) show "[\"hello\"]" If everything in our variant is M$-friendly, we can fold it with the N1 function, and we just show whatever is in there!oopsThe f-less analogue of . The same properties as described above will hold, with the same issues around  result inference. &> > :t toEithers @'[String, Int, Bool]toEithers @'[String, Int, Bool] :: Variant '[String, Int, Bool] -> Either [Char] (Either Int Bool).The round-tripping property is also conserved:oops/Occasionally, we might want to use our "nested O" analogue for whatever reason. For that situation the functions here allow you to swap between the two representations. +> > :t toEithersF @IO @'[String, Int, Bool] toEithersF IO '[String, Int, Bool] :: VariantF IO '[String, Int, Bool] -> Either (IO [Char]) (Either (IO Int) (IO Bool))In order to maintain the round-tripping property (see below), the functional dependency only goes from the variant to the nested either. This is because the opposite doesn't always necessarily make sense.If Variant '[a, b] is converted to  Either a b, it would seem sensible to say the opposite is equally as mechanical. However, consider a nesting like Either a (Either b c): should this translate to Variant '[a, b, c] or Variant '[a, Either b c]? There's not a unique mapping in this direction, so we can't add the functional dependency. oops is to   as  is to catch8. This function allows us to discharge constraints for  types. We can revisit the  & example without the functor wrapper::{5f :: (e `CouldBe` Int, e `CouldBe` Bool) => Variant ef = throw True:}... and be similarly excited when we make one of the constraints disappear::{/g :: e `CouldBe` Int => Either (Variant e) Boolg = catch @Bool f:} oops8This is an odd constraint, as you should rarely need to see it. GHC's partial instantiation tricks should mean that mentions of this class "cancel out" mentions of . As an example, let's imagine a function that represents some business logic that potentially "throws" either an P or Q while it runs:8:set -XFlexibleContexts -XMonoLocalBinds -XTypeOperators:{9f :: (e `CouldBe` Int, e `CouldBe` Bool) => VariantF IO ef = throwF (pure True):}As we can see, there are two constraints here. However, if we "catch" one of these possible errors, we don't just add the   constraint: we /cancel out/ the constraint corresponding to the type we caught::{8g :: e `CouldBe` Int => Either (VariantF IO e) (IO Bool)g = catchF @Bool f:}/This means that constraints only propagate for uncaught exceptions, just as Java functions only need declare exceptions they haven't caught. Once we've caught all the errors, the constraint disappears! This can be a nice way to work if you combine it with something like ExceptT.oopsListing larger variants' constraints might amplify the noise of functions' signatures. The  constraint lets us specify several types a variant may contain in a single type-level list, as opposed to several independent constraints. So, we could replace,f :: (e  Int, e  Bool, e  Char) => VariantF IO ewith the equivalent constraint,f :: e $ '[Int, Bool, Char] => VariantF IO eAs  is just short-hand, we can use  just like when we have  constraints::set -XTypeOperators:{5f :: e `CouldBeAnyOf` '[Int, Bool, Char] => Variant e f = throw 'c':}3... and eliminate constraints in just the same way::{=g :: e `CouldBeAnyOf` '[Int, Bool] => Either (Variant e) Charg = catch @Char f:}oopsAs with , we can also constrain a variant to represent several possible types, as we might with several ) constraints, using one type-level list.oops Just as with , we can "throw" values not& in a functor context into a regular .8throw (3 :: Int) :: Variant '[Bool, Int, Double, String]There (Here (Identity 3))4throw "Woo!" :: Variant '[Bool, Int, Double, String].There (There (There (Here (Identity "Woo!"))))oopsWhen dealing with larger (or polymorphic) variants, it becomes difficult (or impossible) to construct ' values explicitly. In that case, the  function gives us a polymorphic way to lift values into variants.throwF (pure "Hello") :: VariantF Maybe '[Bool, Int, Double, String]+There (There (There (Here (Just "Hello"))))throwF (pure True) :: VariantF Maybe '[Bool, Int, Double, String]Here (Just True)8throwF (pure True) :: VariantF IO '[Int, Double, String]...5... @ Uh oh! I couldn't find Bool inside the variant!... If you're pretty sure I'm wrong, perhaps the variant type is ambiguous;%... could you add some annotations?...oops2Often, you'll want to have a choice of types that aren't5 all wrapped in a functor. For this, we provide the  type synonym, as well as equivalents of all the functions below. These functions take care of wrapping and unwrapping the R3 wrapper, too, so it should be invisible to users.oops The type VariantF f '[x, y, z] is either f x, f y, or f z. The We construct these with Here,  There . Here, and There . There . Here2 respectively, and we can think o fthe number of -nestings as being the index of our chosen type in the type-level list of options.Often, however, we'll want to avoid being too explicit about our list of types, preferring instead to describe it with constraints. See the methods below for more information! 8> > :t [ Here (pure "Hello"), There (Here (pure True)) ]  Here (pure Hello), There (Here (pure True)) 5:: Applicative f => [VariantF f ([Char] : Bool : xs)]oopsRemove the first possibility from a variant. One nice possibility here is a function that tells us whether the first type was the one in our variant: variantF Left Right. For example::set -XDataKinds=variantF Left Right (Here (Identity True) :: Variant '[Bool])Left (Identity True)variantF Left Right (There (Here (Identity 3)) :: Variant '[Bool, Int])Right (Here (Identity 3))oopsSame as *, but the value will be unwrapped (not in R ) if found. > :t caseF (throw True :: Variant '[Bool])caseF (throw True :: Variant '[Bool]) :: (Identity Bool -> r) -> r => > :t caseF (throwF (pure True) :: VariantF IO '[Int, Bool])caseF (throwF (pure True) :: VariantF IO '[Int, Bool]) :: (IO Int -> o) -> (IO Bool -> o) -> ooopsSame as , but without the functor wrappers. Again, this function will specialise according to the provided variant: 1> > :t case_ (throw True :: Variant '[Bool, Int])case_ (throw True :: Variant '[Bool, Int]) :: (Bool -> o) -> (Int -> o) -> oYou can also use TypeApplications7 to check the specialisation for a particular variant: "> > :t case_ @'[Int, Bool, String]case_ @'[Int, Bool, String] :: Variant '[Int, Bool, String] -> (Int -> o) -> (Bool -> o) -> ([Char] -> o) -> ooopsA choice of zero types is an uninhabited type! This means we can convert it to V...oops*... and it also means we can convert back!      Safe-Inferred )*-/0125.8oops,When working in some monadic context, using catch9 becomes trickier. The intuitive behaviour is that each catch- shrinks the variant in the left side of my W7, but this is therefore type-changing: as we can only X and Y with a W type, this is impossible!4To get round this problem, we have to specialise to Z, which allows us to map over the error type and change it as we go. If the error we catch is the one in the variant that we want to handle, we pluck it out and deal with it. Otherwise, we "re-throw" the variant minus the one we've handled.9oopsJust the same as 8 , but specialised for our plain . and sounding much less like a radio station.:oopsSame as 8 except the error is not removed from the type. This is useful for writing recursive computations or computations that rethrow the same error type.;oopsSame as 9 except the error is not removed from the type. This is useful for writing recursive computations or computations that rethrow the same error type.<oopsThrow an error into a variant W context. Note that this isn't* type-changing, so this can work for any W, rather than just Z.=oopsSame as <, but without the f/ context. Given a value of some type within a  within a W context, "throw" the error.>oops;Add 'ExceptT (Variant '[])' to the monad transformer stack.?oopsConvert an 'ExceptT (Variant '[])' expression to an 'ExceptT Void' expression@oopsConvert an ExceptT (Variant '[x]) expression to an 'ExceptT x' expressionAoops Suspend the Z monad transformer from the top of the stack so that the stack can be manipulated without the Z layer.Boops=Catch the specified exception and return the caught value as T>. If no value was caught, then return the returned value in U.CoopsCatch the specified exception. If that exception is caught, exit the program.DoopsWhen the expression of type 'Either x a' evaluates to 'Left x', throw the x, otherwise return a.Eoops3When the expression of type 'Maybe a' evaluates to [, throw (), otherwise return a.Foops3When the expression of type 'Maybe a' evaluates to [/, throw the specified value, otherwise return a.GoopsCatch the specified exception and return it instead. The evaluated computation must return the same type that is being caught.HoopsCatch the specified exception and return it instead. The evaluated computation must return V (ie. it never returns)89:;<=>?@ABCDEFGH89<=:;>?@ABCDEFGH Safe-Inferred )*-/01253Ioops,When working in some monadic context, using catch9 becomes trickier. The intuitive behaviour is that each catch- shrinks the variant in the left side of my W7, but this is therefore type-changing: as we can only X and Y with a W type, this is impossible!4To get round this problem, we have to specialise to Z, which allows us to map over the error type and change it as we go. If the error we catch is the one in the variant that we want to handle, we pluck it out and deal with it. Otherwise, we "re-throw" the variant minus the one we've handled.JoopsJust the same as I , but specialised for our plain . and sounding much less like a radio station.KoopsSame as I except the error is not removed from the type. This is useful for writing recursive computations or computations that rethrow the same error type.LoopsSame as J except the error is not removed from the type. This is useful for writing recursive computations or computations that rethrow the same error type.<=>ABCDEFGHIJKLIJKL<=>ABCDEFGH      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL<=>?MNOMNPMQRSTUSTVMWXMQYMQZMQ[M\]^_`^_a^_bcdeMfg#oops-0.1.3.0-7XEEnxc3aCJFJqZht0WvmC Data.VariantControl.Monad.OopsControl.Monad.Oops.ClassicFoldfoldFoldFfoldFEithers toEithers fromEithersEithersF toEithersF fromEithersFCatchcatchCatchFcatchF CouldBeAnyOf CouldBeAnyOfFCouldBethrowsnatchCouldBeFthrowFsnatchFVariantVariantFHereTherevariantFvariantcaseFcase_ preposterous postposterous$fMonoidVariantF$fSemigroupVariantF $fCaseF:frFUN$fCaseF:frFUN0 $fCase:rFUN $fCase:rFUN0$fCouldBeFk[]x $fCouldBeFk:x$fCouldBeFk:x0 $fCouldBexsx $fCatchFax:: $fCatchFax:xs $fCatchxxsys$fArbitraryVariantF$fEithersFf:Either $fEithersFf:f$fEithers:Either $fEithers:x $fFoldFc: $fFoldFc[] $fFoldcxs $fOrdVariantF$fShowVariantF $fEqVariantFcatchFMcatchMsnatchFMsnatchMthrowFMthrowMrunOopsrunOops0runOops1suspendM catchAsLeftMcatchAndExitFailureM throwLeftM throwNothingMthrowNothingAsMrecoverMrecoverOrVoidMbaseGHC.ShowShowshow Data.EitherEitherghc-prim GHC.TypesIntBoolData.Functor.IdentityIdentityeitherLeftRight Data.VoidVoid mtl-2.2.2Control.Monad.Error.Class MonadError throwError catchErrortransformers-0.5.6.2Control.Monad.Trans.ExceptExceptT GHC.MaybeNothing