rec-def-0.1: Data/Recursive/R/Internal.hs
{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
-- |
--
-- This module provides the 'R' data type, which wraps an imperative propagator (e.g. "Data.Recursive.Propagator.Naive") in a pure and (if done right) safe data structure.
--
-- The result of 'getR' is always a solution of the given equations, but for it
-- to be deterministic (and hence for this API to be safe), the following
-- should hold:
--
-- * The @a@ in @R a@ should be partially orderd ('Data.POrder.POrder')
-- * That partial order must respect equality on @a@
-- * It must have a bottom element 'Data.POrder.bottom' ('Data.POrder.Bottom').
-- * The function passed to 'defR1', 'defR2' etc. must be a monotonic function
-- between these partial orders.
--
-- If this does not hold, then the result of 'getR' may not be deterministic.
--
-- Termination depends on whether a soluiton can be found iteratively. This is
-- guaranteed if all partial orders involved satisfy the Ascending Chain Condition.
module Data.Recursive.R.Internal
( R
, getR, getRDual
, mkR, defR1, defR2, defRList
)
where
import System.IO.Unsafe
import Control.Monad.ST
import Data.Monoid
import Data.Coerce
import Data.Recursive.Propagator.Class
import System.IO.RecThunk
-- | A value of type @R a@ is a @a@, but defined using only specific operations
-- (which you will find in the corresponding module, e.g.
-- "Data.Recursive.Bool"), which allow recursive definitions.
--
-- You can use 'getR' to extract the value.
--
-- Do not use the extracted value in the definition of that value, this will
-- loop just like a recursive definition with plain values would.
data R a = R (Prop a) Thunk
-- | Any value of type @a@ is also a value of type @r a@.
mkR :: HasPropagator a => a -> R a
mkR x = unsafePerformIO $ do
p <- newConstProp x
t <- doneThunk
pure (R p t)
newR :: HasPropagator a => (Prop a -> IO [Thunk]) -> R a
newR act = unsafePerformIO $ do
p <- newProp
t <- thunk (act p)
pure (R p t)
-- | Defines a value of type @R b@ to be a function of the values of @R a@.
--
-- The action passed it should declare that relation to the underlying propagator.
--
-- The @Prop a@ propagator must only be used for reading values _from_.
defR1 :: (HasPropagator a, HasPropagator b) =>
(Prop a -> Prop b -> IO ()) ->
R a -> R b
defR1 def r1 = newR $ \p -> do
let R p1 t1 = r1
def p1 p
pure [t1]
-- | Defines a value of type @R c@ to be a function of the values of @R a@ and @R b@.
--
-- The action passed it should declare that relation to the underlying propagator.
--
-- The @Prop a@ and @Prop b@ propagators must only be used for reading values _from_.
defR2 :: (HasPropagator a, HasPropagator b, HasPropagator c) =>
(Prop a -> Prop b -> Prop c -> IO ()) ->
R a -> R b -> R c
defR2 def r1 r2 = newR $ \p -> do
let R p1 t1 = r1
let R p2 t2 = r2
def p1 p2 p
pure [t1, t2]
-- | Defines a value of type @R b@ to be a function of the values of a list of @R a@ values.
--
-- The action passed it should declare that relation to the underlying propagator.
--
-- The @Prop a@ propagators must only be used for reading values _from_.
defRList :: (HasPropagator a, HasPropagator b) =>
([Prop a] -> Prop b -> IO ()) ->
[R a] -> R b
defRList def rs = newR $ \p -> do
def [ p' | R p' _ <- rs] p
pure [ t | R _ t <- rs]
-- | Extract the value from a @R a@. This must not be used when _defining_ that value.
getR :: HasPropagator a => R a -> a
getR (R p t) = unsafePerformIO $ do
force t
readProp p
-- | Convenience variant of 'getR' to also remove the 'Dual' newtype wrapper, mostly for use with "Data.Recursive.DualBool".
getRDual :: HasPropagator (Dual a) => R (Dual a) -> a
getRDual = getDual . getR