-- | Unsafe utilities for statically dispatched effects.
module Effectful.Dispatch.Static.Unsafe
  ( reallyUnsafeLiftMapIO
  , reallyUnsafeUnliftIO
  ) where

import Effectful.Internal.Monad

-- | Utility for lifting 'IO' computations of type
--
-- @'IO' a -> 'IO' b@
--
-- to
--
-- @'Eff' es a -> 'Eff' es b@
--
-- This function is __really unsafe__ because:
--
-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
--   computations.
--
-- - The 'IO' computation must run its argument in a way that's perceived as
--   sequential to the outside observer, e.g. in the same thread or in a worker
--   thread that finishes before the argument is run again.
--
-- __Warning:__ if you disregard the second point, you will experience weird
-- bugs, data races or internal consistency check failures.
--
-- When in doubt, use 'Effectful.Dispatch.Static.unsafeLiftMapIO', especially
-- since this version saves only a simple safety check per call of
-- @reallyUnsafeLiftMapIO f@.
reallyUnsafeLiftMapIO :: (IO a -> IO b) -> Eff es a -> Eff es b
reallyUnsafeLiftMapIO :: forall a b (es :: [Effect]). (IO a -> IO b) -> Eff es a -> Eff es b
reallyUnsafeLiftMapIO IO a -> IO b
f Eff es a
m = forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
unsafeEff forall a b. (a -> b) -> a -> b
$ \Env es
es -> IO a -> IO b
f (forall (es :: [Effect]) a. Eff es a -> Env es -> IO a
unEff Eff es a
m Env es
es)

-- | Create an unlifting function.
--
-- This function is __really unsafe__ because:
--
-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
--   computations.
--
-- - Unlifted 'Eff' computations must be run in a way that's perceived as
--   sequential to the outside observer, e.g. in the same thread as the caller
--   of 'reallyUnsafeUnliftIO' or in a worker thread that finishes before
--   another unlifted computation is run.
--
-- __Warning:__ if you disregard the second point, you will experience weird
-- bugs, data races or internal consistency check failures.
--
-- When in doubt, use 'Effectful.Dispatch.Static.unsafeSeqUnliftIO', especially
-- since this version saves only a simple safety check per call of the unlifting
-- function.
reallyUnsafeUnliftIO :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO :: forall (es :: [Effect]) a.
((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
reallyUnsafeUnliftIO (forall r. Eff es r -> IO r) -> IO a
k = forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
unsafeEff forall a b. (a -> b) -> a -> b
$ \Env es
es -> (forall r. Eff es r -> IO r) -> IO a
k (forall (es :: [Effect]) a. Eff es a -> Env es -> IO a
`unEff` Env es
es)