1 {-# LANGUAGE DefaultSignatures #-}
2 {-# LANGUAGE ExistentialQuantification #-} -- For SharingName
3 {-# LANGUAGE MagicHash #-} -- For unsafeCoerce#
4 module Symantic.Univariant.Letable where
6 import Control.Applicative (Applicative(..))
7 import Control.Monad (Monad(..))
8 import Data.Bool (Bool(..))
9 import Data.Eq (Eq(..))
10 import Data.Function (($), (.))
11 import Data.Functor ((<$>))
12 import Data.Functor.Compose (Compose(..))
13 import Data.HashMap.Strict (HashMap)
14 import Data.HashSet (HashSet)
15 import Data.Hashable (Hashable, hashWithSalt, hash)
16 import Data.Maybe (Maybe(..), isNothing)
17 import Data.Monoid (Monoid(..))
18 import Data.Ord (Ord(..))
19 import Data.Tuple (fst)
20 import GHC.Exts (Int(..))
21 import GHC.Prim (unsafeCoerce#)
22 import GHC.StableName (StableName(..), makeStableName, hashStableName, eqStableName)
23 import Numeric (showHex)
26 import Text.Show (Show(..))
27 import qualified Control.Monad.Trans.Class as MT
28 import qualified Control.Monad.Trans.Reader as MT
29 import qualified Control.Monad.Trans.State as MT
30 import qualified Data.HashMap.Strict as HM
31 import qualified Data.HashSet as HS
32 import qualified Data.List as List
34 import Symantic.Univariant.Liftable
37 -- | This class is not for manual usage like usual symantic operators, here 'def' and 'ref' are introduced by 'observeSharing'.
38 class Letable letName repr where
39 -- | @('def' letName x)@ let-binds @(letName)@ to be equal to @(x)@.
40 def :: letName -> repr a -> repr a
41 -- | @('ref' isRec letName)@ is a reference to @(letName)@. @(isRec)@ is 'True' iif. this 'ref'erence is recursive, ie. is reachable within its 'def'inition.
42 ref :: Bool -> letName -> repr a
44 Liftable repr => Letable letName (Unlift repr) =>
45 letName -> repr a -> repr a
47 Liftable repr => Letable letName (Unlift repr) =>
48 Bool -> letName -> repr a
50 ref r n = lift (ref r n)
52 -- * Class 'MakeLetName'
53 class MakeLetName letName where
54 makeLetName :: SharingName -> IO letName
56 -- * Type 'SharingName'
57 -- | Note that the observable sharing enabled by 'StableName'
58 -- is not perfect as it will not observe all the sharing explicitely done.
59 data SharingName = forall a. SharingName (StableName a)
60 -- | @('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.
61 makeSharingName :: a -> IO SharingName
62 makeSharingName !x = SharingName <$> makeStableName x
63 instance Eq SharingName where
64 SharingName n == SharingName m = eqStableName n m
65 instance Hashable SharingName where
66 hash (SharingName n) = hashStableName n
67 hashWithSalt salt (SharingName n) = hashWithSalt salt n
68 instance Show SharingName where
69 showsPrec _ (SharingName n) = showHex (I# (unsafeCoerce# n))
71 -- * Type 'ObserveSharing'
72 -- | Interpreter detecting some (Haskell embedded) @let@ definitions used at least once and/or recursively, in order to replace them with the 'def' and 'ref' combinators.
73 -- See [Type-safe observable sharing in Haskell](https://doi.org/10.1145/1596638.1596653)
74 newtype ObserveSharing letName repr a = ObserveSharing { unObserveSharing ::
75 MT.ReaderT (HashSet SharingName)
76 (MT.StateT (ObserveSharingState letName) IO)
77 (CleanDefs letName repr a) }
82 ObserveSharing letName repr a -> IO (repr a)
83 observeSharing (ObserveSharing m) = do
84 (a, st) <- MT.runReaderT m mempty `MT.runStateT`
89 let refs = HS.fromList $
91 List.filter (\(_letName, refCount) -> refCount > 0) $
92 HM.elems $ oss_refs st
94 -- trace (show refs) $
97 -- ** Type 'ObserveSharingState'
98 data ObserveSharingState letName = ObserveSharingState
99 { oss_refs :: HashMap SharingName (letName, Int)
100 , oss_recs :: HashSet SharingName
103 observeSharingNode ::
106 Letable letName repr =>
107 MakeLetName letName =>
108 ObserveSharing letName repr a -> ObserveSharing letName repr a
109 observeSharingNode node@(ObserveSharing m) = ObserveSharing $ do
110 nodeName <- MT.lift $ MT.lift $ makeSharingName node
112 ((letName, before), preds) <- getCompose $ HM.alterF (\before ->
113 Compose $ case before of
115 letName <- MT.lift $ MT.lift $ makeLetName nodeName
116 return ((letName, before), Just (letName, 0))
117 Just (letName, refCount) -> do
118 return ((letName, before), Just (letName, refCount + 1))
119 ) nodeName (oss_refs st)
120 parentNames <- MT.ask
121 if nodeName `HS.member` parentNames
125 , oss_recs = HS.insert nodeName (oss_recs st)
127 return $ ref True letName
129 MT.lift $ MT.put st{ oss_refs = preds }
131 then MT.local (HS.insert nodeName) (def letName <$> m)
132 else return $ ref False letName
134 type instance Unlift (ObserveSharing letName repr) = CleanDefs letName repr
136 ( Letable letName repr
137 , MakeLetName letName
140 ) => Liftable (ObserveSharing letName repr) where
141 lift x = observeSharingNode (ObserveSharing (return x))
142 lift1 f x = observeSharingNode (ObserveSharing (f <$> unObserveSharing x))
143 lift2 f x y = observeSharingNode $ ObserveSharing $
144 f <$> unObserveSharing x
145 <*> unObserveSharing y
146 lift3 f x y z = observeSharingNode $ ObserveSharing $
147 f <$> unObserveSharing x
148 <*> unObserveSharing y
149 <*> unObserveSharing z
151 -- * Type 'CleanDefs'
152 -- | Remove 'def' when non-recursive or unused.
153 newtype CleanDefs letName repr a = CleanDefs { unCleanDefs ::
154 HS.HashSet letName -> repr a }
156 type instance Unlift (CleanDefs letName repr) = repr
157 instance Liftable (CleanDefs letName repr) where
158 lift = CleanDefs . pure
159 lift1 f x = CleanDefs $
161 lift2 f x y = CleanDefs $
164 lift3 f x y z = CleanDefs $
169 ( Letable letName repr
172 ) => Letable letName (CleanDefs letName repr) where
173 def name x = CleanDefs $ \refs ->
174 if name `HS.member` refs
175 then -- Perserve 'def'
176 def name $ unCleanDefs x refs