diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+# effectful-core-2.2.0.0 (2022-10-24)
+* Change `PrimState` for `Eff` from `RealWorld` to `PrimStateEff` to prevent the
+  `Prim` effect from executing arbitrary `IO` actions via `ioToPrim`.
+* Deprecate `(:>>)` as [GHC can't efficiently deal with type
+  families](https://github.com/haskell-effectful/effectful/issues/52#issuecomment-1269155485).
+* Add support for the `Alternative` and `MonadPlus` instances for `Eff` via the
+  `NonDet` effect.
+
 # effectful-core-2.1.0.0 (2022-08-22)
 * Include the `e :> localEs` constraint in the `EffectHandler` to allow more
   flexibility in handling higher order effects.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -49,19 +49,11 @@
 things that matter instead of reinventing all kinds of wheels, hence being a
 necessary condition for broader adoption of the library.
 
-However, `eff` uses delimited continuations underneath, which:
-
-- Are not yet supported by GHC (though [the
-proposal](https://github.com/ghc-proposals/ghc-proposals/pull/313) for including
-support for them has been accepted).
-
-- Are quite hard to understand.
-
-- Make the library "too powerful" in a sense as it faces
-  [a](https://github.com/hasura/eff/issues/13)
-  [few](https://github.com/hasura/eff/issues/7)
-  [issues](https://github.com/hasura/eff/issues/12) with no clear path towards
-  their resolution.
+Unfortunately, the development of `eff` has stalled due to a
+[few](https://github.com/hasura/eff/issues/13)
+[subtle](https://github.com/hasura/eff/issues/7)
+[issues](https://github.com/hasura/eff/issues/12) related to its use of
+delimited continuations underneath.
 
 ### What about `mtl`?
 
@@ -101,8 +93,19 @@
 
 ### Any downsides?
 
-As always, there's no free lunch. The `Eff` monad doesn't support `NonDet` nor
-`Coroutine` effects. However, the `NonDet` effect in existing libraries is
+As always, there's no free lunch. The `Eff` monad doesn't support effect
+handlers that require the ability to suspend or capture the rest of the
+computation and resume it later (potentially multiple times). This prevents
+`effectful` from providing (in particular):
+
+- A `NonDet` effect handler that executes multiple
+[`Alternative`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:Alternative)
+branches and collects their results.
+
+- A `Coroutine` effect.
+
+It needs to be noted however that such `NonDet` effect handler in existing
+libraries is
 [broken](https://github.com/lexi-lambda/eff/blob/8c4df4bf54faf22456354be18095b14825be5e85/notes/semantics-zoo.md)
 and none of the ones with support for higher order effects provide the
 `Coroutine` effect, so arguably it's not a big loss.
@@ -110,7 +113,7 @@
 If you need such capability in your application, there are well established
 libraries such as [conduit](https://hackage.haskell.org/package/conduit) or
 [list-t](https://hackage.haskell.org/package/list-t) that can be used with
-`effectful` without any issues.
+`effectful` without any hassle.
 
 ### Summary
 
@@ -147,9 +150,10 @@
 
 ## Example
 
-A `Filesystem` effect with two handlers, one that runs in `IO` and another that
-uses an in-memory virtual file system can be found
-[here](https://github.com/haskell-effectful/effectful/blob/master/effectful/examples/FileSystem.hs).
+For the examples see the *Introduction* sections of
+[`Effectful.Dispatch.Dynamic`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Dynamic.html)
+and
+[`Effectful.Dispatch.Static`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Static.html).
 
 ## Resources
 
diff --git a/effectful-core.cabal b/effectful-core.cabal
--- a/effectful-core.cabal
+++ b/effectful-core.cabal
@@ -1,7 +1,7 @@
 cabal-version:      2.4
 build-type:         Simple
 name:               effectful-core
-version:            2.1.0.0
+version:            2.2.0.0
 license:            BSD-3-Clause
 license-file:       LICENSE
 category:           Control
@@ -21,7 +21,7 @@
   CHANGELOG.md
   README.md
 
-tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.1
+tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.2
 
 bug-reports:   https://github.com/haskell-effectful/effectful/issues
 source-repository head
@@ -93,6 +93,7 @@
                      Effectful.Internal.Env
                      Effectful.Internal.Monad
                      Effectful.Internal.Unlift
+                     Effectful.NonDet
                      Effectful.Prim
                      Effectful.Reader.Dynamic
                      Effectful.Reader.Static
diff --git a/src/Effectful.hs b/src/Effectful.hs
--- a/src/Effectful.hs
+++ b/src/Effectful.hs
@@ -116,7 +116,7 @@
 -- $integration
 --
 -- Integration with most of existing libraries and frameworks can be done quite
--- easily. The main difference on how that looks like depends on the way a
+-- easily. The main difference in how that looks like depends on the way a
 -- library operates in a monadic context.
 --
 -- There are three main groups a library might fall into. It either operates:
diff --git a/src/Effectful/Dispatch/Dynamic.hs b/src/Effectful/Dispatch/Dynamic.hs
--- a/src/Effectful/Dispatch/Dynamic.hs
+++ b/src/Effectful/Dispatch/Dynamic.hs
@@ -200,9 +200,9 @@
 --
 -- If an effect makes use of the @m@ parameter, it is a /higher order effect/.
 --
--- Interpretation of higher order effects is slightly more involved. To see why,
--- let's consider the @Profiling@ effect for logging how much time a specific
--- action took to run:
+-- Interpretation of higher order effects is slightly more involving. To see
+-- why, let's consider the @Profiling@ effect for logging how much time a
+-- specific action took to run:
 --
 -- >>> :{
 --   data Profiling :: Effect where
diff --git a/src/Effectful/Dispatch/Static/Primitive.hs b/src/Effectful/Dispatch/Static/Primitive.hs
--- a/src/Effectful/Dispatch/Static/Primitive.hs
+++ b/src/Effectful/Dispatch/Static/Primitive.hs
@@ -31,6 +31,7 @@
     -- ** Utils
   , emptyEnv
   , cloneEnv
+  , restoreEnv
   , sizeEnv
   , tailEnv
   ) where
diff --git a/src/Effectful/Internal/Effect.hs b/src/Effectful/Internal/Effect.hs
--- a/src/Effectful/Internal/Effect.hs
+++ b/src/Effectful/Internal/Effect.hs
@@ -61,6 +61,7 @@
 type family xs :>> es :: Constraint where
   '[]      :>> es = ()
   (x : xs) :>> es = (x :> es, xs :>> es)
+{-# DEPRECATED (:>>) "Usage of (:>>) slows down GHC too much, so it will be removed in 3.0.0.0. See https://github.com/haskell-effectful/effectful/issues/52#issuecomment-1269155485 for more information." #-}
 
 ----------------------------------------
 
diff --git a/src/Effectful/Internal/Env.hs b/src/Effectful/Internal/Env.hs
--- a/src/Effectful/Internal/Env.hs
+++ b/src/Effectful/Internal/Env.hs
@@ -18,6 +18,7 @@
     -- * Operations
   , emptyEnv
   , cloneEnv
+  , restoreEnv
   , sizeEnv
   , tailEnv
 
@@ -156,6 +157,28 @@
   relinkEffects storageSize
   pure $ Env offset refs storage
 {-# NOINLINE cloneEnv #-}
+
+-- | Restore the environment from its clone.
+--
+-- @since 2.2.0.0
+restoreEnv
+  :: Env es -- ^ Destination.
+  -> Env es -- ^ Source.
+  -> IO ()
+restoreEnv dest src = do
+  destStorage <- readIORef (envStorage dest)
+  srcStorage  <- readIORef (envStorage src)
+  let destStorageSize = stSize destStorage
+      srcStorageSize  = stSize srcStorage
+  when (destStorageSize /= srcStorageSize) $ do
+    error $ "destStorageSize (" ++ show destStorageSize
+         ++ ") /= srcStorageSize (" ++ show srcStorageSize ++ ")"
+  writeIORef (envStorage dest) $! srcStorage
+    -- Decreasing the counter allows leakage of unsafeCoerce (see unsafeCoerce2
+    -- in the EnvTests module).
+    { stVersion = max (stVersion destStorage) (stVersion srcStorage)
+    }
+{-# NOINLINE restoreEnv #-}
 
 -- | Get the current size of the environment.
 sizeEnv :: Env es -> IO Int
diff --git a/src/Effectful/Internal/Monad.hs b/src/Effectful/Internal/Monad.hs
--- a/src/Effectful/Internal/Monad.hs
+++ b/src/Effectful/Internal/Monad.hs
@@ -15,6 +15,9 @@
   , unsafeEff
   , unsafeEff_
 
+  -- * NonDet
+  , NonDet(..)
+
   -- * Fail
   , Fail(..)
 
@@ -24,6 +27,7 @@
 
   -- * Prim
   , Prim
+  , PrimStateEff
   , runPrim
 
   -- * Lifting
@@ -74,7 +78,8 @@
   , modifyEnv
   ) where
 
-import Control.Applicative (liftA2)
+import Control.Applicative
+import Control.Monad
 import Control.Monad.Base
 import Control.Monad.Fix
 import Control.Monad.IO.Class
@@ -84,8 +89,9 @@
 import Data.Kind (Constraint)
 import GHC.Exts (oneShot)
 import GHC.IO (IO(..))
-import GHC.Stack (HasCallStack)
+import GHC.Stack
 import System.IO.Unsafe (unsafeDupablePerformIO)
+import Unsafe.Coerce (unsafeCoerce)
 import qualified Control.Exception as E
 import qualified Control.Monad.Catch as C
 
@@ -222,6 +228,27 @@
   mfix f = unsafeEff $ \es -> mfix $ \a -> unEff (f a) es
 
 ----------------------------------------
+-- NonDet
+
+-- | Provide the ability to use the 'Alternative' and 'MonadPlus' instance of
+-- 'Eff'.
+--
+-- @since 2.2.0.0
+data NonDet :: Effect where
+  Empty   :: NonDet m a
+  (:<|>:) :: m a -> m a -> NonDet m a
+
+type instance DispatchOf NonDet = Dynamic
+
+-- | @since: 2.2.0.0
+instance NonDet :> es => Alternative (Eff es) where
+  empty   = withFrozenCallStack (send Empty)
+  a <|> b = send (a :<|>: b)
+
+-- | @since: 2.2.0.0
+instance NonDet :> es => MonadPlus (Eff es)
+
+----------------------------------------
 -- Exception
 
 instance C.MonadThrow (Eff es) where
@@ -257,7 +284,7 @@
 type instance DispatchOf Fail = Dynamic
 
 instance Fail :> es => MonadFail (Eff es) where
-  fail = send . Fail
+  fail msg = withFrozenCallStack $ send (Fail msg)
 
 ----------------------------------------
 -- IO
@@ -306,13 +333,17 @@
 type instance DispatchOf Prim = Static WithSideEffects
 data instance StaticRep Prim = Prim
 
+-- | 'PrimState' token for 'Eff'. Used instead of 'RealWorld' to prevent the
+-- 'Prim' effect from executing arbitrary 'IO' actions via 'ioToPrim'.
+data PrimStateEff
+
 -- | Run an 'Eff' computation with primitive state-transformer actions.
 runPrim :: IOE :> es => Eff (Prim : es) a -> Eff es a
 runPrim = evalStaticRep Prim
 
 instance Prim :> es => PrimMonad (Eff es) where
-  type PrimState (Eff es) = RealWorld
-  primitive = unsafeEff_ . IO
+  type PrimState (Eff es) = PrimStateEff
+  primitive = unsafeEff_ . IO . unsafeCoerce
 
 ----------------------------------------
 -- Lifting
diff --git a/src/Effectful/NonDet.hs b/src/Effectful/NonDet.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/NonDet.hs
@@ -0,0 +1,112 @@
+-- | Provider of the t'Control.Applicative.Alternative' and
+-- t'Control.Monad.MonadPlus' instance for 'Eff'.
+module Effectful.NonDet
+  ( -- * Effect
+    NonDet(..)
+  , OnEmptyPolicy(..)
+
+    -- ** Handlers
+  , runNonDet
+
+  -- * Utils
+  , emptyEff
+  , sumEff
+
+    -- * Re-exports
+  , Alternative(..)
+  , HasCallStack
+  , CallStack
+  , getCallStack
+  , prettyCallStack
+  ) where
+
+import Control.Applicative
+import Data.Coerce
+import GHC.Generics
+import GHC.Stack
+
+import Effectful
+import Effectful.Dispatch.Dynamic
+import Effectful.Dispatch.Static
+import Effectful.Dispatch.Static.Primitive
+import Effectful.Error.Static
+import Effectful.Internal.Monad (LocalEnv(..), NonDet(..))
+
+-- | Policy of dealing with modifications to __thread local__ state in the
+-- environment in branches that end up calling the 'Empty' operation.
+--
+-- /Note:/ 'OnEmptyKeep' is significantly faster as there is no need to back up
+-- the environment on each call to ':<|>:'.
+--
+-- @since 2.2.0.0
+data OnEmptyPolicy
+  = OnEmptyKeep     -- ^ Keep modifications on 'Empty'.
+  | OnEmptyRollback -- ^ Rollback modifications on 'Empty'.
+  deriving (Eq, Generic, Ord, Show)
+
+-- | Run the 'NonDet' effect with a given 'OnEmptyPolicy'.
+--
+-- /Note:/ ':<|>:' executes the second computation if (and only if) the first
+-- computation calls 'Empty'.
+--
+-- @since 2.2.0.0
+runNonDet :: OnEmptyPolicy -> Eff (NonDet : es) a -> Eff es (Either CallStack a)
+runNonDet = \case
+  OnEmptyKeep     -> runNonDetKeep
+  OnEmptyRollback -> runNonDetRollback
+
+runNonDetKeep :: Eff (NonDet : es) a -> Eff es (Either CallStack a)
+runNonDetKeep = reinterpret (fmap noError . runError @()) $ \env -> \case
+  Empty       -> throwError ()
+  m1 :<|>: m2 -> localSeqUnlift env $ \unlift -> do
+    mr <- (Just <$> unlift m1) `catchError` \_ () -> pure Nothing
+    case mr of
+      Just r  -> pure r
+      Nothing -> unlift m2
+
+runNonDetRollback :: Eff (NonDet : es) a -> Eff es (Either CallStack a)
+runNonDetRollback = reinterpret (fmap noError . runError @()) $ \env -> \case
+  Empty       -> throwError ()
+  m1 :<|>: m2 -> do
+    backupEnv <- cloneLocalEnv env
+    localSeqUnlift env $ \unlift -> do
+      mr <- (Just <$> unlift m1) `catchError` \_ () -> do
+        -- If m1 failed, roll back the environment.
+        restoreLocalEnv env backupEnv
+        pure Nothing
+      case mr of
+        Just r  -> pure r
+        Nothing -> unlift m2
+
+----------------------------------------
+
+-- | Specialized version of 'empty' with the 'HasCallStack' constraint for
+-- tracking purposes.
+--
+-- @since 2.2.0.0
+emptyEff :: (HasCallStack, NonDet :> es) => Eff es a
+emptyEff = withFrozenCallStack $ send Empty
+
+-- | Specialized version of 'asum' with the 'HasCallStack' constraint for
+-- tracking purposes.
+--
+-- @since 2.2.0.0
+sumEff :: (HasCallStack, Foldable t, NonDet :> es) => t (Eff es a) -> Eff es a
+sumEff = foldr (<|>) emptyEff
+
+----------------------------------------
+-- Internal helpers
+
+noError :: Either (cs, e) a -> Either cs a
+noError = either (Left . fst) Right
+
+cloneLocalEnv
+  :: LocalEnv localEs handlerEs
+  -> Eff es (LocalEnv localEs handlerEs)
+cloneLocalEnv = coerce . unsafeEff_ . cloneEnv . coerce
+
+restoreLocalEnv
+  :: LocalEnv localEs handlerEs
+  -> LocalEnv localEs handlerEs
+  -> Eff es ()
+restoreLocalEnv dest src = unsafeEff_ $ restoreEnv (coerce dest) (coerce src)
diff --git a/src/Effectful/Prim.hs b/src/Effectful/Prim.hs
--- a/src/Effectful/Prim.hs
+++ b/src/Effectful/Prim.hs
@@ -2,6 +2,7 @@
 module Effectful.Prim
   ( -- * Effect
     Prim
+  , PrimStateEff
 
     -- ** Handlers
   , runPrim
