doc: fix comment about `CurryN`
[haskell/symantic-base.git] / src / Symantic / Semantics / SharingObserver.hs
index b68b25b20633c888fa029ca2c0bd0c697e149146..33a886039feff29b5d71a941619cca071210e650 100644 (file)
@@ -1,82 +1,98 @@
-{-# LANGUAGE AllowAmbiguousTypes #-} -- For ShowLetName
-{-# LANGUAGE BangPatterns #-} -- For makeSharingName
-{-# LANGUAGE DataKinds #-} -- For ShowLetName
-{-# LANGUAGE ExistentialQuantification #-} -- For SharingName
+-- For ShowLetName
+{-# LANGUAGE AllowAmbiguousTypes #-}
+-- For makeSharingName
+{-# LANGUAGE BangPatterns #-}
+-- For ShowLetName
+{-# LANGUAGE DataKinds #-}
+-- For SharingName
+{-# LANGUAGE ExistentialQuantification #-}
+
 -- {-# LANGUAGE MagicHash #-} -- For unsafeCoerce#
-module Symantic.SharingObserver where
+module Symantic.Semantics.SharingObserver where
 
-import Control.Applicative (Applicative(..))
-import Control.Monad (Monad(..))
+import Control.Applicative (Applicative (..))
+import Control.Monad (Monad (..))
 import Data.Bool
-import Data.Eq (Eq(..))
+import Data.Eq (Eq (..))
 import Data.Function (($), (.))
 import Data.Functor (Functor, (<$>))
-import Data.Functor.Compose (Compose(..))
+import Data.Functor.Compose (Compose (..))
 import Data.HashMap.Strict (HashMap)
 import Data.HashSet (HashSet)
-import Data.Hashable (Hashable, hashWithSalt, hash)
+import Data.Hashable (Hashable, hash, hashWithSalt)
 import Data.Int (Int)
-import Data.Maybe (Maybe(..), isNothing)
-import Data.Monoid (Monoid(..))
-import Data.Ord (Ord(..))
+import Data.Maybe (Maybe (..), isNothing)
+import Data.Monoid (Monoid (..))
+import Data.Ord (Ord (..))
+
 -- import GHC.Exts (Int(..))
 -- import GHC.Prim (unsafeCoerce#)
-import GHC.StableName (StableName(..), makeStableName, hashStableName, eqStableName)
+import GHC.StableName (StableName (..), eqStableName, hashStableName, makeStableName)
+
 -- import Numeric (showHex)
-import Prelude ((+), error)
+
+import Control.Monad.Trans.Class qualified as MT
+import Control.Monad.Trans.Reader qualified as MT
+import Control.Monad.Trans.State qualified as MT
+import Control.Monad.Trans.Writer qualified as MT
+import Data.HashMap.Strict qualified as HM
+import Data.HashSet qualified as HS
 import System.IO (IO)
 import System.IO.Unsafe (unsafePerformIO)
-import Text.Show (Show(..))
-import qualified Control.Monad.Trans.Class as MT
-import qualified Control.Monad.Trans.Reader as MT
-import qualified Control.Monad.Trans.State as MT
-import qualified Control.Monad.Trans.Writer as MT
-import qualified Data.HashMap.Strict as HM
-import qualified Data.HashSet as HS
+import Text.Show (Show (..))
+import Prelude (error, (+))
 
-import Symantic.Derive
+import Symantic.Syntaxes.Derive
 
 -- * Class 'Referenceable'
+
 -- | This class is not for end-users like usual symantic operators,
 -- though it will have to be defined on end-users' interpreters.
-class Referenceable letName repr where
+class Referenceable letName sem where
   -- | @('ref' isRec letName)@ is a reference to @(letName)@.
   -- It is introduced by 'observeSharing'.
   -- @(isRec)@ is 'True' iif. this 'ref'erence is recursive,
   -- ie. appears within its 'define'.
   --
   -- TODO: index 'letName' with 'a' to enable dependent-map
-  ref :: Bool -> letName -> repr a
+  ref :: Bool -> letName -> sem a
   ref isRec name = liftDerived (ref isRec name)
   default ref ::
-    FromDerived (Referenceable letName) repr =>
-    Bool -> letName -> repr a
+    FromDerived (Referenceable letName) sem =>
+    Bool ->
+    letName ->
+    sem a
 
 -- * Class 'Definable'
+
 -- | This class is not for end-users like usual symantic operators.
 -- There should be not need to use it outside this module,
 -- because used 'define's are gathered in 'Letsable'.
-class Definable letName repr where
+class Definable letName sem where
   -- | @('define' letName sub)@ let-binds @(letName)@ to be equal to @(sub)@.
   -- This is a temporary node either replaced
   -- by 'ref' and an entry in 'lets''s 'LetBindings',
   -- or removed when no 'ref'erence is made to it.
-  define :: letName -> repr a -> repr a
+  define :: letName -> sem a -> sem a
   define name = liftDerived1 (define name)
   default define ::
-    FromDerived1 (Definable letName) repr =>
-    letName -> repr a -> repr a
+    FromDerived1 (Definable letName) sem =>
+    letName ->
+    sem a ->
+    sem a
 
 -- * Class 'MakeLetName'
 class MakeLetName letName where
   makeLetName :: SharingName -> IO letName
 
 -- * Type 'SharingName'
+
 -- | Note that the observable sharing enabled by 'StableName'
 -- is not perfect as it will not observe all the sharing explicitely done.
 --
 -- Note also that the observed sharing could be different between ghc and ghci.
 data SharingName = forall a. SharingName (StableName a)
+
 -- | @('makeSharingName' x)@ is like @('makeStableName' x)@ but it also forces
 -- evaluation of @(x)@ to ensure that the 'StableName' is correct first time,
 -- which avoids to produce a tree bigger than needed.
@@ -94,16 +110,20 @@ instance Eq SharingName where
 instance Hashable SharingName where
   hash (SharingName n) = hashStableName n
   hashWithSalt salt (SharingName n) = hashWithSalt salt n
+
 {-
 instance Show SharingName where
   showsPrec _ (SharingName n) = showHex (I# (unsafeCoerce# n))
 -}
 
 -- * Type 'SharingObserver'
-newtype SharingObserver letName repr a = SharingObserver { unSharingObserver ::
-  MT.ReaderT (HashSet SharingName)
-             (MT.State (SharingObserverState letName))
-             (SharingFinalizer letName repr a) }
+newtype SharingObserver letName sem a = SharingObserver
+  { unSharingObserver ::
+      MT.ReaderT
+        (HashSet SharingName)
+        (MT.State (SharingObserverState letName))
+        (SharingFinalizer letName sem a)
+  }
 
 -- | Interpreter detecting some (Haskell embedded) @let@ definitions used at
 -- least once and/or recursively, in order to replace them
@@ -117,39 +137,42 @@ observeSharing ::
   Eq letName =>
   Hashable letName =>
   Show letName =>
-  SharingObserver letName repr a ->
-  WithSharing letName repr a
+  SharingObserver letName sem a ->
+  WithSharing letName sem a
 observeSharing (SharingObserver m) =
-  let (fs, st) = MT.runReaderT m mempty `MT.runState`
-        SharingObserverState
-          { oss_refs = HM.empty
-          , oss_recs = HS.empty
-          } in
-  let refs = HS.fromList
-        [ letName
-        | (letName, refCount) <- HM.elems (oss_refs st)
-        , refCount > 0
-        ] in
-  --trace (show refs) $
-  MT.runWriter $
-  (`MT.runReaderT` refs) $
-  unFinalizeSharing fs
+  let (fs, st) =
+        MT.runReaderT m mempty
+          `MT.runState` SharingObserverState
+            { oss_refs = HM.empty
+            , oss_recs = HS.empty
+            }
+   in let refs =
+            HS.fromList
+              [ letName
+              | (letName, refCount) <- HM.elems (oss_refs st)
+              , refCount > 0
+              ]
+       in --trace (show refs) $
+          MT.runWriter $
+            (`MT.runReaderT` refs) $
+              unFinalizeSharing fs
 
 -- ** Type 'WithSharing'
-type WithSharing letName repr a =
-  (repr a, HM.HashMap letName (SomeLet repr))
+type WithSharing letName sem a =
+  (sem a, HM.HashMap letName (SomeLet sem))
+
 {-
 -- * Type 'WithSharing'
-data WithSharing letName repr a = WithSharing
-  { lets :: HM.HashMap letName (SomeLet repr)
-  , body :: repr a
+data WithSharing letName sem a = WithSharing
+  { lets :: HM.HashMap letName (SomeLet sem)
+  , body :: sem a
   }
 mapWithSharing ::
-  (forall v. repr v -> repr v) ->
-  WithSharing letName repr a ->
-  WithSharing letName repr a
+  (forall v. sem v -> sem v) ->
+  WithSharing letName sem a ->
+  WithSharing letName sem a
 mapWithSharing f ws = WithSharing
-  { lets = (\(SomeLet repr) -> SomeLet (f repr)) <$> lets ws
+  { lets = (\(SomeLet sem) -> SomeLet (f sem)) <$> lets ws
   , body = f (body ws)
   }
 -}
@@ -164,149 +187,199 @@ observeSharingNode ::
   Eq letName =>
   Hashable letName =>
   Show letName =>
-  Referenceable letName repr =>
+  Referenceable letName sem =>
   MakeLetName letName =>
-  SharingObserver letName repr a ->
-  SharingObserver letName repr a
+  SharingObserver letName sem a ->
+  SharingObserver letName sem a
 observeSharingNode (SharingObserver m) = SharingObserver $ do
   let nodeName = makeSharingName m
   st <- MT.lift MT.get
-  ((letName, seenBefore), seen) <- getCompose $ HM.alterF (\seenBefore ->
-    -- Compose is used to return (letName, seenBefore) along seen
-    -- in the same HashMap lookup.
-    Compose $ return $ case seenBefore of
-      Nothing ->
-        ((letName, seenBefore), Just (letName, 0))
-        where letName = unsafePerformIO $ makeLetName nodeName
-      Just (letName, refCount) ->
-        ((letName, seenBefore), Just (letName, refCount + 1))
-    ) nodeName (oss_refs st)
+  ((letName, seenBefore), seen) <-
+    getCompose $
+      HM.alterF
+        ( \seenBefore ->
+            -- Compose is used to return (letName, seenBefore) along seen
+            -- in the same HashMap lookup.
+            Compose $
+              return $ case seenBefore of
+                Nothing ->
+                  ((letName, seenBefore), Just (letName, 0))
+                  where
+                    letName = unsafePerformIO $ makeLetName nodeName
+                Just (letName, refCount) ->
+                  ((letName, seenBefore), Just (letName, refCount + 1))
+        )
+        nodeName
+        (oss_refs st)
   parentNames <- MT.ask
   if nodeName `HS.member` parentNames
-  then do -- recursive reference to nodeName:
-          -- update seen references
-          -- and mark nodeName as recursive
-    MT.lift $ MT.put st
-      { oss_refs = seen
-      , oss_recs = HS.insert nodeName (oss_recs st)
-      }
-    return $ ref True letName
-  else do -- non-recursive reference to nodeName:
-          -- update seen references
-          -- and recurse if the nodeName hasn't been seen before
-          -- (would be in a preceding sibling branch, not in parentNames).
-    MT.lift $ MT.put st{ oss_refs = seen }
-    if isNothing seenBefore
-      then MT.local (HS.insert nodeName) (define letName <$> m)
-      else return $ ref False letName
-
-type instance Derived (SharingObserver letName repr) = SharingFinalizer letName repr
+    then do
+      -- recursive reference to nodeName:
+      -- update seen references
+      -- and mark nodeName as recursive
+      MT.lift $
+        MT.put
+          st
+            { oss_refs = seen
+            , oss_recs = HS.insert nodeName (oss_recs st)
+            }
+      return $ ref True letName
+    else do
+      -- non-recursive reference to nodeName:
+      -- update seen references
+      -- and recurse if the nodeName hasn't been seen before
+      -- (would be in a preceding sibling branch, not in parentNames).
+      MT.lift $ MT.put st{oss_refs = seen}
+      if isNothing seenBefore
+        then MT.local (HS.insert nodeName) (define letName <$> m)
+        else return $ ref False letName
+
+type instance Derived (SharingObserver letName sem) = SharingFinalizer letName sem
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , MakeLetName letName
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => LiftDerived (SharingObserver letName repr) where
+  ) =>
+  LiftDerived (SharingObserver letName sem)
+  where
   liftDerived = observeSharingNode . SharingObserver . return
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , MakeLetName letName
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => LiftDerived1 (SharingObserver letName repr) where
-  liftDerived1 f a = observeSharingNode $ SharingObserver $
-    f <$> unSharingObserver a
+  ) =>
+  LiftDerived1 (SharingObserver letName sem)
+  where
+  liftDerived1 f a =
+    observeSharingNode $
+      SharingObserver $
+        f <$> unSharingObserver a
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , MakeLetName letName
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => LiftDerived2 (SharingObserver letName repr) where
-  liftDerived2 f a b = observeSharingNode $ SharingObserver $
-    f <$> unSharingObserver a
-      <*> unSharingObserver b
+  ) =>
+  LiftDerived2 (SharingObserver letName sem)
+  where
+  liftDerived2 f a b =
+    observeSharingNode $
+      SharingObserver $
+        f <$> unSharingObserver a
+          <*> unSharingObserver b
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , MakeLetName letName
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => LiftDerived3 (SharingObserver letName repr) where
-  liftDerived3 f a b c = observeSharingNode $ SharingObserver $
-    f <$> unSharingObserver a
-      <*> unSharingObserver b
-      <*> unSharingObserver c
+  ) =>
+  LiftDerived3 (SharingObserver letName sem)
+  where
+  liftDerived3 f a b c =
+    observeSharingNode $
+      SharingObserver $
+        f <$> unSharingObserver a
+          <*> unSharingObserver b
+          <*> unSharingObserver c
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , MakeLetName letName
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => LiftDerived4 (SharingObserver letName repr) where
-  liftDerived4 f a b c d = observeSharingNode $ SharingObserver $
-    f <$> unSharingObserver a
-      <*> unSharingObserver b
-      <*> unSharingObserver c
-      <*> unSharingObserver d
-instance Referenceable letName (SharingObserver letName repr) where
+  ) =>
+  LiftDerived4 (SharingObserver letName sem)
+  where
+  liftDerived4 f a b c d =
+    observeSharingNode $
+      SharingObserver $
+        f <$> unSharingObserver a
+          <*> unSharingObserver b
+          <*> unSharingObserver c
+          <*> unSharingObserver d
+instance Referenceable letName (SharingObserver letName sem) where
   ref = error "[BUG]: observeSharing MUST NOT be applied twice"
-instance Definable letName (SharingObserver letName repr) where
+instance Definable letName (SharingObserver letName sem) where
   define = error "[BUG]: observeSharing MUST NOT be applied twice"
-instance Letsable letName (SharingObserver letName repr) where
+instance Letsable letName (SharingObserver letName sem) where
   lets = error "[BUG]: observeSharing MUST NOT be applied twice"
 
 -- * Type 'SharingFinalizer'
+
 -- | Remove 'define' when non-recursive or unused
 -- or replace it by 'ref', moving 'define's to the top.
-newtype SharingFinalizer letName repr a = SharingFinalizer { unFinalizeSharing ::
-  MT.ReaderT (HS.HashSet letName)
-    (MT.Writer (LetBindings letName repr))
-      (repr a) }
-
-type instance Derived (SharingFinalizer _letName repr) = repr
-instance (Eq letName, Hashable letName) =>
-  LiftDerived (SharingFinalizer letName repr) where
+newtype SharingFinalizer letName sem a = SharingFinalizer
+  { unFinalizeSharing ::
+      MT.ReaderT
+        (HS.HashSet letName)
+        (MT.Writer (LetBindings letName sem))
+        (sem a)
+  }
+
+type instance Derived (SharingFinalizer _letName sem) = sem
+instance
+  (Eq letName, Hashable letName) =>
+  LiftDerived (SharingFinalizer letName sem)
+  where
   liftDerived = SharingFinalizer . pure
-instance (Eq letName, Hashable letName) =>
-  LiftDerived1 (SharingFinalizer letName repr) where
+instance
+  (Eq letName, Hashable letName) =>
+  LiftDerived1 (SharingFinalizer letName sem)
+  where
   liftDerived1 f a = SharingFinalizer $ f <$> unFinalizeSharing a
-instance (Eq letName, Hashable letName) =>
-  LiftDerived2 (SharingFinalizer letName repr) where
-  liftDerived2 f a b = SharingFinalizer $
-    f <$> unFinalizeSharing a
-      <*> unFinalizeSharing b
-instance (Eq letName, Hashable letName) =>
-  LiftDerived3 (SharingFinalizer letName repr) where
-  liftDerived3 f a b c = SharingFinalizer $
-    f <$> unFinalizeSharing a
-      <*> unFinalizeSharing b
-      <*> unFinalizeSharing c
-instance (Eq letName, Hashable letName) =>
-  LiftDerived4 (SharingFinalizer letName repr) where
-  liftDerived4 f a b c d = SharingFinalizer $
-    f <$> unFinalizeSharing a
-      <*> unFinalizeSharing b
-      <*> unFinalizeSharing c
-      <*> unFinalizeSharing d
 instance
-  ( Referenceable letName repr
+  (Eq letName, Hashable letName) =>
+  LiftDerived2 (SharingFinalizer letName sem)
+  where
+  liftDerived2 f a b =
+    SharingFinalizer $
+      f <$> unFinalizeSharing a
+        <*> unFinalizeSharing b
+instance
+  (Eq letName, Hashable letName) =>
+  LiftDerived3 (SharingFinalizer letName sem)
+  where
+  liftDerived3 f a b c =
+    SharingFinalizer $
+      f <$> unFinalizeSharing a
+        <*> unFinalizeSharing b
+        <*> unFinalizeSharing c
+instance
+  (Eq letName, Hashable letName) =>
+  LiftDerived4 (SharingFinalizer letName sem)
+  where
+  liftDerived4 f a b c d =
+    SharingFinalizer $
+      f <$> unFinalizeSharing a
+        <*> unFinalizeSharing b
+        <*> unFinalizeSharing c
+        <*> unFinalizeSharing d
+instance
+  ( Referenceable letName sem
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => Referenceable letName (SharingFinalizer letName repr) where
+  ) =>
+  Referenceable letName (SharingFinalizer letName sem)
+  where
   ref isRec = liftDerived . ref isRec
 instance
-  ( Referenceable letName repr
+  ( Referenceable letName sem
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => Definable letName (SharingFinalizer letName repr) where
+  ) =>
+  Definable letName (SharingFinalizer letName sem)
+  where
   define name body = SharingFinalizer $ do
     refs <- MT.ask
-    let (repr, defs) =
+    let (sem, defs) =
           MT.runWriter $ MT.runReaderT (unFinalizeSharing body) refs
     if name `HS.member` refs
       then do
@@ -314,36 +387,37 @@ instance
         -- to put it in scope even when some 'ref' to it exists outside of 'body'
         -- (which can happen when a body-expression is shared),
         -- and replace it by a 'ref'.
-        MT.lift $ MT.tell $ HM.insert name (SomeLet repr) defs
+        MT.lift $ MT.tell $ HM.insert name (SomeLet sem) defs
         return $ ref False name
-      else
-        -- Remove this unreferenced 'define' node.
+      else -- Remove this unreferenced 'define' node.
         unFinalizeSharing body
 
 -- * Class 'Letsable'
-class Letsable letName repr where
+class Letsable letName sem where
   -- | @('lets' defs x)@ let-binds @(defs)@ in @(x)@.
-  lets :: LetBindings letName repr -> repr a -> repr a
+  lets :: LetBindings letName sem -> sem a -> sem a
   lets defs = liftDerived1 (lets ((\(SomeLet val) -> SomeLet (derive val)) <$> defs))
   default lets ::
-    Derivable repr =>
-    FromDerived1 (Letsable letName) repr =>
-    LetBindings letName repr -> repr a -> repr a
+    Derivable sem =>
+    FromDerived1 (Letsable letName) sem =>
+    LetBindings letName sem ->
+    sem a ->
+    sem a
 
 -- ** Type 'SomeLet'
-data SomeLet repr = forall a. SomeLet (repr a)
+data SomeLet sem = forall a. SomeLet (sem a)
 
 -- ** Type 'LetBindings'
-type LetBindings letName repr = HM.HashMap letName (SomeLet repr)
+type LetBindings letName sem = HM.HashMap letName (SomeLet sem)
 
 {-
 -- | Not used but can be written nonetheless.
 instance
-  ( Letsable letName repr
+  ( Letsable letName sem
   , Eq letName
   , Hashable letName
   , Show letName
-  ) => Letsable letName (SharingFinalizer letName repr) where
+  ) => Letsable letName (SharingFinalizer letName sem) where
   lets defs x = SharingFinalizer $ do
     ds <- traverse (\(SomeLet v) -> do
       r <- unFinalizeSharing v
@@ -354,12 +428,15 @@ instance
 -}
 
 -- ** Type 'OpenRecs'
+
 -- | Mutually recursive terms, in open recursion style.
 type OpenRecs letName a = LetRecs letName (OpenRec letName a)
+
 -- | Mutually recursive term, in open recursion style.
 -- The term is given a @final@ (aka. @self@) map
 -- of other terms it can refer to (including itself).
 type OpenRec letName a = LetRecs letName a -> a
+
 -- | Recursive let bindings.
 type LetRecs letName = HM.HashMap letName
 
@@ -376,8 +453,8 @@ fix f = final where final = f final
 --
 -- Here all mutually dependent functions are restricted to the same polymorphic type @(a)@.
 -- See http://okmij.org/ftp/Computation/fixed-point-combinators.html#Poly-variadic
-mutualFix :: forall recs a. Functor recs => recs ({-finals-}recs a -> a) -> recs a
+mutualFix :: forall recs a. Functor recs => recs ({-finals-} recs a -> a) -> recs a
 mutualFix opens = fix f
   where
-  f :: recs a -> recs a
-  f recs = ($ recs) <$> opens
+    f :: recs a -> recs a
+    f recs = ($ recs) <$> opens