{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DataKinds #-}

{-|

The `DeepFrz` module provides a way to return arbitrarily complex data
structures containing LVars from `Par` computations.

The important thing to know is that to use `runParThenFreeze` to run a
`Par` computation, you must make sure that all types you return from
the `Par` computation have `DeepFrz` instances.  This means that, if
you wish to return a user-defined type, you will need to include a bit
of boilerplate to give it a `DeepFrz` instance.  Here is a complete
example:

> {-# LANGUAGE TypeFamilies #-}
> import Control.LVish.DeepFrz
> 
> data MyData = MyData Int deriving Show
> 
> instance DeepFrz MyData where
>   type FrzType MyData = MyData
> 
> main = print (runParThenFreeze (return (MyData 3)))

-}

-- TODO: a more detailed (recursive?) DeepFrz instance example might
-- be really helpful here for people who want to implement their own
-- LVar types. -- LK

module Control.LVish.DeepFrz
       (
         -- * The functions you'll want to use
         runParThenFreeze,
         runParThenFreezeIO,

         -- * Some supporting types
         DeepFrz(), FrzType,
         NonFrzn, Frzn, Trvrsbl,
         
       ) where

import Data.Int
import Data.Word
import GHC.Prim (unsafeCoerce#)

-- import Control.LVish (LVarData1(..))
import Control.LVish.DeepFrz.Internal (DeepFrz(..), NonFrzn, Frzn, Trvrsbl)
import Control.LVish.Internal (Determinism(..), Par(WrapPar))
import Control.LVish.SchedIdempotent (runPar, runParIO)
--------------------------------------------------------------------------------

-- | Under normal conditions, calling a `freeze` operation inside a
-- `Par` computation makes the `Par` computation quasi-deterministic.
-- However, if we freeze only after all LVar operations are completed
-- (after the implicit global barrier of `runPar`), then we've avoided
-- all data races, and freezing is therefore safe.  Running a `Par`
-- computation with `runParThenFreeze` accomplishes this, without our
-- having to call `freeze` explicitly.
-- 
-- In order to use `runParThenFreeze`, the type returned from the
-- `Par` computation must be a member of the `DeepFrz` class.  All the
-- @Data.LVar.*@ libraries should provide instances of `DeepFrz`
-- already.  Further, you can create additional instances for custom,
-- pure datatypes.  The result of a `runParThenFreeze` depends on the
-- type-level function `FrzType`, whose only purpose is to toggle the
-- `s` parameters of all IVars to the `Frzn` state.
-- 
-- Significantly, the freeze at the end of `runParThenFreeze` has /no/ runtime cost, in
-- spite of the fact that it enables a /deep/ (recursive) freeze of the value returned
-- by the `Par` computation.
runParThenFreeze :: DeepFrz a => Par Det NonFrzn a -> FrzType a
runParThenFreeze (WrapPar p) = frz $ runPar p

-- | This version works for nondeterministic computations as well.
-- 
-- Of course, nondeterministic computations may also call `freeze`
-- internally, but this function has an advantage to doing your own
-- `freeze` at the end of a `runParIO`: there is an implicit barrier
-- before the final freeze.  Further, `DeepFrz` has no runtime
-- overhead, whereas regular freezing has a cost.
runParThenFreezeIO :: DeepFrz a => Par d NonFrzn a -> IO (FrzType a)
runParThenFreezeIO (WrapPar p) = do
  x <- runParIO p
  return $ frz x

{-
-- This won't work because it conflicts with other instances such as "Either":
instance (LVarData1 f, DeepFrz a) => DeepFrz (f s a) where
  type FrzType (f s a) = f Frzn (FrzType a)
  frz = unsafeCoerce#
-}

#define MKFRZINST(T) instance DeepFrz T where type FrzType T = T

MKFRZINST(Int)
MKFRZINST(Int8)
MKFRZINST(Int16)
MKFRZINST(Int32)
MKFRZINST(Int64)
MKFRZINST(Word)
MKFRZINST(Word8)
MKFRZINST(Word16)
MKFRZINST(Word32)
MKFRZINST(Word64)
MKFRZINST(Bool)
MKFRZINST(Char)
MKFRZINST(Integer)
MKFRZINST(Float)
MKFRZINST(Double)

MKFRZINST(())
MKFRZINST(Ordering)

instance DeepFrz a => DeepFrz [a] where
  type FrzType [a] = [FrzType a]
  frz = unsafeCoerce#

instance DeepFrz a => DeepFrz (Maybe a) where
  type FrzType (Maybe a) = Maybe (FrzType a)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b) => DeepFrz (Either a b) where
  type FrzType (Either a b) = Either (FrzType a) (FrzType b)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b) => DeepFrz (a,b) where
  type FrzType (a,b) = (FrzType a,FrzType b)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b, DeepFrz c) => DeepFrz (a,b,c) where
  type FrzType (a,b,c) = (FrzType a,FrzType b,FrzType c)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d) => DeepFrz (a,b,c,d) where
  type FrzType (a,b,c,d) = (FrzType a, FrzType b, FrzType c, FrzType d)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d, DeepFrz e) => DeepFrz (a,b,c,d,e) where
  type FrzType (a,b,c,d,e) = (FrzType a, FrzType b, FrzType c, FrzType d, FrzType e)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d, DeepFrz e,
          DeepFrz f) => DeepFrz (a,b,c,d,e,f) where
  type FrzType (a,b,c,d,e,f) = (FrzType a, FrzType b, FrzType c, FrzType d, FrzType e,
                                FrzType f)
  frz = unsafeCoerce#

instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d, DeepFrz e,
          DeepFrz f, DeepFrz g) => DeepFrz (a,b,c,d,e,f,g) where
  type FrzType (a,b,c,d,e,f,g) = (FrzType a, FrzType b, FrzType c, FrzType d, FrzType e,
                                  FrzType f, FrzType g)
  frz = unsafeCoerce#


instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d, DeepFrz e,
          DeepFrz f, DeepFrz g, DeepFrz h) => DeepFrz (a,b,c,d,e,f,g,h) where
  type FrzType (a,b,c,d,e,f,g,h) = (FrzType a, FrzType b, FrzType c, FrzType d, FrzType e,
                                    FrzType f, FrzType g, FrzType h)
  frz = unsafeCoerce#


instance (DeepFrz a, DeepFrz b, DeepFrz c, DeepFrz d, DeepFrz e,
          DeepFrz f, DeepFrz g, DeepFrz h, DeepFrz i) => DeepFrz (a,b,c,d,e,f,g,h,i) where
  type FrzType (a,b,c,d,e,f,g,h,i) = (FrzType a, FrzType b, FrzType c, FrzType d, FrzType e,
                                      FrzType f, FrzType g, FrzType h, FrzType i)
  frz = unsafeCoerce#